allow multiple devs for same subnet

release
Matthew Salerno 11 months ago
parent 45b70c9063
commit 1e697eb859

1
.gitignore vendored

@ -0,0 +1 @@
/result

@ -0,0 +1,46 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{lib, devNameMethod ? "short", ...}@inputs: keyProviders: intermediateConfig: localPeerName:
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
let
thisPeer = intermediateConfig.peers."${localPeerName}";
# these aren't really important, I just wanted to reverse the argument order
forEachAttr' = flip mapAttrs';
forEachAttrToList = flip mapAttrsToList;
devName = getDevName devNameMethod localPeerName;
in
with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
{
networking.hosts = foldl' (mergeAttrs) {} (concatLists ( concatLists (forEachAttrToList thisPeer.subnetConnections (subnetName: subnetConnection:
forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: {"${asIp ip}" = ["${remotePeerName}.${subnetName}"];}))
))));
systemd.network = {
netdevs = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${devName subnetName}" {
netdevConfig = {
Kind = "wireguard";
Name = "${devName subnetName}";
};
wireguardConfig = {
ListenPort = subnetConnection.listenPort;
PrivateKeyFile = getPrivKeyFile;
};
wireguardPeers = forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: {
wireguardPeerConfig = {
Endpoint = "${peerConnection.endpoint.ip}:${builtins.toString peerConnection.endpoint.port}";
PublicKey = getPeerPubKey remotePeerName;
AllowedIPs = map (ip: asCidr ip) peerConnection.ipAddresses;
PresharedKeyFile = getSubnetPSKFile subnetName;
};
}
// (if peerConnection.endpoint ? persistentKeepalive then {PersistentKeepalive = peerConnection.endpoint.persistentKeepalive;} else {})
// (warnIf (peerConnection.endpoint ? dynamicEndpointRefreshSeconds) "dynamicEndpointRefreshSeconds not supported for networkd" {})
// (warnIf (peerConnection.endpoint ? dynamicEndpointRefreshRestartSeconds) "dynamicEndpointRefreshRestartSeconds not supported for networkd" {})
);
});
};
} // getProviderConfig

@ -3,19 +3,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{lib, ...}@inputs: keyProviders: intermediateConfig: localPeerName:
with lib.trivial;
with lib.attrsets;
with lib.lists;
{lib, devNameMethod ? "short", ...}@inputs: keyProviders: intermediateConfig: localPeerName:
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
with builtins;
with import ../lib.nix;
let
thisPeer = intermediateConfig.peers."${localPeerName}";
# these aren't really important, I just wanted to reverse the argument order
forEachAttr' = flip mapAttrs';
forEachAttrToList = flip mapAttrsToList;
shortName = fqdn: head (strings.splitString "." fqdn);
devName = getDevName devNameMethod localPeerName;
in
with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
{
@ -23,10 +20,10 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: {"${asIp ip}" = ["${remotePeerName}.${subnetName}"];}))
))));
systemd.network = {
netdevs = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" {
netdevs = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${devName subnetName}" {
netdevConfig = {
Kind = "wireguard";
Name = "${shortName subnetName}";
Name = "${devName subnetName}";
};
wireguardConfig = {
ListenPort = subnetConnection.listenPort;
@ -45,8 +42,8 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
// (warnIf (peerConnection.endpoint ? dynamicEndpointRefreshRestartSeconds) "dynamicEndpointRefreshRestartSeconds not supported for networkd" {})
);
});
networks = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" {
matchConfig.Name = "${shortName subnetName}";
networks = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${devName subnetName}" {
matchConfig.Name = "${devName subnetName}";
address = map (address: (asCidr' "64" "24" address)) subnetConnection.ipAddresses;
});
};

