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.
This commit is contained in:
parent
359facc3d3
commit
f57fc6c881
|
@ -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 {
|
privateKeyFile = mkOption {
|
||||||
example = "/private/wireguard_key";
|
example = "/private/wireguard_key";
|
||||||
type = with types; nullOr str;
|
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
|
# 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;
|
let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey;
|
||||||
in
|
in
|
||||||
nameValuePair "wireguard-${name}"
|
nameValuePair "wireguard-${name}"
|
||||||
|
@ -279,10 +327,27 @@ in
|
||||||
|
|
||||||
config = mkIf (cfg.interfaces != {}) {
|
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 ];
|
boot.extraModulePackages = [ kernel.wireguard ];
|
||||||
environment.systemPackages = [ pkgs.wireguard-tools ];
|
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,7 @@ in
|
||||||
vault = handleTest ./vault.nix {};
|
vault = handleTest ./vault.nix {};
|
||||||
virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
|
virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
|
||||||
wireguard = handleTest ./wireguard {};
|
wireguard = handleTest ./wireguard {};
|
||||||
|
wireguard-generated = handleTest ./wireguard/generated.nix {};
|
||||||
wordpress = handleTest ./wordpress.nix {};
|
wordpress = handleTest ./wordpress.nix {};
|
||||||
xautolock = handleTest ./xautolock.nix {};
|
xautolock = handleTest ./xautolock.nix {};
|
||||||
xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {};
|
xdg-desktop-portal = handleTest ./xdg-desktop-portal.nix {};
|
||||||
|
|
57
nixos/tests/wireguard/generated.nix
Normal file
57
nixos/tests/wireguard/generated.nix
Normal file
|
@ -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");
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Reference in a new issue