From f57fc6c881ffe9acaaddfa8739b50f9bb7fa260c Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 23 Apr 2019 22:30:05 -0400 Subject: wireguard: add generatePrivateKeyFile option + test Ideally, private keys never leave the host they're generated on - like SSH. Setting generatePrivateKeyFile to true causes the PK to be generate automatically. --- nixos/modules/services/networking/wireguard.nix | 71 +++++++++++++++++++++++-- nixos/tests/all-tests.nix | 1 + nixos/tests/wireguard/generated.nix | 57 ++++++++++++++++++++ 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 nixos/tests/wireguard/generated.nix diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index 41aff1480a05..100e9a379c11 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -33,6 +33,15 @@ let ''; }; + generatePrivateKeyFile = mkOption { + default = false; + type = types.bool; + description = '' + Automatically generate a private key with `wg genkey`, + at the privateKeyFile location. + ''; + }; + privateKeyFile = mkOption { example = "/private/wireguard_key"; type = with types; nullOr str; @@ -182,9 +191,48 @@ let }; - generateUnit = name: values: + + generatePathUnit = name: values: + assert (values.privateKey == null); + assert (values.privateKeyFile != null); + nameValuePair "wireguard-${name}" + { + description = "WireGuard Tunnel - ${name} - Private Key"; + requiredBy = [ "wireguard-${name}.service" ]; + before = [ "wireguard-${name}.service" ]; + pathConfig.PathExists = values.privateKeyFile; + }; + + generateKeyServiceUnit = name: values: + assert values.generatePrivateKeyFile; + nameValuePair "wireguard-${name}-key" + { + description = "WireGuard Tunnel - ${name} - Key Generator"; + wantedBy = [ "wireguard-${name}.service" ]; + requiredBy = [ "wireguard-${name}.service" ]; + before = [ "wireguard-${name}.service" ]; + path = with pkgs; [ wireguard ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + mkdir --mode 0644 -p "${dirOf values.privateKeyFile}" + if [ ! -f "${values.privateKeyFile}" ]; then + touch "${values.privateKeyFile}" + chmod 0600 "${values.privateKeyFile}" + wg genkey > "${values.privateKeyFile}" + chmod 0400 "${values.privateKeyFile}" + fi + ''; + }; + + + generateSetupServiceUnit = name: values: # exactly one way to specify the private key must be set - assert (values.privateKey != null) != (values.privateKeyFile != null); + #assert (values.privateKey != null) != (values.privateKeyFile != null); let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey; in nameValuePair "wireguard-${name}" @@ -279,10 +327,27 @@ in config = mkIf (cfg.interfaces != {}) { + assertions = (attrValues ( + mapAttrs (name: value: { + assertion = (value.privateKey != null) != (value.privateKeyFile != null); + message = "Either networking.wireguard.interfaces.${name}.privateKey or networking.wireguard.interfaces.${name}.privateKeyFile must be set."; + }) cfg.interfaces)) + ++ (attrValues ( + 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)); + + boot.extraModulePackages = [ kernel.wireguard ]; environment.systemPackages = [ pkgs.wireguard-tools ]; - systemd.services = mapAttrs' generateUnit cfg.interfaces; + systemd.services = (mapAttrs' generateSetupServiceUnit cfg.interfaces) + // (mapAttrs' generateKeyServiceUnit + (filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces)); + + systemd.paths = mapAttrs' generatePathUnit + (filterAttrs (name: value: value.privateKeyFile != null) cfg.interfaces); }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 950eb01044f1..bf6fd60b144a 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -238,6 +238,7 @@ in vault = handleTest ./vault.nix {}; virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {}; wireguard = handleTest ./wireguard {}; + wireguard-generated = handleTest ./wireguard/generated.nix {}; wordpress = handleTest ./wordpress.nix {}; xautolock = handleTest ./xautolock.nix {}; xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {}; diff --git a/nixos/tests/wireguard/generated.nix b/nixos/tests/wireguard/generated.nix new file mode 100644 index 000000000000..897feafe3ff2 --- /dev/null +++ b/nixos/tests/wireguard/generated.nix @@ -0,0 +1,57 @@ +import ../make-test.nix ({ pkgs, ...} : { + name = "wireguard-generated"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ma27 grahamc ]; + }; + + nodes = { + peer1 = { + networking.firewall.allowedUDPPorts = [ 12345 ]; + networking.wireguard.interfaces.wg0 = { + ips = [ "10.10.10.1/24" ]; + listenPort = 12345; + privateKeyFile = "/etc/wireguard/private"; + generatePrivateKeyFile = true; + + }; + }; + + peer2 = { + networking.firewall.allowedUDPPorts = [ 12345 ]; + networking.wireguard.interfaces.wg0 = { + ips = [ "10.10.10.2/24" ]; + listenPort = 12345; + privateKeyFile = "/etc/wireguard/private"; + generatePrivateKeyFile = true; + }; + }; + }; + + testScript = '' + startAll; + + $peer1->waitForUnit("wireguard-wg0.service"); + $peer2->waitForUnit("wireguard-wg0.service"); + + my ($retcode, $peer1pubkey) = $peer1->execute("wg pubkey < /etc/wireguard/private"); + $peer1pubkey =~ s/\s+$//; + if ($retcode != 0) { + die "Could not read public key from peer1"; + } + + my ($retcode, $peer2pubkey) = $peer2->execute("wg pubkey < /etc/wireguard/private"); + $peer2pubkey =~ s/\s+$//; + if ($retcode != 0) { + die "Could not read public key from peer2"; + } + + $peer1->succeed("wg set wg0 peer $peer2pubkey allowed-ips 10.10.10.2/32 endpoint 192.168.1.2:12345 persistent-keepalive 1"); + $peer1->succeed("ip route replace 10.10.10.2/32 dev wg0 table main"); + + $peer2->succeed("wg set wg0 peer $peer1pubkey allowed-ips 10.10.10.1/32 endpoint 192.168.1.1:12345 persistent-keepalive 1"); + $peer2->succeed("ip route replace 10.10.10.1/32 dev wg0 table main"); + + $peer1->succeed("ping -c1 10.10.10.2"); + $peer2->succeed("ping -c1 10.10.10.1"); + ''; +}) -- cgit 1.4.1 From 06c83a14e1826694c0cda073e253a3ca785e1461 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 23 Apr 2019 22:42:37 -0400 Subject: Wrap 'wg' commands in --- nixos/modules/services/networking/wireguard.nix | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index 100e9a379c11..dd3cb1af2716 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -26,7 +26,7 @@ let type = with types; nullOr str; default = null; description = '' - Base64 private key generated by wg genkey. + Base64 private key generated by wg genkey. Warning: Consider using privateKeyFile instead if you do not want to store the key in the world-readable Nix store. @@ -37,8 +37,8 @@ let default = false; type = types.bool; description = '' - Automatically generate a private key with `wg genkey`, - at the privateKeyFile location. + Automatically generate a private key with + wg genkey, at the privateKeyFile location. ''; }; @@ -47,7 +47,7 @@ let type = with types; nullOr str; default = null; description = '' - Private key file as generated by wg genkey. + Private key file as generated by wg genkey. ''; }; @@ -133,8 +133,8 @@ let example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I="; type = with types; nullOr str; description = '' - Base64 preshared key generated by wg genpsk. Optional, - and may be omitted. This option adds an additional layer of + Base64 preshared key generated by wg genpsk. + Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. @@ -148,8 +148,8 @@ let example = "/private/wireguard_psk"; type = with types; nullOr str; description = '' - File pointing to preshared key as generated by wg pensk. Optional, - and may be omitted. This option adds an additional layer of + File pointing to preshared key as generated by wg pensk. + Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. ''; -- cgit 1.4.1