railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md
codex 4d9e77c968
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Close RAIL-HO-WP-0009 declared-state gaps; leave live 6443 prune gated
Make the k3s API tunnel-only (ADR-005), stop declaring Flannel VXLAN
open to Anywhere, tag the base role so firewall can be scoped, and
schedule the Goss declared-vs-live check. CoulombCore sets ufw_manage
false so a converge cannot enable UFW there. T02 still needs operator
approval for make converge-firewall HOST=Railiance01.
2026-08-15 15:41:59 +02:00

58 lines
2.1 KiB
Markdown

# ADR-005 — k3s API is tunnel-only
**Status:** Accepted
**Date:** 2026-08-15
**Deciders:** implementation of RAIL-HO-WP-0009-T04
**Workplans:** `RAIL-HO-WP-0009`
---
## Context
Operator addresses on this network rotate with the ISP lease. A public UFW
allowlist for `6443/tcp` is therefore a treadmill:
- miss a rotation and `kubectl` breaks mid-session
- leave the old grant standing and it becomes a grant to a stranger
- hand-add the new address and the declaration drifts again
That cycle produced this workplan. The live allowlist drifted by hand *during
the session that was fixing allowlist drift*, and again before the next
session (`89.244.90.248` appeared undeclared). On 2026-08-15 the workstation
egress address was `85.132.220.102` — already on the revoked list as a
"historic" operator address.
`docs/deploy-stack.md` already documents API access over the ops-bridge SSH
tunnel for CoulombCore (`k3s-api-coulombcore`, local port 16443). The same
tunnel already exists for Railiance01 (`k3s-api-railiance01`, local port
16444). SSH itself stays public, so the host remains recoverable.
## Decision
The public k3s API allowlist is **empty**. Operator and agent `kubectl` access
uses the ops-bridge tunnels:
| Cluster | Tunnel | Local port | Remote |
|-------------|-------------------------|------------|--------|
| CoulombCore | `k3s-api-coulombcore` | 16443 | 6443 |
| Railiance01 | `k3s-api-railiance01` | 16444 | 6443 |
```bash
bridge up k3s-api-railiance01
# kubeconfig server: https://127.0.0.1:16444
```
Trade: every operator `kubectl` action depends on ops-bridge. That is accepted.
A rotating public allowlist is the worse dependency.
Emergency break-glass remains SSH: `ssh railiance01 -- sudo k3s kubectl …`.
Do not re-open `6443/tcp` to Anywhere.
## Consequences
- `k3s_api_allowed_sources` stays `[]`.
- Former public grants live in `k3s_api_revoked_sources` so a firewall-tagged
converge deletes them.
- Goss asserts the 6443 allowlist size is exactly the declared length (zero)
and that no revoked address remains.
- Amending this ADR is required before adding any new public 6443 source.