fix(deploy): tejo had no WireGuard address, so it had no way back in

`networking.wireguard.interfaces.wg0.ips` was set in hardware/douro.nix
and hardware/batm3.nix, but hardware/upboard.nix is shared by tejo and
sintra — an address there would be claimed by both machines on the same
/24, so neither got one. tejo therefore evaluated to `wg0.ips = [ ]`:
the interface comes up with no IP and the tunnel is silently dead. On a
machine with no other route in, that is how you lose a box.

Replace the two per-hardware definitions with one `wireguardIpForModel`
table in flake.nix, keyed on model like fiatCodeForModel /
upgradeWindowForModel / nfcReaderForModel, and give tejo 10.0.0.3/24 —
the address it answers on today under its factory Debian.

douro (10.0.0.4/24) and batm3 (10.0.0.5/24) evaluate unchanged; sintra
stays deliberately unlisted, since it is reachable on the LAN and has
never had a tunnel address.

The address is only half of it: the VPS maps peer pubkey to tunnel IP,
so the machine still needs /var/lib/wireguard/wg0.key carried over from
its previous install (or a fresh key added to the VPS peer list). Both
wireguard units are ConditionPathExists-guarded on that key, so a
keyless first boot is clean and the tunnel starts once it is dropped in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-10-06 19:26:14 +02:00
commit 6042d69356
3 changed files with 37 additions and 4 deletions

View file

@ -223,6 +223,5 @@
}; };
}; };
# WireGuard VPN address # WireGuard VPN address → wireguardIpForModel in flake.nix.
networking.wireguard.interfaces.wg0.ips = [ "10.0.0.5/24" ];
} }

View file

@ -93,8 +93,7 @@
hybrid-sleep.enable = false; hybrid-sleep.enable = false;
}; };
# WireGuard VPN address # WireGuard VPN address → wireguardIpForModel in flake.nix.
networking.wireguard.interfaces.wg0.ips = [ "10.0.0.4/24" ];
# Serial port access for bill validator/dispenser # Serial port access for bill validator/dispenser
services.udev.extraRules = lib.mkAfter '' services.udev.extraRules = lib.mkAfter ''

View file

@ -159,6 +159,36 @@
sintra = true; # HID Global OMNIKEY 5022 sintra = true; # HID Global OMNIKEY 5022
}; };
# WireGuard address on the 10.0.0.0/24 management tunnel to the VPS
# (peer + listenPort live in configuration.nix; only the address is
# per-machine). Same keying caveat as the three tables above.
#
# This cannot live in a hardware file for the UP Board models, and the
# reason it now lives here for ALL of them is tejo: hardware/upboard.nix
# is shared by tejo and sintra, so an address set there would be claimed
# by both machines on the same /24. tejo had no address at all as a
# result — `wg0.ips = [ ]` brings the interface up with no IP and the
# tunnel is dead, which is a silent way to lose remote access to a
# machine that has no other route in. Keeping douro's and batm3's
# addresses here too means there is one list to read when allocating the
# next one, rather than three files plus the VPS peer config.
#
# An unlisted model gets no address and no tunnel. That is deliberate for
# sintra, which is reachable on the LAN (192.168.0.252) and has never had
# a tunnel address.
#
# NOTE: the address is only half of it. The VPS maps peer PUBLIC KEY to
# pragma: allowlist secret
# tunnel IP, so a machine also needs its private key at
# /var/lib/wireguard/wg0.key — carried over from the machine's previous
# install, or newly generated with its pubkey added to the VPS peer list.
# The key is operator-provisioned and deliberately not in the image.
wireguardIpForModel = {
tejo = "10.0.0.3/24";
douro = "10.0.0.4/24";
batm3 = "10.0.0.5/24";
};
lib = nixpkgs.lib; lib = nixpkgs.lib;
# Helper to create a live USB NixOS config for a specific machine model # Helper to create a live USB NixOS config for a specific machine model
@ -218,6 +248,11 @@
nfc.enable = nfcReaderForModel.${machineModel} or false; nfc.enable = nfcReaderForModel.${machineModel} or false;
}; };
# Management-tunnel address; see wireguardIpForModel.
networking.wireguard.interfaces.wg0.ips =
lib.optional (wireguardIpForModel ? ${machineModel})
wireguardIpForModel.${machineModel};
# Operator TUI and CLI tools # Operator TUI and CLI tools
environment.systemPackages = [ environment.systemPackages = [
atm-tui.packages.${system}.default atm-tui.packages.${system}.default