docs(arch): ADR-0035 and the runbook section for publishing the stack (refs #177)
CI / k8s (pull_request) Successful in 6s
CI / build (pull_request) Successful in 1m28s
CI / lint (pull_request) Successful in 1m51s
CI / unit (pull_request) Successful in 1m12s
CI / frontend (pull_request) Successful in 2m11s
CI / mutation (pull_request) Successful in 3m45s
CI / verify-stack (pull_request) Successful in 8m15s
CI / k8s (pull_request) Successful in 6s
CI / build (pull_request) Successful in 1m28s
CI / lint (pull_request) Successful in 1m51s
CI / unit (pull_request) Successful in 1m12s
CI / frontend (pull_request) Successful in 2m11s
CI / mutation (pull_request) Successful in 3m45s
CI / verify-stack (pull_request) Successful in 8m15s
The ADR records why the edge is in the cluster rather than on the Fedora host — routing and certificates should be state a `helm upgrade` can see — and the three costs that buys: the host forward nobody in the cluster can repair, the Let's Encrypt rate limit that makes `persistence.storageClass` non-optional, and publishing behandel and beheer to the internet behind synthetic accounts. Runbook §10 is the operational half: the five DNS records, the two firewalld rules (including the masquerade that makes the return path work), and the symptoms each missing piece produces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -222,6 +222,9 @@ string, so the port the browser uses has to match the one baked into `config.jso
|
||||
This is the same mechanism `infra/host-browser.yml` uses for the compose stack (which pins
|
||||
`localhost:8180`); only the addresses differ.
|
||||
|
||||
All of this is what §10 removes: with a public domain the portals have real certificates,
|
||||
so the browser gets its secure context and no forwarding is involved.
|
||||
|
||||
### The admin UIs work straight off the NodePorts
|
||||
|
||||
These are server-rendered and need no secure context, so they are reachable at the VM's
|
||||
@@ -360,6 +363,68 @@ immutable, so `helm upgrade` is rejected with `cannot patch "…" with kind Job`
|
||||
| Pods `Evicted` / `OOMKilled` | the VM is too small (§0) |
|
||||
| A Job shows `BackoffLimitExceeded` | read it: `kubectl -n big logs job/<name>` |
|
||||
|
||||
## 10. Publishing it on a public domain
|
||||
|
||||
By default the stack has no hostname: it is reached on NodePorts, and §5's secure-context
|
||||
problem forces `TALOS_HOST=localhost` plus five SSH forwards. Setting `public.domain` puts a
|
||||
Caddy deployment in front of it that terminates TLS for real hostnames (ADR-0035), and the
|
||||
forwards go away.
|
||||
|
||||
### Once, outside the cluster
|
||||
|
||||
**DNS** — five A records to the *host's* public address (the cluster is behind it):
|
||||
|
||||
```
|
||||
register.<domain> mijn.<domain> behandel.<domain> beheer.<domain> auth.<domain> → 46.224.220.37
|
||||
```
|
||||
|
||||
**The host's forward** — the public IP is on the Fedora host, so it has to hand 80/443 to
|
||||
the node. This is the only host-side configuration, and it is dumb layer 4:
|
||||
|
||||
```bash
|
||||
sudo firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toaddr=<TALOS_VM_IP>:toport=32080
|
||||
sudo firewall-cmd --permanent --zone=public --add-forward-port=port=443:proto=tcp:toaddr=<TALOS_VM_IP>:toport=32443
|
||||
sudo firewall-cmd --permanent --zone=public --add-masquerade
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
`--add-masquerade` is what makes the return path work: without it the node answers the
|
||||
client's address directly and the reply never goes back through the host.
|
||||
|
||||
**A StorageClass.** Caddy's certificates live in `/data`, which is an `emptyDir` unless
|
||||
`persistence.storageClass` is set (§6). Let's Encrypt allows five duplicate certificates per
|
||||
week, so on an `emptyDir` a handful of pod restarts leaves the edge serving an untrusted
|
||||
certificate until the limit resets. Install local-path first (§6).
|
||||
|
||||
### Deploy
|
||||
|
||||
```bash
|
||||
make k8s-up TALOS_HOST=<domain-facing name> K8S_REGISTRY=<TALOS_VM_IP>:30500 \
|
||||
K8S_SET='--set public.domain=<domain> --set public.email=<ops address> --set persistence.storageClass=local-path'
|
||||
```
|
||||
|
||||
`public.domain` is the only switch: with it empty nothing in `templates/edge.yaml` renders
|
||||
and the stack behaves exactly as §4 describes. With it set, `KC_HOSTNAME` and the portals'
|
||||
`config.json` both become `https://auth.<domain>` — one helper builds both, so the issuer
|
||||
and the authority cannot drift (ADR-0010).
|
||||
|
||||
Watch the first certificate being issued:
|
||||
|
||||
```bash
|
||||
kubectl -n big logs deploy/caddy-edge -f # "certificate obtained successfully"
|
||||
curl -sSI https://register.<domain>/openbaar/register | head -1
|
||||
```
|
||||
|
||||
### When it doesn't work
|
||||
|
||||
| Symptom | Cause |
|
||||
|---|---|
|
||||
| ACME fails with `connection refused` or a timeout on the HTTP-01 challenge | the host's 80 → 32080 forward is missing, or `--add-masquerade` is |
|
||||
| ACME fails with `NXDOMAIN` / `no such host` | the A record isn't there yet. Caddy retries with backoff; fix DNS and it recovers |
|
||||
| An untrusted certificate after several restarts | the Let's Encrypt duplicate limit, from certificates on an `emptyDir` — see above |
|
||||
| The portal loads but login bounces back logged out | `public.domain` changed without the portals rolling. The chart hashes the issuer into their pod template, so `helm upgrade` should do it — check `kubectl -n big describe deploy/self-service` |
|
||||
| `404` from the edge on a name that should work | the name isn't in `public.routes`; Caddy answers 404 for a Host it has no site block for |
|
||||
|
||||
## What is not ported
|
||||
|
||||
- **Observability** (Tempo, Prometheus, Grafana) is defined but disabled — those are built
|
||||
|
||||
Reference in New Issue
Block a user