@ -3,18 +3,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{lib, ...}@inputs: keyProviders: intermediateConfig: localPeerName:
with lib.trivial;
with lib.attrsets;
with lib.lists;
{lib, devNameMethod ? "short", ...}@inputs: keyProviders: intermediateConfig: localPeerName:
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
with builtins;
with import ../lib.nix;
let
thisPeer = intermediateConfig.peers."${localPeerName}";
# these aren't really important, I just wanted to reverse the argument order
forEachAttr' = flip mapAttrs';
forEachAttrToList = flip mapAttrsToList;
devName = getDevName devNameMethod localPeerName;
in
with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
{
@ -22,7 +20,7 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: {"${asIp ip}" = ["${remotePeerName}.${subnetName}"];}))
))));
networking.wireguard = {
interfaces = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "${head (strings.splitString "." subnetName)}"
interfaces = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair (devName subnetName)
{
ips = map (address: (asCidr' "64" "24" address)) subnetConnection.ipAddresses;
listenPort = subnetConnection.listenPort;

@ -8,9 +8,9 @@
outputs = { self, nixpkgs, ... }:
let
forAllSystems = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ];
wnlib = import ./lib.nix { lib = nixpkgs.lib; };
in
{
wnlib = import ./lib.nix;
{
nixosModules.default = import ./wire.nix;
checks = forAllSystems (system:
let
@ -18,7 +18,7 @@
# reference to nixpkgs for the current system
pkgs = nixpkgs.legacyPackages.${system};
# this gives us a reference to our flake but also all flake inputs
inherit self;
inherit self wnlib;
};
in {
# import our test
@ -26,6 +26,7 @@
simple = import ./tests/simple.nix checkArgs;
mesh = import ./tests/mesh.nix checkArgs;
ring = import ./tests/ring.nix checkArgs;
double-dev-ring = import ./tests/double-dev-ring.nix checkArgs;
manual-ipv4 = import ./tests/manual-ipv4.nix checkArgs;
manual-ipv6 = import ./tests/manual-ipv6.nix checkArgs;
manual-ipv6-auto = import ./tests/manual-ipv6-auto.nix checkArgs;

@ -4,9 +4,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{lib, ...}: intermediateConfig: localPeerName:
with import ../lib.nix;
with lib.attrsets;
with builtins;
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
{
getPeerPubKey = remotePeerName: attrByPath [remotePeerName "publicKey"] null intermediateConfig.peers;
getPrivKeyFile = attrByPath [localPeerName "privateKeyFile"] null intermediateConfig.peers;

@ -4,9 +4,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{config, lib, ...}: intermediateConfig: localPeerName:
with (import ../lib.nix);
with lib.attrsets;
with builtins;
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
{
config.age = {
secrets = {

@ -3,21 +3,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
with builtins;
/**
ACL independent functions that can be used in parsers.
*/
let
# stubbornly not passing lib and reimplementing everything since 2023
findFirst = pred: default: list:
if length list == 0
then default
else
if pred (head list)
then head list
else findFirst pred default (tail list);
in
{ lib, ... }:
with lib;
rec {
/** Builtin Parsers */
defaultParsers = {
@ -27,6 +17,7 @@ rec {
defaultConfigurers = {
static = import ./configurers/static.nix;
networkd = import ./configurers/networkd.nix;
networkd-dev-only = import ./configurers/networkd-dev-only.nix;
};
/** Builtin key providers */
defaultKeyProviders = {
@ -76,10 +67,10 @@ rec {
funcs: list: map (pipe' funcs) list;
/** generate last 20 characters (80 bits) of the peer's IPv6 address */
generateIPv6Suffix = peerName: substring 0 16 (hashString "sha256" peerName);
generateIPv6Suffix = peerName: substring 0 16 (builtins.hashString "sha256" peerName);
/** generate the first 10 characters of the IPV6 address for the subnet name */
generateIPv6Prefix = subnetName: "fd" + (substring 0 14 (hashString "sha256" subnetName));
generateIPv6Prefix = subnetName: "fd" + (substring 0 14 (builtins.hashString "sha256" subnetName));
/** generates a full IPv6 subnet */
generateIPv6Subnet = subnetName: (addColonsToIPv6 (generateIPv6Prefix subnetName)) + "::/64";
@ -89,7 +80,25 @@ rec {
/** generates a full IPv6 address with cidr */
generateIPv6Cidr = subnetName: peerName: (addColonsToIPv6 ((generateIPv6Prefix subnetName) + (generateIPv6Suffix peerName))) + "/64";
getDevName' = devNameMethod: peerName: subnetName:
let
getDevNameLong = peerName: subnetName: subnetName;
getDevNameShort = peerName: subnetName: head (splitString "." subnetName);
getDevNameHash = peerName: subnetName: "wn." + (substring 0 12 (builtins.hashString "sha256" (peerName + "." + subnetName)));
in
if devNameMethod == "hash" then
getDevNameHash peerName subnetName
else if devNameMethod == "long" then
getDevNameLong peerName subnetName
else
getDevNameShort peerName subnetName;
getDevName = devNameMethod: peerName: subnetName:
let
name = getDevName' devNameMethod peerName subnetName;
in
throwIf (stringLength name > 15) "Wirenix: Dev name must be less than or equal to 15 characters. Try changing devNameMethod to \"hash\"" name;
# getDevName = subnetName: peerName: if stringLength (getDevNameLong subnetName peerName) > 12 then getDevNameShort subnetName peerName else getDevNameLong subnetName peerName;
/**
* makes the intermediate config non-recursive, so it can be pretty printed and
* inspected in the repl. Also helps with testing as it forces evaluation of the config.
@ -97,7 +106,7 @@ rec {
breakIntermediateRecursion = intermediateConfig:
let recurse = parentName:
mapAttrs (name: value:
if typeOf value == "set" then
if builtins.typeOf value == "set" then
if elem name [ "peer" "subnet" "group" "groups" ] then
"${name}s.${parentName}"
else if elem parentName ["peers"] then
@ -110,9 +119,9 @@ rec {
in
mapAttrs (name: value: recurse "" value) intermediateConfig;
nixosConfigForPeer = nixosConfigurations: peerName: builtins.head (builtins.attrValues (
lib.attrsets.filterAttrs (
name: value: (lib.attrsets.attrByPath ["config" "modules" "wirenix" "peerName"] null value) == peerName
nixosConfigForPeer = nixosConfigurations: peerName: head (attrValues (
filterAttrs (
name: value: (attrByPath ["config" "modules" "wirenix" "peerName"] null value) == peerName
) nixosConfigurations));
getKeyProviderFuncs = keyProvidersUninitialized: inputs: intermediateConfig: peerName:
@ -129,10 +138,10 @@ rec {
getProviderConfig = foldl' (x: y: x // y) {} (map (provider: if provider ? config then provider.config else {}) keyProviders);
};
mergeIf = attr: key: if builtins.hasAttr key attr then {"${key}" = attr."${key}";} else {};
asIp = cidr: head (filter (item: item != []) (split "/" cidr));
isIpv6 = ip: match ".*:.*" ip != null;
isCidr = cidr: match ".*/.*" cidr != null;
mergeIf = attr: key: if hasAttr key attr then {"${key}" = attr."${key}";} else {};
asIp = cidr: head (splitString "/" cidr);
isIpv6 = ip: builtins.match ".*:.*" ip != null;
isCidr = cidr: builtins.match ".*/.*" cidr != null;
asCidr' = ifv6: ifv4: ip: if (isCidr ip) then ip else if isIpv6 ip then ip+"/"+ifv6 else ip+"/"+ifv4;
asCidr = asCidr' "128" "32";
}

@ -4,11 +4,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{lib, ...}: v1_acl:
with lib.attrsets;
with lib.lists;
with lib.trivial;
with (import ../lib.nix);
with builtins;
let wnlib = import ../lib.nix {inherit lib;}; in
with wnlib;
with lib;
let
/** parsePeer :: acl_peer -> ic_peer */
parsePeer = acl_peer: {

@ -0,0 +1,104 @@
{
version = "v1";
subnets = [
{
name = "ring";
endpoints = [
{}
];
}
];
groups = [
# groups field is expected, but can be empty
];
peers = [
{
name = "peer1";
subnets = {
ring = {
listenPort = 51820;
# empty ipAddresses will auto generate an IPv6 address
};
};
publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE=";
privateKeyFile = "/etc/wg-key1";
endpoints = [
{
# no match can be any
ip = "node1";
port = 51820;
}
];
}
{
name = "peer2";
subnets = {
ring = {
listenPort = 51820;
};
};
publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE=";
privateKeyFile = "/etc/wg-key2";
endpoints = [
{
# no match can be any
ip = "node2";
port = 51820;
}
];
}
{
name = "peer3";
subnets = {
ring = {
listenPort = 51821;
# empty ipAddresses will auto generate an IPv6 address
};
};
publicKey = "43tP6JgckdTFrnbYuy8a42jdNt3+wwVcb4+ae5U4ez4=";
privateKeyFile = "/etc/wg-key3";
endpoints = [
{
# no match can be any
ip = "node1";
port = 51821;
}
];
}
{
name = "peer4";
subnets = {
ring = {
listenPort = 51821;
};
};
publicKey = "g6+Tq9aeVfm5CXPIwZDqoTxGmsQ/TlLtxcxVn2aSiVA=";
privateKeyFile = "/etc/wg-key4";
endpoints = [
{
# no match can be any
ip = "node2";
port = 51821;
}
];
}
];
connections = [
{
a = [{type= "peer"; rule = "is"; value = "peer1";}];
b = [{type= "peer"; rule = "is"; value = "peer2";}];
}
{
a = [{type= "peer"; rule = "is"; value = "peer2";}];
b = [{type= "peer"; rule = "is"; value = "peer3";}];
}
{
a = [{type= "peer"; rule = "is"; value = "peer3";}];
b = [{type= "peer"; rule = "is"; value = "peer4";}];
}
{
a = [{type= "peer"; rule = "is"; value = "peer4";}];
b = [{type= "peer"; rule = "is"; value = "peer1";}];
}
];
}

@ -0,0 +1,86 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix) ({wnlib}:
{
name = "double dev ring connection";
nodes = {
# `self` here is set by using specialArgs in `lib.nix`
node1 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
systemd.network.enable = true;
wirenix = {
configurer = "networkd";
devNameMethod = "hash";
enable = true;
aclConfig = import ./acls/double-dev-ring.nix;
peerNames = ["peer1" "peer3"];
};
environment.etc."wg-key1" = {
text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI=";
};
environment.etc."wg-key3" = {
text = "yPcTvQOK9eVXQjLNapOsv2iAkbOeSzCCxlrWPMe1o0g=";
};
environment.systemPackages = [pkgs.wireguard-tools];
networking.firewall.enable = false;
};
node2 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
systemd.network.enable = true;
wirenix = {
configurer = "networkd";
devNameMethod = "hash";
enable = true;
keyProviders = ["acl"];
aclConfig = import ./acls/double-dev-ring.nix;
peerNames = ["peer2" "peer4"];
};
environment.etc."wg-key2" = {
text = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
};
environment.etc."wg-key4" = {
text = "CLREBQ+oGXsGxhlQc3ufSoBd7MNFoM6KmMnNyuQ9S0E=";
};
environment.systemPackages = [pkgs.wireguard-tools];
networking.firewall.enable = false;
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
nodes = {
"peer1": node1,
"peer2": node2,
"peer3": node1,
"peer4": node2
}
ifaces = {
"peer1": "${wnlib.getDevName "hash" "peer1" "ring"}",
"peer2": "${wnlib.getDevName "hash" "peer2" "ring"}",
"peer3": "${wnlib.getDevName "hash" "peer3" "ring"}",
"peer4": "${wnlib.getDevName "hash" "peer4" "ring"}"
}
connections = {
"peer1": ["peer2", "peer4"],
"peer2": ["peer3", "peer1"],
"peer3": ["peer4", "peer2"],
"peer4": ["peer1", "peer3"]
}
node1.wait_for_unit("systemd-networkd-wait-online")
node2.wait_for_unit("systemd-networkd-wait-online")
node1.succeed("ping -c 3 node2 >&2")
node2.succeed("ping -c 3 node1 >&2")
for local_name, local_node in nodes.items():
for remote_name in set(nodes.keys()) - set([local_name]):
if remote_name in connections[local_name]:
local_node.succeed(f"ping -c 3 -I {ifaces[local_name]} {remote_name}.ring >&2")
else:
local_node.fail(f"ping -c 3 -W 1 -I {ifaces[local_name]} {remote_name}.ring")
'';
})

@ -3,7 +3,7 @@
# The first argument to this function is the test module itself
test:
# These arguments are provided by `flake.nix` on import, see checkArgs
{ pkgs, self}:
{ pkgs, self, wnlib }:
let
inherit (pkgs) lib;
# this imports the nixos library that contains our testing framework
@ -17,5 +17,5 @@ in
# This is useful for referencing modules or packages from your own flake
# as well as importing from other flakes.
node.specialArgs = { inherit self; };
imports = [ test ];
imports = [ (test {inherit wnlib;}) ];
}).config.result

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "manual ipv4 connection";
nodes = {
@ -50,4 +50,4 @@
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "explicit auto ipv6 connection";
nodes = {
@ -50,4 +50,4 @@
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "manual ipv6 connection";
nodes = {
@ -50,4 +50,4 @@
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "mesh connection";
nodes = {
@ -94,11 +94,9 @@
node2.wait_for_unit("wireguard-mesh.target")
node3.wait_for_unit("systemd-networkd-wait-online")
node4.wait_for_unit("systemd-networkd-wait-online")
for local_name, local_node in nodes.items():
local_node.succeed("wg showconf mesh >&2")
for local_name, local_node in nodes.items():
for remote_name in set(nodes.keys()) - set([local_name]):
local_node.succeed(f"ping -c 1 {remote_name} >&2")
local_node.succeed(f"ping -c 1 {remote_name}.mesh >&2")
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix) {
(import ./lib.nix) ({wnlib}: {
name = "null test";
nodes = {
# `self` here is set by using specialArgs in `lib.nix`
@ -19,4 +19,4 @@
# Check if our webserver returns the expected result
assert "Hello world" in output, f"'{output}' does not contain 'Hello world'"
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "ring connection";
nodes = {
@ -96,4 +96,4 @@
else:
local_node.fail(f"ping -c 1 -W 1 {remote_name}.ring")
'';
}
})

@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(import ./lib.nix)
(import ./lib.nix) ({wnlib}:
{
name = "simple connection";
nodes = {
@ -51,4 +51,4 @@
node1.succeed("ping -c 1 node2.simple")
node2.succeed("ping -c 1 node1.simple")
'';
}
})

@ -4,9 +4,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
{ config, lib, ... }@inputs:
let wnlib = import ./lib.nix {inherit lib;}; in
with wnlib;
with lib;
with import ./lib.nix;
with builtins;
let
cfg = config.wirenix;
parsers = defaultParsers // cfg.additionalParsers;
@ -14,10 +14,10 @@ let
availableKeyProviders = defaultKeyProviders // cfg.additionalKeyProviders;
acl = cfg.aclConfig;
parser = parsers."${acl.version}" inputs;
configurer = (getAttr cfg.configurer configurers) inputs; #config.wirenix.configurer inputs;
configurer = (getAttr cfg.configurer configurers) (inputs//{devNameMethod = cfg.devNameMethod;}); #config.wirenix.configurer inputs;
keyProviders = map (providerName: getAttr providerName availableKeyProviders) cfg.keyProviders; # config.wirenix.keyProviders;
mkMergeTopLevel = names: attrs: attrsets.getAttrs names (
mapAttrs (k: v: mkMerge v) (attrsets.foldAttrs (n: a: [n] ++ a) [] attrs)
mkMergeTopLevel = names: attrs: getAttrs names (
mapAttrs (k: v: mkMerge v) (foldAttrs (n: a: [n] ++ a) [] attrs)
);
/**
* We can merge if we want to
@ -26,7 +26,7 @@ let
* Well they're, no friends of mine.
*/
safetyMerge = possibleTopLevelKeys: attrs:
(mkMergeTopLevel possibleTopLevelKeys ((lists.singleton (attrsets.genAttrs possibleTopLevelKeys (name: {})))++attrs));
(mkMergeTopLevel possibleTopLevelKeys ((singleton (genAttrs possibleTopLevelKeys (name: {})))++attrs));
in
{
options = {
@ -35,15 +35,24 @@ in
wirenix = {
enable = mkEnableOption "wirenix";
peerName = mkOption {
default = config.networking.hostName;
defaultText = literalExpression "hostName";
default = config.system.name;
defaultText = literalExpression "config.system.name";
example = "bernd";
type = types.str;
type = with types; str;
description = mdDoc ''
Name of the peer using this module, to match the name in
`wirenix.config.peers.*.name`
'';
};
peerNames = mkOption {
default = null;
example = [ "container1" "container2" ];
type = with types; nullOr (listOf str);
description = mdDoc ''
When one host needs multiple devs for the same subnet, specify
multiple names manually. Overrides peerName.
'';
};
configurer = mkOption {
default = "static";
type = types.str;
@ -102,15 +111,33 @@ in
the same on all peers that need to connect to eachother
'';
};
devNameMethod = mkOption {
default = "short";
type = with types; strMatching "hash|long|short";
description = mdDoc ''
The method used to derive device names. Device names are limited to 15 characters,
but often times subnet names will exceed that. "hash" is the most reliable, and
will always create a name unique to the subnet and peer combination. "long" will
return the entire subnet, and "short" will return the beginning of the subnet up
until the first "." character.
'';
};
};
};
# --------------------------------------------------------------- #
# Due to merge weirdness, I have to define what configuration keys
# we're touching upfront, and make sure they exist
config = mkIf cfg.enable (safetyMerge ["networking" "sops" "age" "systemd" "services" "environment"]
[
(configurer keyProviders (parser acl) cfg.peerName)
]
);
config =
mkIf cfg.enable (safetyMerge ["networking" "sops" "age" "systemd" "services" "environment"] (
if builtins.typeOf cfg.peerNames == "null" then (
[(configurer keyProviders (parser acl) cfg.peerName)]
)
else (
warnIf (cfg.devNameMethod != "hash") "Wirenix: Using multiple peerNames for devNameMethod = \"${cfg.devNameMethod}\" can (will) cause device name collisions. Please use devNameMethod = \"hash\" instead" (
warnIf (cfg.configurer == "static") "Wirenix: static configurer not supported with multiple peerNames. Please use networkd or networkd-dev-only instead." (
(map (name: (configurer keyProviders (parser acl) name)) cfg.peerNames)
))
)
));
}
Loading…
Cancel
Save