From e3b51eaa378ae0eff07584d3791e8c9fb9d53bbb Mon Sep 17 00:00:00 2001 From: Padreug Date: Tue, 22 Sep 2026 20:38:35 +0200 Subject: [PATCH] fix(deploy): guard the WireGuard peer units too, not just the interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #101 skipped wireguard-wg0 when no key is provisioned, but the module emits one unit per peer alongside it, and a condition-skipped unit is not a failed dependency — so the peer unit still ran and died on 'Unable to modify interface: No such device'. Same exit 4 from switch-to-configuration, different unit, so the nightly auto-upgrade is still marked failed on an unprovisioned machine (seen on sintra today). Guard the peers on the same key. Unit names come from the module's own peers.*.name option rather than re-deriving its escaping here, with the -refresh suffix following nixpkgs' peerUnitServiceName (a peer's null interval falls back to the interface's). Verified by evaluation that every wireguard-* unit in the installed config now carries the condition, that each is a real unit with an ExecStart, and that the live image — which mkForce's the interfaces away — still gets none. Refs #98 Co-Authored-By: Claude Fable 5.1 --- deploy/nixos/configuration.nix | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/deploy/nixos/configuration.nix b/deploy/nixos/configuration.nix index 652de98..6c11db7 100644 --- a/deploy/nixos/configuration.nix +++ b/deploy/nixos/configuration.nix @@ -191,9 +191,29 @@ # activation over an interface that was never set up; a provisioned machine # is unaffected. Guarded on wg0 still being declared so the live image, # which mkForce's the interfaces away, doesn't get a unit with no ExecStart. - systemd.services = lib.mkIf (config.networking.wireguard.interfaces ? wg0) { - wireguard-wg0.unitConfig.ConditionPathExists = "/var/lib/wireguard/wg0.key"; - }; + systemd.services = lib.mkIf (config.networking.wireguard.interfaces ? wg0) ( + let + iface = config.networking.wireguard.interfaces.wg0; + guard = { unitConfig.ConditionPathExists = "/var/lib/wireguard/wg0.key"; }; + # The module emits one unit per peer alongside the interface unit, and a + # skipped interface is NOT a failed dependency, so the peer units still + # run and die on "Unable to modify interface: No such device" — same + # exit 4, different unit. Guard them too. Names come from the module's + # own `peers.*.name` option (whose default is the escaped public key) + # rather than re-deriving the escaping here; the `-refresh` suffix + # follows nixpkgs' peerUnitServiceName, where a peer's null refresh + # interval falls back to the interface's. + refreshes = peer: + (if peer.dynamicEndpointRefreshSeconds != null then + peer.dynamicEndpointRefreshSeconds + else + iface.dynamicEndpointRefreshSeconds) != 0; + peerUnit = peer: + "wireguard-wg0-peer-${peer.name}" + lib.optionalString (refreshes peer) "-refresh"; + in + { wireguard-wg0 = guard; } + // lib.listToAttrs (map (peer: lib.nameValuePair (peerUnit peer) guard) iface.peers) + ); # In-place rename migration: lamassu user → bitspire user. # Runs after `users` activation so the bitspire user exists with its UID.