226 lines
6.7 KiB
Nix
226 lines
6.7 KiB
Nix
|
{ config, lib, pkgs, ... }:
|
||
|
|
||
|
with lib;
|
||
|
|
||
|
let
|
||
|
|
||
|
cfg = config.networking.wireguard;
|
||
|
|
||
|
kernel = config.boot.kernelPackages;
|
||
|
|
||
|
# interface options
|
||
|
|
||
|
interfaceOpts = { name, ... }: {
|
||
|
|
||
|
options = {
|
||
|
|
||
|
ips = mkOption {
|
||
|
example = [ "192.168.2.1/24" ];
|
||
|
default = [];
|
||
|
type = with types; listOf str;
|
||
|
description = "The IP addresses of the interface.";
|
||
|
};
|
||
|
|
||
|
privateKey = mkOption {
|
||
|
example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
|
||
|
type = types.str;
|
||
|
description = "Base64 private key generated by wg genkey.";
|
||
|
};
|
||
|
|
||
|
presharedKey = mkOption {
|
||
|
default = null;
|
||
|
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
|
||
|
symmetric-key cryptography to be mixed into the already existing
|
||
|
public-key cryptography, for post-quantum resistance.'';
|
||
|
};
|
||
|
|
||
|
listenPort = mkOption {
|
||
|
default = null;
|
||
|
type = with types; nullOr int;
|
||
|
example = 51820;
|
||
|
description = ''16-bit port for listening. Optional; if not specified,
|
||
|
automatically generated based on interface name.'';
|
||
|
};
|
||
|
|
||
|
preSetup = mkOption {
|
||
|
example = literalExample [''
|
||
|
${pkgs.iproute}/bin/ip netns add foo
|
||
|
''];
|
||
|
default = [];
|
||
|
type = with types; listOf str;
|
||
|
description = ''A list of commands called at the start of the interface
|
||
|
setup.'';
|
||
|
};
|
||
|
|
||
|
postSetup = mkOption {
|
||
|
example = literalExample [''
|
||
|
${pkgs.bash} -c 'printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0'
|
||
|
''];
|
||
|
default = [];
|
||
|
type = with types; listOf str;
|
||
|
description = "A list of commands called at the end of the interface setup.";
|
||
|
};
|
||
|
|
||
|
postShutdown = mkOption {
|
||
|
example = literalExample ["${pkgs.openresolv}/bin/resolvconf -d wg0"];
|
||
|
default = [];
|
||
|
type = with types; listOf str;
|
||
|
description = "A list of commands called after shutting down the interface.";
|
||
|
};
|
||
|
|
||
|
peers = mkOption {
|
||
|
default = [];
|
||
|
description = "Peers linked to the interface.";
|
||
|
type = with types; listOf (submodule peerOpts);
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
# peer options
|
||
|
|
||
|
peerOpts = {
|
||
|
|
||
|
options = {
|
||
|
|
||
|
publicKey = mkOption {
|
||
|
example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
|
||
|
type = types.str;
|
||
|
description = "The base64 public key the peer.";
|
||
|
};
|
||
|
|
||
|
allowedIPs = mkOption {
|
||
|
example = [ "10.192.122.3/32" "10.192.124.1/24" ];
|
||
|
type = with types; listOf str;
|
||
|
description = ''List of IP (v4 or v6) addresses with CIDR masks from
|
||
|
which this peer is allowed to send incoming traffic and to which
|
||
|
outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may
|
||
|
be specified for matching all IPv4 addresses, and ::/0 may be specified
|
||
|
for matching all IPv6 addresses.'';
|
||
|
};
|
||
|
|
||
|
endpoint = mkOption {
|
||
|
default = null;
|
||
|
example = "demo.wireguard.io:12913";
|
||
|
type = with types; nullOr str;
|
||
|
description = ''Endpoint IP or hostname of the peer, followed by a colon,
|
||
|
and then a port number of the peer.'';
|
||
|
};
|
||
|
|
||
|
persistentKeepalive = mkOption {
|
||
|
default = null;
|
||
|
type = with types; nullOr int;
|
||
|
example = 25;
|
||
|
description = ''This is optional and is by default off, because most
|
||
|
users will not need it. It represents, in seconds, between 1 and 65535
|
||
|
inclusive, how often to send an authenticated empty packet to the peer,
|
||
|
for the purpose of keeping a stateful firewall or NAT mapping valid
|
||
|
persistently. For example, if the interface very rarely sends traffic,
|
||
|
but it might at anytime receive traffic from a peer, and it is behind
|
||
|
NAT, the interface might benefit from having a persistent keepalive
|
||
|
interval of 25 seconds; however, most users will not need this.'';
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
generateConf = name: values: pkgs.writeText "wireguard-${name}.conf" ''
|
||
|
[Interface]
|
||
|
PrivateKey = ${values.privateKey}
|
||
|
${optionalString (values.presharedKey != null) "PresharedKey = ${values.presharedKey}"}
|
||
|
${optionalString (values.listenPort != null) "ListenPort = ${toString values.listenPort}"}
|
||
|
|
||
|
${concatStringsSep "\n\n" (map (peer: ''
|
||
|
[Peer]
|
||
|
PublicKey = ${peer.publicKey}
|
||
|
${optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep ", " peer.allowedIPs}"}
|
||
|
${optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}"}
|
||
|
${optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}"}
|
||
|
'') values.peers)}
|
||
|
'';
|
||
|
|
||
|
ipCommand = "${pkgs.iproute}/bin/ip";
|
||
|
wgCommand = "${pkgs.wireguard}/bin/wg";
|
||
|
|
||
|
generateUnit = name: values:
|
||
|
nameValuePair "wireguard-${name}"
|
||
|
{
|
||
|
description = "WireGuard Tunnel - ${name}";
|
||
|
wantedBy = [ "ip-up.target" ];
|
||
|
serviceConfig = {
|
||
|
Type = "oneshot";
|
||
|
RemainAfterExit = true;
|
||
|
ExecStart = lib.flatten([
|
||
|
values.preSetup
|
||
|
|
||
|
"-${ipCommand} link del dev ${name}"
|
||
|
"${ipCommand} link add dev ${name} type wireguard"
|
||
|
"${wgCommand} setconf ${name} ${generateConf name values}"
|
||
|
|
||
|
(map (ip:
|
||
|
''${ipCommand} address add ${ip} dev ${name}''
|
||
|
) values.ips)
|
||
|
|
||
|
"${ipCommand} link set up dev ${name}"
|
||
|
|
||
|
(flatten (map (peer: (map (ip:
|
||
|
"${ipCommand} route add ${ip} dev ${name}"
|
||
|
) peer.allowedIPs)) values.peers))
|
||
|
|
||
|
values.postSetup
|
||
|
]);
|
||
|
|
||
|
ExecStop = [ ''${ipCommand} link del dev "${name}"'' ] ++ values.postShutdown;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
in
|
||
|
|
||
|
{
|
||
|
|
||
|
###### interface
|
||
|
|
||
|
options = {
|
||
|
|
||
|
networking.wireguard = {
|
||
|
|
||
|
interfaces = mkOption {
|
||
|
description = "Wireguard interfaces.";
|
||
|
default = {};
|
||
|
example = {
|
||
|
wg0 = {
|
||
|
ips = [ "192.168.20.4/24" ];
|
||
|
privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
|
||
|
peers = [
|
||
|
{ allowedIPs = [ "192.168.20.1/32" ];
|
||
|
publicKey = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
|
||
|
endpoint = "demo.wireguard.io:12913"; }
|
||
|
];
|
||
|
};
|
||
|
};
|
||
|
type = with types; attrsOf (submodule interfaceOpts);
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
###### implementation
|
||
|
|
||
|
config = mkIf (cfg.interfaces != {}) {
|
||
|
|
||
|
boot.extraModulePackages = [ kernel.wireguard ];
|
||
|
environment.systemPackages = [ pkgs.wireguard ];
|
||
|
|
||
|
systemd.services = mapAttrs' generateUnit cfg.interfaces;
|
||
|
|
||
|
};
|
||
|
|
||
|
}
|