wireguard service: use scripts instead of ExecStarts/Stops

This is more in line with what other services do; also looks cleaner.
It changes configuration entries for pre-and post-hooks type to lines from
lists of strings which are more logical for them; coersion is provided for
backwards compatibility.

Finally, add several steps to improve robustness:

1. Load kernel module on start if not loaded;
2. Don't remove wireguard interface on start; it is removed on service stop. If
   it's not something is wrong.
This commit is contained in:
Nikolay Amiantov 2018-04-01 18:22:14 +03:00 committed by Austin Seipp
parent fb7cdbc15d
commit 7c90a86770

View file

@ -53,30 +53,30 @@ let
}; };
preSetup = mkOption { preSetup = mkOption {
example = literalExample ['' example = literalExample ''
${pkgs.iproute}/bin/ip netns add foo ${pkgs.iproute}/bin/ip netns add foo
'']; '';
default = []; default = "";
type = with types; listOf str; type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
description = '' description = ''
A list of commands called at the start of the interface setup. Commands called at the start of the interface setup.
''; '';
}; };
postSetup = mkOption { postSetup = mkOption {
example = literalExample ['' example = literalExample ''
${pkgs.bash} -c 'printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0' printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0
'']; '';
default = []; default = "";
type = with types; listOf str; type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
description = "A list of commands called at the end of the interface setup."; description = "Commands called at the end of the interface setup.";
}; };
postShutdown = mkOption { postShutdown = mkOption {
example = literalExample ["${pkgs.openresolv}/bin/resolvconf -d wg0"]; example = literalExample "${pkgs.openresolv}/bin/resolvconf -d wg0";
default = []; default = "";
type = with types; listOf str; type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
description = "A list of commands called after shutting down the interface."; description = "Commands called after shutting down the interface.";
}; };
table = mkOption { table = mkOption {
@ -182,9 +182,6 @@ let
}; };
ipCommand = "${pkgs.iproute}/bin/ip";
wgCommand = "${pkgs.wireguard}/bin/wg";
generateUnit = name: values: generateUnit = 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);
@ -196,49 +193,53 @@ let
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
environment.DEVICE = name; environment.DEVICE = name;
path = with pkgs; [ kmod iproute wireguard ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = flatten([ };
values.preSetup
"-${ipCommand} link del dev ${name}" script = ''
"${ipCommand} link add dev ${name} type wireguard" modprobe wireguard
(map (ip: ${values.preSetup}
"${ipCommand} address add ${ip} dev ${name}"
) values.ips)
("${wgCommand} set ${name} private-key ${privKey}" + ip link add dev ${name} type wireguard
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}")
(map (peer: ${concatMapStringsSep "\n" (ip:
"ip address add ${ip} dev ${name}"
) values.ips}
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 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; let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey else peer.presharedKeyFile;
in in
"${wgCommand} set ${name} peer ${peer.publicKey}" + "wg set ${name} peer ${peer.publicKey}" +
optionalString (psk != null) " preshared-key ${psk}" + optionalString (psk != null) " preshared-key ${psk}" +
optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" +
optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}" optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}"
) values.peers) ) values.peers}
"${ipCommand} link set up dev ${name}" ip link set up dev ${name}
(optionals (values.allowedIPsAsRoutes != false) (map (peer: ${optionalString (values.allowedIPsAsRoutes != false) (concatStringsSep "\n" (concatMap (peer:
(map (allowedIP: (map (allowedIP:
"${ipCommand} route replace ${allowedIP} dev ${name} table ${values.table}" "ip route replace ${allowedIP} dev ${name} table ${values.table}"
) peer.allowedIPs) ) peer.allowedIPs)
) values.peers)) ) values.peers))}
values.postSetup ${values.postSetup}
]); '';
ExecStop = flatten([
"${ipCommand} link del dev ${name}" preStop = ''
values.postShutdown ip link del dev ${name}
]); ${values.postShutdown}
}; '';
}; };
in in