diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index 41aff1480a0..dd3cb1af271 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -26,19 +26,28 @@ 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.
'';
};
+ 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;
default = null;
description = ''
- Private key file as generated by wg genkey.
+ Private key file as generated by wg genkey.
'';
};
@@ -124,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.
@@ -139,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.
'';
@@ -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 950eb01044f..bf6fd60b144 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 00000000000..897feafe3ff
--- /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");
+ '';
+})