1
1
Fork 1
mirror of https://github.com/oddlama/nixos-extra-modules.git synced 2025-10-10 22:00:39 +02:00

chore: remove external peers from wireguard module

This commit is contained in:
oddlama 2025-01-25 15:04:00 +01:00
parent 2502ff50ab
commit 0660c722cf
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
2 changed files with 278 additions and 361 deletions

View file

@ -6,30 +6,18 @@ inputs: final: prev: let
attrValues attrValues
concatLists concatLists
concatMap concatMap
concatStringsSep
escapeShellArg
filter filter
flatten
flip flip
genAttrs genAttrs
mapAttrs'
nameValuePair
partition partition
removeSuffix
warn warn
; ;
inherit inherit
(final.lib) (final.lib)
net net
concatAttrs
types types
; ;
inherit
(final.lib.secrets)
rageDecryptArgs
;
in { in {
lib = lib =
prev.lib prev.lib
@ -66,15 +54,15 @@ in {
in "wireguard-${wgName}-psks-${peer1}+${peer2}"; in "wireguard-${wgName}-psks-${peer1}+${peer2}";
# All nodes that are part of this network # All nodes that are part of this network
participatingNodes = participatingNodes = filter (n: builtins.hasAttr wgName nodes.${n}.config.wireguard) (
filter attrNames nodes
(n: builtins.hasAttr wgName nodes.${n}.config.wireguard) );
(attrNames nodes);
# Partition nodes by whether they are servers # Partition nodes by whether they are servers
_participatingNodes_isServerPartition = _participatingNodes_isServerPartition =
partition partition (
(n: (wgCfgOf n).server.host != null) n: (wgCfgOf n).server.host != null
)
participatingNodes; participatingNodes;
participatingServerNodes = _participatingNodes_isServerPartition.right; participatingServerNodes = _participatingNodes_isServerPartition.right;
@ -83,43 +71,26 @@ in {
# Maps all nodes that are part of this network to their addresses # Maps all nodes that are part of this network to their addresses
nodePeers = genAttrs participatingNodes (n: (wgCfgOf n).addresses); nodePeers = genAttrs participatingNodes (n: (wgCfgOf n).addresses);
externalPeerName = p: "external-${p}";
# Only peers that are defined as externalPeers on the given node.
# Prepends "external-" to their name.
externalPeersForNode = node:
mapAttrs' (p: nameValuePair (externalPeerName p)) (wgCfgOf node).server.externalPeers;
# All peers that are defined as externalPeers on any node.
# Prepends "external-" to their name.
allExternalPeers = concatAttrs (map externalPeersForNode participatingNodes);
# All peers that are part of this network
allPeers = nodePeers // allExternalPeers;
# Concatenation of all external peer names names without any transformations.
externalPeerNamesRaw = concatMap (n: attrNames (wgCfgOf n).server.externalPeers) participatingNodes;
# A list of all occurring addresses. # A list of all occurring addresses.
usedAddresses = usedAddresses = concatMap (n: (wgCfgOf n).addresses) participatingNodes;
concatMap (n: (wgCfgOf n).addresses) participatingNodes
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes);
# A list of all occurring addresses, but only includes addresses that # A list of all occurring addresses, but only includes addresses that
# are not assigned automatically. # are not assigned automatically.
explicitlyUsedAddresses = explicitlyUsedAddresses = flip concatMap participatingNodes (
flip concatMap participatingNodes n:
(n: filter (x: !types.isLazyValue x) (
filter (x: !types.isLazyValue x) concatLists
(concatLists (nodes.${n}.options.wireguard.type.nestedTypes.elemType.getSubOptions (wgCfgOf n))
(nodes.${n}.options.wireguard.type.nestedTypes.elemType.getSubOptions (wgCfgOf n)).addresses.definitions)) .addresses
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) participatingNodes); .definitions
)
);
# The cidrv4 and cidrv6 of the network spanned by all participating peer addresses. # The cidrv4 and cidrv6 of the network spanned by all participating peer addresses.
# This also takes into account any reserved address ranges that should be part of the network. # This also takes into account any reserved address ranges that should be part of the network.
networkAddresses = networkAddresses = net.cidr.merge (
net.cidr.merge (usedAddresses usedAddresses ++ concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes
++ concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes); );
# The network spanning cidr addresses. The respective cidrv4 and cirdv6 are only # The network spanning cidr addresses. The respective cidrv4 and cirdv6 are only
# included if they exist. # included if they exist.
@ -127,29 +98,30 @@ in {
# The cidrv4 and cidrv6 of the network spanned by all reserved addresses only. # The cidrv4 and cidrv6 of the network spanned by all reserved addresses only.
# Used to determine automatically assigned addresses first. # Used to determine automatically assigned addresses first.
spannedReservedNetwork = spannedReservedNetwork = net.cidr.merge (
net.cidr.merge (concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes); concatMap (n: (wgCfgOf n).server.reservedAddresses) participatingServerNodes
);
# Assigns an ipv4 address from spannedReservedNetwork.cidrv4 # Assigns an ipv4 address from spannedReservedNetwork.cidrv4
# to each participant that has not explicitly specified an ipv4 address. # to each participant that has not explicitly specified an ipv4 address.
assignedIpv4Addresses = assert assertMsg assignedIpv4Addresses = assert assertMsg (spannedReservedNetwork.cidrv4 != null)
(spannedReservedNetwork.cidrv4 != null)
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv4 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network."; "Wireguard network '${wgName}': At least one participating node must reserve a cidrv4 address via `reservedAddresses` so that ipv4 addresses can be assigned automatically from that network.";
net.cidr.assignIps net.cidr.assignIps spannedReservedNetwork.cidrv4
spannedReservedNetwork.cidrv4
# Don't assign any addresses that are explicitly configured on other hosts # Don't assign any addresses that are explicitly configured on other hosts
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv4) (filter net.ip.isv4 explicitlyUsedAddresses)) (filter (x: net.cidr.contains x spannedReservedNetwork.cidrv4) (
filter net.ip.isv4 explicitlyUsedAddresses
))
participatingNodes; participatingNodes;
# Assigns an ipv6 address from spannedReservedNetwork.cidrv6 # Assigns an ipv6 address from spannedReservedNetwork.cidrv6
# to each participant that has not explicitly specified an ipv6 address. # to each participant that has not explicitly specified an ipv6 address.
assignedIpv6Addresses = assert assertMsg assignedIpv6Addresses = assert assertMsg (spannedReservedNetwork.cidrv6 != null)
(spannedReservedNetwork.cidrv6 != null)
"Wireguard network '${wgName}': At least one participating node must reserve a cidrv6 address via `reservedAddresses` so that ipv6 addresses can be assigned automatically from that network."; "Wireguard network '${wgName}': At least one participating node must reserve a cidrv6 address via `reservedAddresses` so that ipv6 addresses can be assigned automatically from that network.";
net.cidr.assignIps net.cidr.assignIps spannedReservedNetwork.cidrv6
spannedReservedNetwork.cidrv6
# Don't assign any addresses that are explicitly configured on other hosts # Don't assign any addresses that are explicitly configured on other hosts
(filter (x: net.cidr.contains x spannedReservedNetwork.cidrv6) (filter net.ip.isv6 explicitlyUsedAddresses)) (filter (x: net.cidr.contains x spannedReservedNetwork.cidrv6) (
filter net.ip.isv6 explicitlyUsedAddresses
))
participatingNodes; participatingNodes;
# Appends / replaces the correct cidr length to the argument, # Appends / replaces the correct cidr length to the argument,
@ -160,45 +132,11 @@ in {
then networkAddresses.cidrv6 then networkAddresses.cidrv6
else networkAddresses.cidrv4; else networkAddresses.cidrv4;
in "${net.cidr.ip addr}/${toString (net.cidr.length relevantNetworkAddr)}"; in "${net.cidr.ip addr}/${toString (net.cidr.length relevantNetworkAddr)}";
# Creates a script that when executed outputs a wg-quick compatible configuration
# file for use with external peers. This is a script so we can access secrets without
# storing them in the nix-store.
wgQuickConfigScript = system: serverNode: extPeer: let
pkgs = userInputs.self.pkgs.${system};
snCfg = wgCfgOf serverNode;
peerName = externalPeerName extPeer;
addresses = map toNetworkAddr snCfg.server.externalPeers.${extPeer};
in
pkgs.writeShellScript "create-wg-conf-${wgName}-${serverNode}-${extPeer}" ''
privKey=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPrivateKeyPath peerName)}) \
|| { echo "error: Failed to decrypt!" >&2; exit 1; }
serverPsk=$(${pkgs.rage}/bin/rage -d ${rageDecryptArgs} ${escapeShellArg (peerPresharedKeyPath serverNode peerName)}) \
|| { echo "error: Failed to decrypt!" >&2; exit 1; }
cat <<EOF
[Interface]
Address = ${concatStringsSep ", " addresses}
PrivateKey = $privKey
[Peer]
PublicKey = ${removeSuffix "\n" (builtins.readFile (peerPublicKeyPath serverNode))}
PresharedKey = $serverPsk
AllowedIPs = ${concatStringsSep ", " networkCidrs}
Endpoint = ${snCfg.server.host}:${toString snCfg.server.port}
PersistentKeepalive = 25
EOF
'';
in { in {
inherit inherit
allExternalPeers
allPeers
assignedIpv4Addresses assignedIpv4Addresses
assignedIpv6Addresses assignedIpv6Addresses
explicitlyUsedAddresses explicitlyUsedAddresses
externalPeerName
externalPeerNamesRaw
externalPeersForNode
networkAddresses networkAddresses
networkCidrs networkCidrs
nodePeers nodePeers
@ -218,27 +156,23 @@ in {
toNetworkAddr toNetworkAddr
usedAddresses usedAddresses
wgCfgOf wgCfgOf
wgQuickConfigScript
; ;
}; };
wireguard.createEvalCache = userInputs: wgNames: wireguard.createEvalCache = userInputs: wgNames: genAttrs wgNames (wireguard.evaluateNetwork userInputs);
genAttrs wgNames (wireguard.evaluateNetwork userInputs);
wireguard.getNetwork = userInputs: wgName: wireguard.getNetwork = userInputs: wgName:
userInputs.self.wireguardEvalCache.${wgName} userInputs.self.wireguardEvalCache.${wgName}
or ( or (warn ''
warn '' The calculated information for the wireguard network "${wgName}" is not cached!
The calculated information for the wireguard network "${wgName}" is not cached! This will siginificantly increase evaluation times. Please consider pre-evaluating
This will siginificantly increase evaluation times. Please consider pre-evaluating this information by exposing it in your flake:
this information by exposing it in your flake:
wireguardEvalCache = lib.wireguard.createEvalCache inputs [ wireguardEvalCache = lib.wireguard.createEvalCache inputs [
"${wgName}" "${wgName}"
# all other networks # all other networks
]; ];
'' (wireguard.evaluateNetwork userInputs wgName) '' (wireguard.evaluateNetwork userInputs wgName));
);
}; };
} }

