railiance-infra/docs/adr/ADR-005-k3s-api-tunnel-only.md
codex 301f88575d
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Resolve repository owner requests
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
2026-08-22 15:14:01 +02:00

69 lines
2.3 KiB
Markdown

---
id: RINFRA-ADR-0005
type: adr
title: "k3s API is tunnel-only"
status: accepted
owner: railiance-infra
revision: "accepted-1"
last_reviewed: "2026-08-22"
review_interval: 6m
---
# 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.