feat(fw,web-arm): replace cyberghost germany exit with a wireguard tunnel via web-arm #152

Merged
dominik.polakovics merged 1 commit from afk/151 into main 2026-06-12 23:57:17 +02:00

Closes #151

web-arm becomes a WireGuard exit node and fw's German-exit toggle routes the Apple TV through it instead of CyberGhost, which is removed entirely.

web-arm (hosts/web-arm/modules/de-exit.nix, new)

  • wg_de_exit interface, listen UDP 51820 (opened in the firewall), one peer (fw, no endpoint — fw initiates with keepalive since its WAN IP is dynamic).
  • Transfer net 10.44.0.0/30 (web-arm .2, fw .1) — deliberately outside wg_cloonar's 10.42.0.0/16, ghetto's 10.43.0.0/16 and epicenter's 10.14.x/10.50.x claims, so replies can't land in the wrong tunnel.
  • networking.nat masquerades the /30 out enp1s0 (IPv4 only). Docker sets the iptables FORWARD policy to DROP on this host, so the tunnel gets explicit forward accepts via an idempotent de-exit-fwd chain.
  • wg_cloonar, the ADR-0010 IPv6 setup, and public web services are untouched.

fw (hosts/fw/modules/de-exit.nix, replaces cyberghost-de.nix)

  • Permanently-up wg_de_exit interface to web-arm.cloonar.com:51820; allowedIPs 0.0.0.0/0 with allowedIPsAsRoutes = false so it never touches the main table.
  • cyberghost-de.targetde-exit.target, same semantics: start = Apple TV German, stop = Austrian, off after every boot. The target toggles only the policy-routing unit (rules at priorities 90/100, table vpn-de/101, client list unchanged).
  • The routing unit now installs the tunnel default route (metric 50) statically — WireGuard has no up/down events. postSetup reinstalls it after interface recreation (e.g. a rebuild restarting the wg unit while the target is on), since deleting the interface flushes the route.
  • Kill semantics preserved: web-arm unreachable → WireGuard blackholes inside the interface; interface absent → the metric-100 unreachable route catches it. The priority-90 local-/20 rule keeps Jellyfin (hairpin DNAT) and LAN/cross-VLAN traffic working in every state.
  • nftables: the three tun-cg-de references (rpfilter exemption, multimedia forward accept, masquerade) now name wg_de_exit.

Maintainer cleanup

The following fw secrets are no longer referenced by any module and can be removed from hosts/fw/secrets.yaml (not touched here, per policy):

  • cyberghost-auth
  • cyberghost-ca
  • cyberghost-cert
  • cyberghost-key

Verification

  • Pre-commit dry-builds pass for fw and web-arm.
  • Runtime acceptance (stream playback from the Hetzner range, toggle round-trip, reboot-off, tunnel-dead kill, Jellyfin in all three states) needs the maintainer's HITL pass after deploy.
Closes #151 web-arm becomes a WireGuard exit node and fw's German-exit toggle routes the Apple TV through it instead of CyberGhost, which is removed entirely. ## web-arm (`hosts/web-arm/modules/de-exit.nix`, new) - `wg_de_exit` interface, listen UDP 51820 (opened in the firewall), one peer (fw, no endpoint — fw initiates with keepalive since its WAN IP is dynamic). - Transfer net **10.44.0.0/30** (web-arm `.2`, fw `.1`) — deliberately outside wg_cloonar's 10.42.0.0/16, ghetto's 10.43.0.0/16 and epicenter's 10.14.x/10.50.x claims, so replies can't land in the wrong tunnel. - `networking.nat` masquerades the /30 out `enp1s0` (IPv4 only). Docker sets the iptables FORWARD policy to DROP on this host, so the tunnel gets explicit forward accepts via an idempotent `de-exit-fwd` chain. - `wg_cloonar`, the ADR-0010 IPv6 setup, and public web services are untouched. ## fw (`hosts/fw/modules/de-exit.nix`, replaces `cyberghost-de.nix`) - Permanently-up `wg_de_exit` interface to `web-arm.cloonar.com:51820`; `allowedIPs 0.0.0.0/0` with **`allowedIPsAsRoutes = false`** so it never touches the main table. - `cyberghost-de.target` → **`de-exit.target`**, same semantics: start = Apple TV German, stop = Austrian, off after every boot. The target toggles only the policy-routing unit (rules at priorities 90/100, table vpn-de/101, client list unchanged). - The routing unit now installs the tunnel default route (metric 50) statically — WireGuard has no up/down events. `postSetup` reinstalls it after interface recreation (e.g. a rebuild restarting the wg unit while the target is on), since deleting the interface flushes the route. - Kill semantics preserved: web-arm unreachable → WireGuard blackholes inside the interface; interface absent → the metric-100 unreachable route catches it. The priority-90 local-/20 rule keeps Jellyfin (hairpin DNAT) and LAN/cross-VLAN traffic working in every state. - nftables: the three `tun-cg-de` references (rpfilter exemption, multimedia forward accept, masquerade) now name `wg_de_exit`. ## Maintainer cleanup The following fw secrets are no longer referenced by any module and can be removed from `hosts/fw/secrets.yaml` (not touched here, per policy): - `cyberghost-auth` - `cyberghost-ca` - `cyberghost-cert` - `cyberghost-key` ## Verification - Pre-commit dry-builds pass for fw and web-arm. - Runtime acceptance (stream playback from the Hetzner range, toggle round-trip, reboot-off, tunnel-dead kill, Jellyfin in all three states) needs the maintainer's HITL pass after deploy.
web-arm becomes a WireGuard exit node (wg_de_exit, UDP 51820, transfer
net 10.44.0.0/30) that masquerades peer traffic out its German IPv4.
fw replaces the CyberGhost OpenVPN client with a permanently-up
WireGuard interface to it; allowedIPs 0.0.0.0/0 stays out of the main
table (allowedIPsAsRoutes = false) and the renamed de-exit.target
toggles only the policy-routing unit, which now installs the tunnel
route statically. Toggle UX, rule priorities (90/100), table vpn-de/101
and the kill-switch semantics are unchanged.

Closes #151
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/nixos!152
No description provided.