Group J — closes#127, #128, #129, #130, #131, #132.
Defaults
- control_plane_size default cx42 (16 GB) — cx32 (8 GB) is INSUFFICIENT
for a solo Sovereign per PLATFORM-TECH-STACK.md §7.1 (~11.3 GB Catalyst)
+ §7.4 (~8.8 GB per-host-cluster) = ~20 GB minimum. The previous cx32
default would OOM during the OpenBao + Keycloak step of bootstrap.
- New k3s_version variable (v1.31.4+k3s1) — pinned, validated against
the INSTALL_K3S_VERSION format. Previously hardcoded inside the
cloud-init templates, in violation of INVIOLABLE-PRINCIPLES.md §4.
Validation
- Region restricted to the 5 known Hetzner locations.
- control_plane_size + worker_size restricted to the cxNN | ccxNN | caxNN
namespace (blocks tiny dev sizes that would OOM at runtime).
- k3s_version regex matches the upstream installer's version format.
- ssh_allowed_cidrs validated as proper CIDRs.
Firewall
- Document each open port (80, 443, 6443, ICMP) and each blocked port
(22, 10250, 2379/2380, 8472) in README.md §"Firewall rules".
- SSH (22) is now a dynamic rule keyed off ssh_allowed_cidrs (default
empty = no SSH at the firewall, break-glass via Hetzner Console).
OS hardening (cloudinit-*.tftpl)
- sshd drop-in: PasswordAuthentication no, PermitRootLogin
prohibit-password, no forwarding, MaxAuthTries=3, LoginGraceTime=30.
- enable_unattended_upgrades (default true): security-only pocket,
auto-reboot at 02:30, removes unused kernels.
- enable_fail2ban (default true): sshd jail, systemd backend.
- Both control-plane and worker templates carry the same baseline.
Documentation
- New infra/hetzner/README.md (operator-facing) covers:
* What the module creates + Phase-0/Phase-1 boundary.
* Sizing rationale with the §7.1+§7.4 RAM math + upgrade path.
* Firewall rules: every open port, every blocked port, every
deliberate egress flow.
* k3s flag-by-flag rationale tied to PLATFORM-TECH-STACK.md §8.
* SSH key management: why no auto-generated keys (break-glass +
audit-trail + custody + compliance).
* OS hardening table.
* Standalone CLI invocation pattern (tofu apply -var-file=...).
* What the module does NOT do (Crossplane / Flux territory).
Closes#127#128#129#130#131#132