From dc44fc1760051d7d2983811b8fb0cce600bb9c32 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Fri, 31 May 2019 14:32:43 -0400 Subject: [PATCH 1/3] wireguard: add each peer in a separate service Before, changing any peers caused the entire WireGuard interface to be torn down and rebuilt. By configuring each peer in a separate service we're able to only restart the affected peers. Adding each peer individually also means individual peer configurations can fail, but the overall interface and all other peers will still be added. A WireGuard peer's internal identifier is its public key. This means it is the only reliable identifier to use for the systemd service. --- .../modules/services/networking/wireguard.nix | 95 ++++++++++++++----- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index 3a65f7ff32c7..d98446642de6 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -229,8 +229,60 @@ let ''; }; + generatePeerUnit = { interfaceName, interfaceCfg, peer }: + let + keyToUnitName = replaceChars + [ "/" "-" " " "+" "=" ] + [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ]; + unitName = keyToUnitName peer.publicKey; + psk = + if peer.presharedKey != null + then pkgs.writeText "wg-psk" peer.presharedKey + else peer.presharedKeyFile; + in nameValuePair "wireguard-${interfaceName}-peer-${unitName}" + { + description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}"; + requires = [ "wireguard-${interfaceName}.service" ]; + after = [ "wireguard-${interfaceName}.service" ]; + wantedBy = [ "multi-user.target" ]; + environment.DEVICE = interfaceName; + path = with pkgs; [ iproute wireguard-tools ]; - generateSetupServiceUnit = name: values: + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = let + wg_setup = "wg set ${interfaceName} peer ${peer.publicKey}" + + optionalString (psk != null) " preshared-key ${psk}" + + optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + + optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + + optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}"; + route_setup = + optionalString (interfaceCfg.allowedIPsAsRoutes != false) + (concatMapStringsSep "\n" + (allowedIP: + "ip route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ) peer.allowedIPs); + in '' + ${wg_setup} + ${route_setup} + ''; + + postStop = let + route_destroy = optionalString (interfaceCfg.allowedIPsAsRoutes != false) + (concatMapStringsSep "\n" + (allowedIP: + "ip route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ) peer.allowedIPs); + in '' + wg set ${interfaceName} peer ${peer.publicKey} remove + ${route_destroy} + ''; + }; + + generateInterfaceUnit = name: values: # exactly one way to specify the private key must be set #assert (values.privateKey != null) != (values.privateKeyFile != null); let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey; @@ -245,9 +297,7 @@ let path = with pkgs; [ kmod iproute wireguard-tools ]; serviceConfig = { - Type = "simple"; - Restart = "on-failure"; - RestartSec = "5s"; + Type = "oneshot"; RemainAfterExit = true; }; @@ -265,25 +315,8 @@ let wg set ${name} private-key ${privKey} ${ optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"} - ${concatMapStringsSep "\n" (peer: - assert (peer.presharedKeyFile == null) || (peer.presharedKey == null); # at most one of the two must be set - let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey else peer.presharedKeyFile; - in - "wg set ${name} peer ${peer.publicKey}" + - optionalString (psk != null) " preshared-key ${psk}" + - optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + - optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + - optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}" - ) values.peers} - ip link set up dev ${name} - ${optionalString (values.allowedIPsAsRoutes != false) (concatStringsSep "\n" (concatMap (peer: - (map (allowedIP: - "ip route replace ${allowedIP} dev ${name} table ${values.table}" - ) peer.allowedIPs) - ) values.peers))} - ${values.postSetup} ''; @@ -335,7 +368,12 @@ in ###### implementation - config = mkIf cfg.enable { + config = mkIf cfg.enable (let + all_peers = flatten + (mapAttrsToList (interfaceName: interfaceCfg: + map (peer: { inherit interfaceName interfaceCfg peer;}) interfaceCfg.peers + ) cfg.interfaces); + in { assertions = (attrValues ( mapAttrs (name: value: { @@ -346,19 +384,24 @@ in mapAttrs (name: value: { assertion = value.generatePrivateKeyFile -> (value.privateKey == null); message = "networking.wireguard.interfaces.${name}.generatePrivateKey must not be set if networking.wireguard.interfaces.${name}.privateKey is set."; - }) cfg.interfaces)); - + }) cfg.interfaces)) + ++ map ({ interfaceName, peer, ... }: { + assertion = (peer.presharedKey == null) || (peer.presharedKeyFile == null); + message = "networking.wireguard.interfaces.${interfaceName} peer «${peer.publicKey}» has both presharedKey and presharedKeyFile set, but only one can be used."; + }) all_peers; boot.extraModulePackages = [ kernel.wireguard ]; environment.systemPackages = [ pkgs.wireguard-tools ]; - systemd.services = (mapAttrs' generateSetupServiceUnit cfg.interfaces) + systemd.services = + (mapAttrs' generateInterfaceUnit cfg.interfaces) + // (listToAttrs (map generatePeerUnit all_peers)) // (mapAttrs' generateKeyServiceUnit (filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces)); systemd.paths = mapAttrs' generatePathUnit (filterAttrs (name: value: value.privateKeyFile != null) cfg.interfaces); - }; + }); } From 29eb4bcdb84967d8603cab14973f1fbebc39c570 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Fri, 31 May 2019 14:42:19 -0400 Subject: [PATCH 2/3] wireguard: 0.0.20190406 -> 0.0.20190531 --- pkgs/tools/networking/wireguard-tools/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/tools/networking/wireguard-tools/default.nix b/pkgs/tools/networking/wireguard-tools/default.nix index 2f71ea7a421e..8525427fe9a9 100644 --- a/pkgs/tools/networking/wireguard-tools/default.nix +++ b/pkgs/tools/networking/wireguard-tools/default.nix @@ -4,11 +4,11 @@ with stdenv.lib; stdenv.mkDerivation rec { name = "wireguard-tools-${version}"; - version = "0.0.20190406"; + version = "0.0.20190531"; src = fetchzip { url = "https://git.zx2c4.com/WireGuard/snapshot/WireGuard-${version}.tar.xz"; - sha256 = "1rqyyyx7j41vpp4jigagqs2qdyfngh15y48ghdqfrkv7v93vwdak"; + sha256 = "0caw6kc0lqsvzvdjpdbdn3r9ff93vdl46616pwlig0rsdjrqnklj"; }; sourceRoot = "source/src/tools"; From 1de35c7f5ecbfe3c5bae252f660068669eb62b7a Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Fri, 31 May 2019 14:40:02 -0400 Subject: [PATCH 3/3] wireguard: attempt infinity times to resolve a peer --- nixos/modules/services/networking/wireguard.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index d98446642de6..c07481582c2f 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -246,6 +246,7 @@ let after = [ "wireguard-${interfaceName}.service" ]; wantedBy = [ "multi-user.target" ]; environment.DEVICE = interfaceName; + environment.WG_ENDPOINT_RESOLUTION_RETRIES = "infinity"; path = with pkgs; [ iproute wireguard-tools ]; serviceConfig = {