Fix node1 SSH lockout and pin k3s node-ip to K3s VLAN - #247
Conversation
Binding sshd to node1's Servers IP address locked out SSH entirely after a reboot (the address is DHCP-assigned, not guaranteed present at sshd start). Reverting to plain enable=true until this is done at the interface/firewall level instead of by IP. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR
Hosts here are multi-homed (Servers/K3s/Storage). k3s's own node-ip
auto-detection isn't stable across reboots, and a mismatch breaks
etcd — its persisted peer URL stops matching whatever address k3s
picks this time ("this server is not a member of the etcd cluster").
Pin --node-ip explicitly to the host's own <hostname>-k3s device
address so it's deterministic.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR
NixOS's own firewall is force-disabled on k3s hosts (Cilium owns packet filtering via eBPF), so binding sshd or the host firewall to node1's Servers IP doesn't work — nothing enforces it. Enable Cilium's host firewall (devices pinned explicitly, not auto-detected — it's already picked the wrong interface twice today on this multi-homed host) and add a CiliumClusterwideNetworkPolicy: cluster-internal traffic to the host is always allowed, SSH is only allowed from the Servers CIDR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR
Added: Cilium host-firewall SSH restriction (3rd commit)Replaces the reverted IP-based approach: NixOS's firewall is force-disabled on k3s hosts (Cilium owns filtering via eBPF), so binding to an IP/interface at the OS level was never going to work. Enables Cilium's Host Firewall + a This needs a careful rollout, not a plain merge-and-deploy — per Cilium's own docs, once a Recommended sequence:
Audit mode doesn't persist across 🤖 Generated with Claude Code |
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR
Cilium's devices/hostFirewall previously covered every interface on node1, forcing NixOS's own firewall off entirely. Scope Cilium to just k3s (the only interface BGP, native routing, and the egress-gateway policy actually touch) and let NixOS's real firewall govern everything else, trusting only the interfaces Cilium itself manages (k3s plus its own cilium_host/cilium_net/cilium_vxlan/lxc* interfaces) so the two don't double-filter the same traffic. This retires the Cilium host-firewall SSH policy from the previous commit — it can't do anything once Cilium stops attaching to the Servers interface — in favor of a plain NixOS firewall rule scoping SSH to that interface, which only failed the first time because the firewall was globally disabled. The egress-gateway policy is unrelated and unaffected: it forces pod egress routing/masquerade explicitly, independent of Cilium's devices list, which only controls where Cilium's own programs attach. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR
Superseded: SSH restriction moved off Cilium host-firewall (4th commit)The audit-mode rollout procedure in my previous comment no longer applies — that whole approach is gone. Turns out scoping Cilium's Changed:
The egress-gateway policy (pod egress routing) is untouched — confirmed against Cilium's own masquerade docs that 🤖 Generated with Claude Code |
Summary
Follow-up to #246: node1 got a fresh
nixos-anywherereinstall after two problems surfaced.sshdto node1's Servers IP (services.openssh.listenAddresses, from Route all pod egress via Cilium Egress Gateway; restrict node1 SSH to Servers #246) locked out SSH entirely after a reboot — the address is DHCP-assigned, not guaranteed present when sshd starts. Reverted to plainenable = true;for now; the interface/firewall-scoped version of this restriction is a separate follow-up.--node-ipauto-detection isn't stable across reboots. After the reboot, k3s picked a different address than etcd's on-disk membership record expected (this server is not a member of the etcd cluster), and the control plane got stuck retrying forever. Pinned--node-ipexplicitly to each host's own<hostname>-k3sdevice address (the K3s VLAN) so it's deterministic — same root-cause class as the pod-egress routing issue in Route all pod egress via Cilium Egress Gateway; restrict node1 SSH to Servers #246, just hitting k3s's own control-plane binding this time.Verified against node1's current live state (post-reinstall, currently healthy on
10.0.40.10— the K3s VLAN address) — the pin matches what's already running, so applying it is a same-address restart, not an IP change.Test plan
nix build .#nixosConfigurations.node1.config.system.build.toplevel— builds cleanly, confirmed--node-ip=10.0.40.10in the built k3s unitnix flake check --print-build-logs— all checks passdeploy node1and confirm k3s restarts cleanly with no etcd membership error🤖 Generated with Claude Code
https://claude.ai/code/session_01CezjyaVpC3FVPTUECp7cMR