fix(deploy): guard the WireGuard peer units too, not just the interface

#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 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-09-22 20:38:35 +02:00
commit e3b51eaa37

View file

@ -191,9 +191,29 @@
# activation over an interface that was never set up; a provisioned machine # activation over an interface that was never set up; a provisioned machine
# is unaffected. Guarded on wg0 still being declared so the live image, # 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. # which mkForce's the interfaces away, doesn't get a unit with no ExecStart.
systemd.services = lib.mkIf (config.networking.wireguard.interfaces ? wg0) { systemd.services = lib.mkIf (config.networking.wireguard.interfaces ? wg0) (
wireguard-wg0.unitConfig.ConditionPathExists = "/var/lib/wireguard/wg0.key"; 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. # In-place rename migration: lamassu user → bitspire user.
# Runs after `users` activation so the bitspire user exists with its UID. # Runs after `users` activation so the bitspire user exists with its UID.