forked from mirrors_public/oddlama_nix-config
feat: allow reservation of addresses in wireguard network
This commit is contained in:
parent
0221a24225
commit
f95bc0eb30
4 changed files with 70 additions and 30 deletions
|
@ -67,16 +67,16 @@
|
||||||
# > net.cidr.canonicalize "192.168.1.100/24"
|
# > net.cidr.canonicalize "192.168.1.100/24"
|
||||||
# "192.168.1.0/24"
|
# "192.168.1.0/24"
|
||||||
canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x);
|
canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x);
|
||||||
# coercev4 :: [cidr4 | ipv4] -> (cidr4 | null)
|
# mergev4 :: [cidr4 | ipv4] -> (cidr4 | null)
|
||||||
#
|
#
|
||||||
# Returns the smallest cidr network that includes all given addresses.
|
# Returns the smallest cidr network that includes all given networks.
|
||||||
# If no cidr mask is given, /32 is assumed.
|
# If no cidr mask is given, /32 is assumed.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
#
|
#
|
||||||
# > net.cidr.coercev4 ["192.168.1.1/24" "192.168.6.1/32"]
|
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
|
||||||
# "192.168.0.0/21"
|
# "192.168.0.0/21"
|
||||||
coercev4 = addrs_: let
|
mergev4 = addrs_: let
|
||||||
# Append /32 if necessary
|
# Append /32 if necessary
|
||||||
addrs = map (x:
|
addrs = map (x:
|
||||||
if lib.hasInfix "/" x
|
if lib.hasInfix "/" x
|
||||||
|
@ -104,20 +104,20 @@
|
||||||
addrs)
|
addrs)
|
||||||
possibleLengths);
|
possibleLengths);
|
||||||
in
|
in
|
||||||
assert lib.assertMsg (!lib.any (lib.hasInfix ":") addrs) "coercev4 cannot operate on ipv6 addresses";
|
assert lib.assertMsg (!lib.any (lib.hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
|
||||||
if addrs == []
|
if addrs == []
|
||||||
then null
|
then null
|
||||||
else libWithNet.net.cidr.make bestLength firstIp;
|
else libWithNet.net.cidr.make bestLength firstIp;
|
||||||
# coercev6 :: [cidr6 | ipv6] -> (cidr6 | null)
|
# mergev6 :: [cidr6 | ipv6] -> (cidr6 | null)
|
||||||
#
|
#
|
||||||
# Returns the smallest cidr network that includes all given addresses.
|
# Returns the smallest cidr network that includes all given networks.
|
||||||
# If no cidr mask is given, /128 is assumed.
|
# If no cidr mask is given, /128 is assumed.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
#
|
#
|
||||||
# > net.cidr.coercev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
|
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
|
||||||
# "fd00:c000::/18"
|
# "fd00:c000::/18"
|
||||||
coercev6 = addrs_: let
|
mergev6 = addrs_: let
|
||||||
# Append /128 if necessary
|
# Append /128 if necessary
|
||||||
addrs = map (x:
|
addrs = map (x:
|
||||||
if lib.hasInfix "/" x
|
if lib.hasInfix "/" x
|
||||||
|
@ -145,20 +145,20 @@
|
||||||
addrs)
|
addrs)
|
||||||
possibleLengths);
|
possibleLengths);
|
||||||
in
|
in
|
||||||
assert lib.assertMsg (lib.all (lib.hasInfix ":") addrs) "coercev6 cannot operate on ipv4 addresses";
|
assert lib.assertMsg (lib.all (lib.hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
|
||||||
if addrs == []
|
if addrs == []
|
||||||
then null
|
then null
|
||||||
else libWithNet.net.cidr.make bestLength firstIp;
|
else libWithNet.net.cidr.make bestLength firstIp;
|
||||||
# coerce :: [cidr] -> { cidrv4 = (cidr4 | null); cidrv6 = (cidr4 | null); }
|
# merge :: [cidr] -> { cidrv4 = (cidr4 | null); cidrv6 = (cidr4 | null); }
|
||||||
#
|
#
|
||||||
# Returns the smallest cidr network that includes all given addresses,
|
# Returns the smallest cidr network that includes all given networks,
|
||||||
# but yields two separate result for all given ipv4 and ipv6 addresses.
|
# but yields two separate result for all given ipv4 and ipv6 addresses.
|
||||||
# Equivalent to calling coercev4 and coercev6 on a partition individually.
|
# Equivalent to calling mergev4 and mergev6 on a partition individually.
|
||||||
coerce = addrs: let
|
merge = addrs: let
|
||||||
v4_and_v6 = lib.partition (lib.hasInfix ":") addrs;
|
v4_and_v6 = lib.partition (lib.hasInfix ":") addrs;
|
||||||
in {
|
in {
|
||||||
cidrv4 = coercev4 v4_and_v6.wrong;
|
cidrv4 = mergev4 v4_and_v6.wrong;
|
||||||
cidrv6 = coercev6 v4_and_v6.right;
|
cidrv6 = mergev6 v4_and_v6.right;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
ip = {
|
ip = {
|
||||||
|
|
|
@ -187,6 +187,7 @@
|
||||||
inherit (vmCfg.networking) host;
|
inherit (vmCfg.networking) host;
|
||||||
inherit (cfg.networking.wireguard) port;
|
inherit (cfg.networking.wireguard) port;
|
||||||
openFirewallRules = ["${vmCfg.networking.mainLinkName}-to-local"];
|
openFirewallRules = ["${vmCfg.networking.mainLinkName}-to-local"];
|
||||||
|
reservedAddresses = [cfg.networking.wireguard.cidrv4 cfg.networking.wireguard.cidrv6];
|
||||||
};
|
};
|
||||||
# If We don't have such guarantees, so we must use a client-server architecture.
|
# If We don't have such guarantees, so we must use a client-server architecture.
|
||||||
client = optionalAttrs (cfg.networking.host == null) {
|
client = optionalAttrs (cfg.networking.host == null) {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
lib,
|
lib,
|
||||||
extraLib,
|
extraLib,
|
||||||
pkgs,
|
pkgs,
|
||||||
nodes,
|
|
||||||
nodeName,
|
nodeName,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
|
@ -42,9 +41,9 @@
|
||||||
configForNetwork = wgName: wgCfg: let
|
configForNetwork = wgName: wgCfg: let
|
||||||
inherit
|
inherit
|
||||||
(extraLib.wireguard wgName)
|
(extraLib.wireguard wgName)
|
||||||
|
associatedClientNodes
|
||||||
associatedNodes
|
associatedNodes
|
||||||
associatedServerNodes
|
associatedServerNodes
|
||||||
associatedClientNodes
|
|
||||||
externalPeerName
|
externalPeerName
|
||||||
externalPeerNamesRaw
|
externalPeerNamesRaw
|
||||||
peerPresharedKeyPath
|
peerPresharedKeyPath
|
||||||
|
@ -52,15 +51,14 @@
|
||||||
peerPrivateKeyPath
|
peerPrivateKeyPath
|
||||||
peerPrivateKeySecret
|
peerPrivateKeySecret
|
||||||
peerPublicKeyPath
|
peerPublicKeyPath
|
||||||
usedAddresses
|
|
||||||
toNetworkAddr
|
toNetworkAddr
|
||||||
|
usedAddresses
|
||||||
|
wgCfgOf
|
||||||
;
|
;
|
||||||
|
|
||||||
isServer = wgCfg.server.host != null;
|
isServer = wgCfg.server.host != null;
|
||||||
isClient = wgCfg.client.via != null;
|
isClient = wgCfg.client.via != null;
|
||||||
|
|
||||||
filterSelf = filter (x: x != nodeName);
|
filterSelf = filter (x: x != nodeName);
|
||||||
wgCfgOf = node: nodes.${node}.config.extra.wireguard.${wgName};
|
|
||||||
|
|
||||||
# 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 =
|
||||||
|
@ -82,7 +80,7 @@
|
||||||
# Figure out if there are duplicate peers or addresses so we can
|
# Figure out if there are duplicate peers or addresses so we can
|
||||||
# make an assertion later.
|
# make an assertion later.
|
||||||
duplicatePeers = duplicates externalPeerNamesRaw;
|
duplicatePeers = duplicates externalPeerNamesRaw;
|
||||||
duplicateAddrs = duplicates (map net.cidr.ip usedAddresses);
|
duplicateAddrs = duplicates usedAddresses;
|
||||||
|
|
||||||
# Adds context information to the assertions for this network
|
# Adds context information to the assertions for this network
|
||||||
assertionPrefix = "Wireguard network '${wgName}' on '${nodeName}'";
|
assertionPrefix = "Wireguard network '${wgName}' on '${nodeName}'";
|
||||||
|
@ -281,6 +279,22 @@ in {
|
||||||
this node to act as a server.
|
this node to act as a server.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reservedAddresses = mkOption {
|
||||||
|
type = types.listOf net.types.cidr;
|
||||||
|
default = [];
|
||||||
|
example = ["10.0.0.1/24" "fd00:cafe::/64"];
|
||||||
|
description = mdDoc ''
|
||||||
|
Allows defining extra cidr network ranges that shall be reserved for this machine
|
||||||
|
and its children (i.e. external peers or via clients). Reservation means that those
|
||||||
|
address spaces will be guaranteed to be included in the spanned network.
|
||||||
|
|
||||||
|
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 = {
|
client = {
|
||||||
|
@ -341,6 +355,25 @@ in {
|
||||||
By default this will just include {option}`ipv4` and {option}`ipv6` as configured.
|
By default this will just include {option}`ipv4` and {option}`ipv6` as configured.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# TODO this needs to be implemented.
|
||||||
|
# - is 0.0.0.0/0 also for valid for routing global ipv6?
|
||||||
|
# - is 0.0.0.0/0 routing private spaces such as 192.168.1 ? that'd be baaad
|
||||||
|
# - force nodes to opt-in or allow nodes to opt-out? sometimes a node want's
|
||||||
|
# to use the network without routing additional stuff.
|
||||||
|
# - allow specifying the route metric.
|
||||||
|
routedAddresses = mkOption {
|
||||||
|
type = types.listOf net.types.cidr;
|
||||||
|
default = [];
|
||||||
|
example = ["0.0.0.0/0"];
|
||||||
|
description = mdDoc ''
|
||||||
|
Additional networks that are accessible through this machine. This will allow
|
||||||
|
other participants of the network to access these networks through the tunnel.
|
||||||
|
|
||||||
|
Make sure to configure a NAT on the created interface (or that the proper routes
|
||||||
|
are generated) to allow inter-network communication.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
22
nix/lib.nix
22
nix/lib.nix
|
@ -138,6 +138,9 @@ in rec {
|
||||||
# Not ideal, but ok.
|
# Not ideal, but ok.
|
||||||
inherit (self.nodes.${head associatedNodes}.config.lib) net;
|
inherit (self.nodes.${head associatedNodes}.config.lib) net;
|
||||||
|
|
||||||
|
# Returns the given node's wireguard configuration of this network
|
||||||
|
wgCfgOf = node: self.nodes.${node}.config.extra.wireguard.${wgName};
|
||||||
|
|
||||||
sortedPeers = peerA: peerB:
|
sortedPeers = peerA: peerB:
|
||||||
if peerA < peerB
|
if peerA < peerB
|
||||||
then {
|
then {
|
||||||
|
@ -173,21 +176,21 @@ in rec {
|
||||||
# Partition nodes by whether they are servers
|
# Partition nodes by whether they are servers
|
||||||
_associatedNodes_isServerPartition =
|
_associatedNodes_isServerPartition =
|
||||||
partition
|
partition
|
||||||
(n: self.nodes.${n}.config.extra.wireguard.${wgName}.server.host != null)
|
(n: (wgCfgOf n).server.host != null)
|
||||||
associatedNodes;
|
associatedNodes;
|
||||||
|
|
||||||
associatedServerNodes = _associatedNodes_isServerPartition.right;
|
associatedServerNodes = _associatedNodes_isServerPartition.right;
|
||||||
associatedClientNodes = _associatedNodes_isServerPartition.wrong;
|
associatedClientNodes = _associatedNodes_isServerPartition.wrong;
|
||||||
|
|
||||||
# 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 associatedNodes (n: self.nodes.${n}.config.extra.wireguard.${wgName}.addresses);
|
nodePeers = genAttrs associatedNodes (n: (wgCfgOf n).addresses);
|
||||||
|
|
||||||
externalPeerName = p: "external-${p}";
|
externalPeerName = p: "external-${p}";
|
||||||
|
|
||||||
# Only peers that are defined as externalPeers on the given node.
|
# Only peers that are defined as externalPeers on the given node.
|
||||||
# Prepends "external-" to their name.
|
# Prepends "external-" to their name.
|
||||||
externalPeersForNode = node:
|
externalPeersForNode = node:
|
||||||
mapAttrs' (p: nameValuePair (externalPeerName p)) self.nodes.${node}.config.extra.wireguard.${wgName}.server.externalPeers;
|
mapAttrs' (p: nameValuePair (externalPeerName p)) (wgCfgOf node).server.externalPeers;
|
||||||
|
|
||||||
# All peers that are defined as externalPeers on any node.
|
# All peers that are defined as externalPeers on any node.
|
||||||
# Prepends "external-" to their name.
|
# Prepends "external-" to their name.
|
||||||
|
@ -197,15 +200,18 @@ in rec {
|
||||||
allPeers = nodePeers // allExternalPeers;
|
allPeers = nodePeers // allExternalPeers;
|
||||||
|
|
||||||
# Concatenation of all external peer names names without any transformations.
|
# Concatenation of all external peer names names without any transformations.
|
||||||
externalPeerNamesRaw = concatMap (n: attrNames self.nodes.${n}.config.extra.wireguard.${wgName}.server.externalPeers) associatedNodes;
|
externalPeerNamesRaw = concatMap (n: attrNames (wgCfgOf n).server.externalPeers) associatedNodes;
|
||||||
|
|
||||||
# A list of all occurring addresses.
|
# A list of all occurring addresses.
|
||||||
usedAddresses =
|
usedAddresses =
|
||||||
concatMap (n: self.nodes.${n}.config.extra.wireguard.${wgName}.addresses) associatedNodes
|
concatMap (n: (wgCfgOf n).addresses) associatedNodes
|
||||||
++ flatten (concatMap (n: map (net.cidr.make 128) (attrValues self.nodes.${n}.config.extra.wireguard.${wgName}.server.externalPeers)) associatedNodes);
|
++ flatten (concatMap (n: attrValues (wgCfgOf n).server.externalPeers) associatedNodes);
|
||||||
|
|
||||||
# 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.
|
||||||
networkAddresses = net.cidr.coerce usedAddresses;
|
# This also takes into account any reserved address ranges that should be part of the network.
|
||||||
|
networkAddresses =
|
||||||
|
net.cidr.merge (usedAddresses
|
||||||
|
++ concatMap (n: (wgCfgOf n).server.reservedAddresses) associatedServerNodes);
|
||||||
|
|
||||||
# Appends / replaces the correct cidr length to the argument,
|
# Appends / replaces the correct cidr length to the argument,
|
||||||
# so that the resulting address is in the cidr.
|
# so that the resulting address is in the cidr.
|
||||||
|
@ -221,7 +227,7 @@ in rec {
|
||||||
# storing them in the nix-store.
|
# storing them in the nix-store.
|
||||||
wgQuickConfigScript = system: serverNode: extPeer: let
|
wgQuickConfigScript = system: serverNode: extPeer: let
|
||||||
pkgs = self.pkgs.${system};
|
pkgs = self.pkgs.${system};
|
||||||
snCfg = self.nodes.${serverNode}.config.extra.wireguard.${wgName};
|
snCfg = wgCfgOf serverNode;
|
||||||
peerName = externalPeerName extPeer;
|
peerName = externalPeerName extPeer;
|
||||||
addresses = map toNetworkAddr snCfg.server.externalPeers.${extPeer};
|
addresses = map toNetworkAddr snCfg.server.externalPeers.${extPeer};
|
||||||
in
|
in
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue