docs: update CLAUDE.md and README.md to include me.nik4nao.com deployment details
Some checks failed
validate / lint (push) Failing after 1s
Some checks failed
validate / lint (push) Failing after 1s
This commit is contained in:
parent
471e9ae8ca
commit
2c26c227ee
@ -140,3 +140,8 @@ otherwise it reports spurious `unknown-module` errors for `community.general`/
|
|||||||
`manifests/network/pihole-debian-patch.sh`.
|
`manifests/network/pihole-debian-patch.sh`.
|
||||||
- Dashy's manifest ships an empty ConfigMap shell; real config only lands
|
- Dashy's manifest ships an empty ConfigMap shell; real config only lands
|
||||||
after running `manifests/core/apply-dashy-config.sh`.
|
after running `manifests/core/apply-dashy-config.sh`.
|
||||||
|
- A newly pushed Argo CD `Application` doesn't create its namespace
|
||||||
|
instantly — `app-of-apps` only picks it up on its next Git poll. A
|
||||||
|
namespace-scoped secret script run immediately after pushing can fail
|
||||||
|
with `namespaces "<ns>" not found` until then (or until a forced
|
||||||
|
refresh).
|
||||||
|
|||||||
62
README.md
62
README.md
@ -40,7 +40,7 @@ Public services under `nik4nao.com` use Let's Encrypt.
|
|||||||
| Git and CI | Gitea, Gitea Actions runner, registry pull secrets, Gitea backup CronJob |
|
| Git and CI | Gitea, Gitea Actions runner, registry pull secrets, Gitea backup CronJob |
|
||||||
| Media | Jellyfin, qBittorrent, JDownloader |
|
| Media | Jellyfin, qBittorrent, JDownloader |
|
||||||
| Home services | Home Assistant ingress, HA gateway, AI gateway, Discord bot, Alexa bridge, Alert bridge |
|
| Home services | Home Assistant ingress, HA gateway, AI gateway, Discord bot, Alexa bridge, Alert bridge |
|
||||||
| Public apps | Portfolio, Watch Party ingress to the Mac Mini |
|
| Public apps | Portfolio, me.nik4nao.com, Watch Party ingress to the Mac Mini |
|
||||||
| Dashboard | Dashy |
|
| Dashboard | Dashy |
|
||||||
|
|
||||||
## Important URLs
|
## Important URLs
|
||||||
@ -64,6 +64,7 @@ Public services under `nik4nao.com` use Let's Encrypt.
|
|||||||
| `http://alert-bridge.home.arpa` | Alert bridge (Discord webhook relay) | Plain HTTP |
|
| `http://alert-bridge.home.arpa` | Alert bridge (Discord webhook relay) | Plain HTTP |
|
||||||
| `https://watch-party.nik4nao.com` | Watch Party on Mac Mini | Let's Encrypt |
|
| `https://watch-party.nik4nao.com` | Watch Party on Mac Mini | Let's Encrypt |
|
||||||
| `https://nik4nao.com` | Portfolio | Let's Encrypt |
|
| `https://nik4nao.com` | Portfolio | Let's Encrypt |
|
||||||
|
| `https://me.nik4nao.com` | me.nik4nao.com (terminal-style personal site) | Let's Encrypt |
|
||||||
| `http://ca.home.arpa` | Internal CA installer | Plain HTTP |
|
| `http://ca.home.arpa` | Internal CA installer | Plain HTTP |
|
||||||
|
|
||||||
`home.arpa` names are defined explicitly in `values/pihole.yaml` and
|
`home.arpa` names are defined explicitly in `values/pihole.yaml` and
|
||||||
@ -190,6 +191,7 @@ bash manifests/home-services/registry-secret.sh
|
|||||||
bash manifests/monitoring/grafana-secret.sh
|
bash manifests/monitoring/grafana-secret.sh
|
||||||
bash manifests/network/ddns-secret.sh
|
bash manifests/network/ddns-secret.sh
|
||||||
bash manifests/portfolio/registry-secret.sh
|
bash manifests/portfolio/registry-secret.sh
|
||||||
|
bash manifests/me-nik4nao-com/registry-secret.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Sealed Secret regeneration:
|
Sealed Secret regeneration:
|
||||||
@ -223,7 +225,8 @@ The Debian NFS server exports both `/mnt/storage` and `/home/nik/backups` to
|
|||||||
|
|
||||||
## TLS and Trust
|
## TLS and Trust
|
||||||
|
|
||||||
`*.home.arpa` certificates use `internal-ca-issuer` from `manifests/cert-manager`.
|
`*.home.arpa` certificates use `internal-ca-issuer` from `manifests/cert-manager`,
|
||||||
|
which signs off the self-signed root `internal-ca-cert` (`cert-manager` namespace).
|
||||||
The CA installer at `http://ca.home.arpa` serves `ca.crt` and an iOS/macOS
|
The CA installer at `http://ca.home.arpa` serves `ca.crt` and an iOS/macOS
|
||||||
mobileconfig profile. The `ca-sync` CronJob updates those files from the
|
mobileconfig profile. The `ca-sync` CronJob updates those files from the
|
||||||
`cert-manager/internal-ca-cert` secret when the CA changes.
|
`cert-manager/internal-ca-cert` secret when the CA changes.
|
||||||
@ -231,6 +234,49 @@ mobileconfig profile. The `ca-sync` CronJob updates those files from the
|
|||||||
`*.nik4nao.com` certificates use the Let's Encrypt issuers in
|
`*.nik4nao.com` certificates use the Let's Encrypt issuers in
|
||||||
`manifests/cert-manager/cluster-issuer-letsencrypt.yaml`.
|
`manifests/cert-manager/cluster-issuer-letsencrypt.yaml`.
|
||||||
|
|
||||||
|
### Renewing the internal CA
|
||||||
|
|
||||||
|
`internal-ca-cert`'s default `privateKey.rotationPolicy` is `Never`, so a
|
||||||
|
normal renewal reuses the existing private key — every already-issued leaf
|
||||||
|
cert and every device that already trusts the CA keep working unchanged.
|
||||||
|
**Deleting the `internal-ca-cert` secret instead of renewing it in place
|
||||||
|
generates a brand-new key pair**, which invalidates every leaf cert and every
|
||||||
|
device's trust simultaneously (this happened 2026-09-02: leaf certs issued
|
||||||
|
before the deletion failed `openssl verify` against the recreated CA
|
||||||
|
entirely, and Jellyfin/Kavita/etc. all showed as insecure on client devices).
|
||||||
|
|
||||||
|
To renew correctly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmctl renew certificate internal-ca-cert -n cert-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
This reissues the CA certificate (new serial/validity) while keeping the same
|
||||||
|
key, so it should not require any downstream action. If the key ever *does*
|
||||||
|
change (intentional rotation, or the secret was lost and had to be
|
||||||
|
recreated), do both of the following — the CA rotating does **not**
|
||||||
|
automatically cascade to either:
|
||||||
|
|
||||||
|
1. **Force-reissue every leaf cert**, since each `Certificate` renews on its
|
||||||
|
own independent schedule and won't notice the issuing CA changed:
|
||||||
|
```bash
|
||||||
|
for c in $(kubectl get certificate -A -o json | \
|
||||||
|
jq -r '.items[] | select(.spec.issuerRef.name=="internal-ca-issuer") | "\(.metadata.namespace)/\(.metadata.name)"'); do
|
||||||
|
ns="${c%%/*}"; name="${c##*/}"
|
||||||
|
secret=$(kubectl get certificate "$name" -n "$ns" -o jsonpath='{.spec.secretName}')
|
||||||
|
kubectl delete secret "$secret" -n "$ns"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
Verify afterward that every leaf chains to the current CA:
|
||||||
|
```bash
|
||||||
|
kubectl get secret internal-ca-cert -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/current-ca.crt
|
||||||
|
openssl verify -CAfile /tmp/current-ca.crt <(kubectl get secret <leaf-secret> -n <ns> -o jsonpath='{.data.tls\.crt}' | base64 -d)
|
||||||
|
```
|
||||||
|
2. **Re-trust the CA on every client device** — visit `http://ca.home.arpa`,
|
||||||
|
reinstall the mobileconfig, and re-enable full trust under Settings →
|
||||||
|
General → About → Certificate Trust Settings (iOS/macOS). The old trust
|
||||||
|
profile does not update itself; nothing pushes the new CA to devices.
|
||||||
|
|
||||||
## VPN VLAN 50 (PIA gateway) — status and runbook
|
## VPN VLAN 50 (PIA gateway) — status and runbook
|
||||||
|
|
||||||
`minisforum` is a PIA WireGuard egress gateway for a dedicated VPN VLAN
|
`minisforum` is a PIA WireGuard egress gateway for a dedicated VPN VLAN
|
||||||
@ -329,8 +375,20 @@ plane.
|
|||||||
- Authentik forward-auth depends on the `Cookie` header in
|
- Authentik forward-auth depends on the `Cookie` header in
|
||||||
`authRequestHeaders`; removing it causes redirect loops.
|
`authRequestHeaders`; removing it causes redirect loops.
|
||||||
- CoreDNS custom zone snippets must use the `.server` key suffix.
|
- CoreDNS custom zone snippets must use the `.server` key suffix.
|
||||||
|
- Renewing `internal-ca-cert` by deleting its secret (instead of `cmctl
|
||||||
|
renew`) generates a new key pair, silently breaking every leaf cert and
|
||||||
|
every device's trust at once — see "Renewing the internal CA" under [TLS
|
||||||
|
and Trust](#tls-and-trust) for the recovery/prevention procedure.
|
||||||
- The Dashy manifest contains an empty ConfigMap shell; use
|
- The Dashy manifest contains an empty ConfigMap shell; use
|
||||||
`manifests/core/apply-dashy-config.sh` to load the real config.
|
`manifests/core/apply-dashy-config.sh` to load the real config.
|
||||||
|
- A newly pushed Argo CD `Application` (a new file under `argocd/apps/`)
|
||||||
|
doesn't create its namespace/resources the instant you push — `app-of-apps`
|
||||||
|
only picks it up on its next Git poll. Running a namespace-scoped
|
||||||
|
`registry-secret.sh` (or anything else assuming the namespace already
|
||||||
|
exists) right after pushing can fail with `namespaces "<ns>" not found`;
|
||||||
|
either wait for the next poll or force one: `kubectl patch application
|
||||||
|
app-of-apps -n argocd --type merge -p
|
||||||
|
'{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"hard"}}}'`.
|
||||||
|
|
||||||
See the scoped READMEs in `ansible/`, `argocd/`, and `manifests/` for workflows
|
See the scoped READMEs in `ansible/`, `argocd/`, and `manifests/` for workflows
|
||||||
specific to those directories.
|
specific to those directories.
|
||||||
|
|||||||
@ -14,6 +14,7 @@ subdirectories are consumed by Argo CD Applications in `argocd/apps`.
|
|||||||
| `gitea/` | Gitea storage, backup, public ingress, runner and OIDC/admin secrets |
|
| `gitea/` | Gitea storage, backup, public ingress, runner and OIDC/admin secrets |
|
||||||
| `home-services/` | HA gateway, AI gateway, Discord bot, service TLS, registry secret |
|
| `home-services/` | HA gateway, AI gateway, Discord bot, service TLS, registry secret |
|
||||||
| `homeassistant/` | Home Assistant external service and ingress |
|
| `homeassistant/` | Home Assistant external service and ingress |
|
||||||
|
| `me-nik4nao-com/` | me.nik4nao.com deployment, ingress, registry pull secret |
|
||||||
| `media/` | Jellyfin, qBittorrent, JDownloader ([sending links](media/jdownloader.md)) |
|
| `media/` | Jellyfin, qBittorrent, JDownloader ([sending links](media/jdownloader.md)) |
|
||||||
| `monitoring/` | Monitoring PVs, Grafana datasource, Grafana/Auth OIDC secrets |
|
| `monitoring/` | Monitoring PVs, Grafana datasource, Grafana/Auth OIDC secrets |
|
||||||
| `multus/` | Multus CNI, VLAN 50 NetworkAttachmentDefinition, canary, browser-vpn-proxy SOCKS5 proxy ([details](multus/README.md)) — manual-sync Application, see `argocd/apps/multus.yaml` |
|
| `multus/` | Multus CNI, VLAN 50 NetworkAttachmentDefinition, canary, browser-vpn-proxy SOCKS5 proxy ([details](multus/README.md)) — manual-sync Application, see `argocd/apps/multus.yaml` |
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user