View file

@ -7,11 +7,8 @@
inherit inherit
(lib) (lib)
any any
attrNames
attrValues
concatAttrs concatAttrs
concatMap concatMap
concatMapStrings
concatStringsSep concatStringsSep
duplicates duplicates
filter filter
@ -36,8 +33,6 @@
configForNetwork = wgName: wgCfg: let configForNetwork = wgName: wgCfg: let
inherit inherit
(lib.wireguard.getNetwork inputs wgName) (lib.wireguard.getNetwork inputs wgName)
externalPeerName
externalPeerNamesRaw
networkCidrs networkCidrs
participatingClientNodes participatingClientNodes
participatingNodes participatingNodes
@ -57,9 +52,9 @@
filterSelf = filter (x: x != nodeName); filterSelf = filter (x: x != nodeName);
# All nodes that use our node as the via into the wireguard network # All nodes that use our node as the via into the wireguard network
ourClientNodes = ourClientNodes = optionals isServer (
optionals isServer filter (n: (wgCfgOf n).client.via == nodeName) participatingClientNodes
(filter (n: (wgCfgOf n).client.via == nodeName) participatingClientNodes); );
# The list of peers for which we have to know the psk. # The list of peers for which we have to know the psk.
neededPeers = neededPeers =
@ -67,15 +62,11 @@
then then
# Other servers in the same network # Other servers in the same network
filterSelf participatingServerNodes filterSelf participatingServerNodes
# Our external peers
++ map externalPeerName (attrNames wgCfg.server.externalPeers)
# Our clients # Our clients
++ ourClientNodes ++ ourClientNodes
else [wgCfg.client.via]; else [wgCfg.client.via];
# Figure out if there are duplicate peers or addresses so we can # Figure out if there are duplicate addresses so we can make an assertion later.
# make an assertion later.
duplicatePeers = duplicates externalPeerNamesRaw;
duplicateAddrs = duplicates usedAddresses; duplicateAddrs = duplicates usedAddresses;
# Adds context information to the assertions for this network # Adds context information to the assertions for this network
@ -93,10 +84,10 @@
map (net.cidr.make 128) ( map (net.cidr.make 128) (
# The server accepts traffic to it's own address # The server accepts traffic to it's own address
snCfg.addresses snCfg.addresses
# plus traffic for any of its external peers
++ attrValues snCfg.server.externalPeers
# plus traffic for any client that is connected via that server # plus traffic for any client that is connected via that server
++ concatMap (n: (wgCfgOf n).addresses) (filter (n: (wgCfgOf n).client.via == serverNode) participatingClientNodes) ++ concatMap (n: (wgCfgOf n).addresses) (
filter (n: (wgCfgOf n).client.via == serverNode) participatingClientNodes
)
); );
in { in {
assertions = [ assertions = [
@ -104,10 +95,6 @@
assertion = any (n: (wgCfgOf n).server.host != null) participatingNodes; assertion = any (n: (wgCfgOf n).server.host != null) participatingNodes;
message = "${assertionPrefix}: At least one node in a network must be a server."; message = "${assertionPrefix}: At least one node in a network must be a server.";
} }
{
assertion = duplicatePeers == [];
message = "${assertionPrefix}: Multiple definitions for external peer(s):${concatMapStrings (x: " '${x}'") duplicatePeers}";
}
{ {
assertion = duplicateAddrs == []; assertion = duplicateAddrs == [];
message = "${assertionPrefix}: Addresses used multiple times: ${concatStringsSep ", " duplicateAddrs}"; message = "${assertionPrefix}: Addresses used multiple times: ${concatStringsSep ", " duplicateAddrs}";
@ -127,7 +114,9 @@
]; ];
# Open the udp port for the wireguard endpoint in the firewall # Open the udp port for the wireguard endpoint in the firewall
networking.firewall.allowedUDPPorts = mkIf (isServer && wgCfg.server.openFirewall) [wgCfg.server.port]; networking.firewall.allowedUDPPorts = mkIf (isServer && wgCfg.server.openFirewall) [
wgCfg.server.port
];
# If requested, create firewall rules for the network / specific participants and open ports. # If requested, create firewall rules for the network / specific participants and open ports.
networking.nftables.firewall = let networking.nftables.firewall = let
@ -138,17 +127,19 @@
# Parent zone for the whole interface # Parent zone for the whole interface
"wg-${wgCfg.linkName}".interfaces = [wgCfg.linkName]; "wg-${wgCfg.linkName}".interfaces = [wgCfg.linkName];
} }
// listToAttrs (flip map participatingNodes ( // listToAttrs (
peer: let flip map participatingNodes (
peerCfg = wgCfgOf peer; peer: let
in peerCfg = wgCfgOf peer;
# Subzone to specifically target the peer in
nameValuePair "wg-${wgCfg.linkName}-node-${peer}" { # Subzone to specifically target the peer
parent = "wg-${wgCfg.linkName}"; nameValuePair "wg-${wgCfg.linkName}-node-${peer}" {
ipv4Addresses = [peerCfg.ipv4]; parent = "wg-${wgCfg.linkName}";
ipv6Addresses = [peerCfg.ipv6]; ipv4Addresses = [peerCfg.ipv4];
} ipv6Addresses = [peerCfg.ipv6];
)); }
)
);
rules = rules =
{ {
@ -166,34 +157,37 @@
}; };
} }
# Open ports for specific nodes network # Open ports for specific nodes network
// listToAttrs (flip map participatingNodes ( // listToAttrs (
peer: flip map participatingNodes (
nameValuePair "wg-${wgCfg.linkName}-node-${peer}-to-${localZoneName}" ( peer:
mkIf (wgCfg.firewallRuleForNode ? ${peer}) { nameValuePair "wg-${wgCfg.linkName}-node-${peer}-to-${localZoneName}" (
from = ["wg-${wgCfg.linkName}-node-${peer}"]; mkIf (wgCfg.firewallRuleForNode ? ${peer}) {
to = [localZoneName]; from = ["wg-${wgCfg.linkName}-node-${peer}"];
ignoreEmptyRule = true; to = [localZoneName];
ignoreEmptyRule = true;
inherit inherit
(wgCfg.firewallRuleForNode.${peer}) (wgCfg.firewallRuleForNode.${peer})
allowedTCPPorts allowedTCPPorts
allowedUDPPorts allowedUDPPorts
; ;
} }
) )
)); )
);
}; };
age.secrets = age.secrets =
concatAttrs (map concatAttrs (
(other: { map (other: {
${peerPresharedKeySecret nodeName other} = { ${peerPresharedKeySecret nodeName other} = {
rekeyFile = peerPresharedKeyPath nodeName other; rekeyFile = peerPresharedKeyPath nodeName other;
owner = "systemd-network"; owner = "systemd-network";
generator.script = {pkgs, ...}: "${pkgs.wireguard-tools}/bin/wg genpsk"; generator.script = {pkgs, ...}: "${pkgs.wireguard-tools}/bin/wg genpsk";
}; };
}) })
neededPeers) neededPeers
)
// { // {
${peerPrivateKeySecret nodeName} = { ${peerPrivateKeySecret nodeName} = {
rekeyFile = peerPrivateKeyPath nodeName; rekeyFile = peerPrivateKeyPath nodeName;
@ -204,7 +198,9 @@
... ...
}: '' }: ''
priv=$(${pkgs.wireguard-tools}/bin/wg genkey) priv=$(${pkgs.wireguard-tools}/bin/wg genkey)
${pkgs.wireguard-tools}/bin/wg pubkey <<< "$priv" > ${lib.escapeShellArg (lib.removeSuffix ".age" file + ".pub")} ${pkgs.wireguard-tools}/bin/wg pubkey <<< "$priv" > ${
lib.escapeShellArg (lib.removeSuffix ".age" file + ".pub")
}
echo "$priv" echo "$priv"
''; '';
}; };
@ -227,34 +223,26 @@
if isServer if isServer
then then
# Always include all other server nodes. # Always include all other server nodes.
map (serverNode: let map (
snCfg = wgCfgOf serverNode; serverNode: let
in { snCfg = wgCfgOf serverNode;
PublicKey = builtins.readFile (peerPublicKeyPath serverNode); in {
PresharedKeyFile = config.age.secrets.${peerPresharedKeySecret nodeName serverNode}.path; PublicKey = builtins.readFile (peerPublicKeyPath serverNode);
AllowedIPs = serverAllowedIPs serverNode; PresharedKeyFile = config.age.secrets.${peerPresharedKeySecret nodeName serverNode}.path;
Endpoint = "${snCfg.server.host}:${toString snCfg.server.port}"; AllowedIPs = serverAllowedIPs serverNode;
}) Endpoint = "${snCfg.server.host}:${toString snCfg.server.port}";
(filterSelf participatingServerNodes) }
# All our external peers ) (filterSelf participatingServerNodes)
++ mapAttrsToList (extPeer: ips: let
peerName = externalPeerName extPeer;
in {
PublicKey = builtins.readFile (peerPublicKeyPath peerName);
PresharedKeyFile = config.age.secrets.${peerPresharedKeySecret nodeName peerName}.path;
AllowedIPs = map (net.cidr.make 128) ips;
# Connections to external peers should always be kept alive
PersistentKeepalive = 25;
})
wgCfg.server.externalPeers
# All client nodes that have their via set to us. # All client nodes that have their via set to us.
++ map (clientNode: let ++ map (
clientCfg = wgCfgOf clientNode; clientNode: let
in { clientCfg = wgCfgOf clientNode;
PublicKey = builtins.readFile (peerPublicKeyPath clientNode); in {
PresharedKeyFile = config.age.secrets.${peerPresharedKeySecret nodeName clientNode}.path; PublicKey = builtins.readFile (peerPublicKeyPath clientNode);
AllowedIPs = map (net.cidr.make 128) clientCfg.addresses; PresharedKeyFile = config.age.secrets.${peerPresharedKeySecret nodeName clientNode}.path;
}) AllowedIPs = map (net.cidr.make 128) clientCfg.addresses;
}
)
ourClientNodes ourClientNodes
else else
# We are a client node, so only include our via server. # We are a client node, so only include our via server.
@ -286,189 +274,184 @@ in {
options.wireguard = mkOption { options.wireguard = mkOption {
default = {}; default = {};
description = "Configures wireguard networks via systemd-networkd."; description = "Configures wireguard networks via systemd-networkd.";
type = types.lazyAttrsOf (types.submodule ({ type = types.lazyAttrsOf (
config, types.submodule (
name, {
options, config,
... name,
}: { options,
options = { ...
server = { }: {
host = mkOption { options = {
default = null; server = {
type = types.nullOr types.str; host = mkOption {
description = "The hostname or ip address which other peers can use to reach this host. No server functionality will be activated if set to null."; default = null;
}; type = types.nullOr types.str;
description = "The hostname or ip address which other peers can use to reach this host. No server functionality will be activated if set to null.";
port = mkOption {
default = 51820;
type = types.port;
description = "The port to listen on.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified {option}`port`.";
};
externalPeers = mkOption {
type = types.attrsOf (types.listOf (types.net.ip-in config.addresses));
default = {};
example = {my-android-phone = ["10.0.0.97"];};
description = ''
Allows defining an extra set of peers that should be added to this wireguard network,
but will not be managed by this flake. (e.g. phones)
These external peers will only know this node as a peer, which will forward
their traffic to other members of the network if required. This requires
this node to act as a server.
'';
};
reservedAddresses = mkOption {
type = types.listOf types.net.cidr;
default = [];
example = ["10.0.0.0/24" "fd00:cafe::/64"];
description = ''
Allows defining extra CIDR network ranges that shall be reserved for this network.
Reservation means that those address spaces will be guaranteed to be included in
the spanned network, but no rules will be enforced as to who in the network may use them.
By default, this module will try to allocate the smallest address space that includes
all network peers. If you know that there might be additional external peers added later,
it may be beneficial to reserve a bigger address space from the start to avoid having
to update existing external peers when the generated address space expands.
'';
};
};
client = {
via = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The server node via which to connect to the network.
No client functionality will be activated if set to null.
'';
};
keepalive = mkOption {
default = true;
type = types.bool;
description = "Whether to keep this connection alive using PersistentKeepalive. Set to false only for networks where client and server IPs are stable.";
};
};
priority = mkOption {
default = 40;
type = types.int;
description = "The order priority used when creating systemd netdev and network files.";
};
linkName = mkOption {
default = name;
type = types.str;
description = "The name for the created network interface.";
};
unitConfName = mkOption {
default = "${toString config.priority}-${config.linkName}";
readOnly = true;
type = types.str;
description = ''
The name used for unit configuration files. This is a read-only option.
Access this if you want to add additional settings to the generated systemd units.
'';
};
ipv4 = mkOption {
type = types.lazyOf types.net.ipv4;
default = types.lazyValue (lib.wireguard.getNetwork inputs name).assignedIpv4Addresses.${nodeName};
description = ''
The ipv4 address for this machine. If you do not set this explicitly,
a semi-stable ipv4 address will be derived automatically based on the
hostname of this machine. At least one participating server must reserve
a big-enough space of addresses by setting `reservedAddresses`.
See `net.cidr.assignIps` for more information on the algorithm.
'';
};
ipv6 = mkOption {
type = types.lazyOf types.net.ipv6;
default = types.lazyValue (lib.wireguard.getNetwork inputs name).assignedIpv6Addresses.${nodeName};
description = ''
The ipv6 address for this machine. If you do not set this explicitly,
a semi-stable ipv6 address will be derived automatically based on the
hostname of this machine. At least one participating server must reserve
a big-enough space of addresses by setting `reservedAddresses`.
See `net.cidr.assignIps` for more information on the algorithm.
'';
};
addresses = mkOption {
type = types.listOf (types.lazyOf types.net.ip);
default = [
(head options.ipv4.definitions)
(head options.ipv6.definitions)
];
description = ''
The ip addresses (v4 and/or v6) to use for this machine.
The actual network cidr will automatically be derived from all network participants.
By default this will just include {option}`ipv4` and {option}`ipv6` as configured.
'';
};
firewallRuleForAll = mkOption {
default = {};
description = ''
Allows you to set specific firewall rules for traffic originating from any participant in this
wireguard network. A corresponding rule `wg-<network-name>-to-<local-zone-name>` will be created to easily expose
services to the network.
'';
type = types.submodule {
options = {
allowedTCPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific TCP ports for traffic from the network.";
}; };
allowedUDPPorts = mkOption {
type = types.listOf types.port; port = mkOption {
default = 51820;
type = types.port;
description = "The port to listen on.";
};
openFirewall = mkOption {
default = false;
type = types.bool;
description = "Whether to open the firewall for the specified {option}`port`.";
};
reservedAddresses = mkOption {
type = types.listOf types.net.cidr;
default = []; default = [];
description = "Convenience option to open specific UDP ports for traffic from the network."; example = [
"10.0.0.0/24"
"fd00:cafe::/64"
];
description = ''
Allows defining extra CIDR network ranges that shall be reserved for this network.
Reservation means that those address spaces will be guaranteed to be included in
the spanned network, but no rules will be enforced as to who in the network may use them.
By default, this module will try to allocate the smallest address space that includes
all network peers.
'';
}; };
}; };
};
};
firewallRuleForNode = mkOption { client = {
default = {}; via = mkOption {
description = '' default = null;
Allows you to set specific firewall rules just for traffic originating from another network node. type = types.nullOr types.str;
A corresponding rule `wg-<network-name>-node-<node-name>-to-<local-zone-name>` will be created to easily expose description = ''
services to that node. The server node via which to connect to the network.
''; No client functionality will be activated if set to null.
type = types.attrsOf (types.submodule { '';
options = {
allowedTCPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific TCP ports for traffic from another node.";
}; };
allowedUDPPorts = mkOption {
type = types.listOf types.port; keepalive = mkOption {
default = []; default = true;
description = "Convenience option to open specific UDP ports for traffic from another node."; type = types.bool;
description = "Whether to keep this connection alive using PersistentKeepalive. Set to false only for networks where client and server IPs are stable.";
}; };
}; };
});
}; priority = mkOption {
}; default = 40;
})); type = types.int;
description = "The order priority used when creating systemd netdev and network files.";
};
linkName = mkOption {
default = name;
type = types.str;
description = "The name for the created network interface.";
};
unitConfName = mkOption {
default = "${toString config.priority}-${config.linkName}";
readOnly = true;
type = types.str;
description = ''
The name used for unit configuration files. This is a read-only option.
Access this if you want to add additional settings to the generated systemd units.
'';
};
ipv4 = mkOption {
type = types.lazyOf types.net.ipv4;
default = types.lazyValue (lib.wireguard.getNetwork inputs name).assignedIpv4Addresses.${nodeName};
description = ''
The ipv4 address for this machine. If you do not set this explicitly,
a semi-stable ipv4 address will be derived automatically based on the
hostname of this machine. At least one participating server must reserve
a big-enough space of addresses by setting `reservedAddresses`.
See `net.cidr.assignIps` for more information on the algorithm.
'';
};
ipv6 = mkOption {
type = types.lazyOf types.net.ipv6;
default = types.lazyValue (lib.wireguard.getNetwork inputs name).assignedIpv6Addresses.${nodeName};
description = ''
The ipv6 address for this machine. If you do not set this explicitly,
a semi-stable ipv6 address will be derived automatically based on the
hostname of this machine. At least one participating server must reserve
a big-enough space of addresses by setting `reservedAddresses`.
See `net.cidr.assignIps` for more information on the algorithm.
'';
};
addresses = mkOption {
type = types.listOf (types.lazyOf types.net.ip);
default = [
(head options.ipv4.definitions)
(head options.ipv6.definitions)
];
description = ''
The ip addresses (v4 and/or v6) to use for this machine.
The actual network cidr will automatically be derived from all network participants.
By default this will just include {option}`ipv4` and {option}`ipv6` as configured.
'';
};
firewallRuleForAll = mkOption {
default = {};
description = ''
Allows you to set specific firewall rules for traffic originating from any participant in this
wireguard network. A corresponding rule `wg-<network-name>-to-<local-zone-name>` will be created to easily expose
services to the network.
'';
type = types.submodule {
options = {
allowedTCPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific TCP ports for traffic from the network.";
};
allowedUDPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific UDP ports for traffic from the network.";
};
};
};
};
firewallRuleForNode = mkOption {
default = {};
description = ''
Allows you to set specific firewall rules just for traffic originating from another network node.
A corresponding rule `wg-<network-name>-node-<node-name>-to-<local-zone-name>` will be created to easily expose
services to that node.
'';
type = types.attrsOf (
types.submodule {
options = {
allowedTCPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific TCP ports for traffic from another node.";
};
allowedUDPPorts = mkOption {
type = types.listOf types.port;
default = [];
description = "Convenience option to open specific UDP ports for traffic from another node.";
};
};
}
);
};
};
}
)
);
}; };
config = mkIf (cfg != {}) (mergeToplevelConfigs config = mkIf (cfg != {}) (
["assertions" "age" "networking" "systemd"] mergeToplevelConfigs ["assertions" "age" "networking" "systemd"] (
(mapAttrsToList configForNetwork cfg)); mapAttrsToList configForNetwork cfg
)
);
} }