Added manual IP tests and resulting fixes

release
Matthew Salerno 1 year ago
parent f78dc06eb1
commit 0dea96cf37

@ -19,8 +19,8 @@ let
in in
with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName; with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
{ {
networking.extraHosts = concatStringsSep "\n" (concatLists ( concatLists (forEachAttrToList thisPeer.subnetConnections (subnetName: subnetConnection: networking.hosts = foldl' (mergeAttrs) {} (concatLists ( concatLists (forEachAttrToList thisPeer.subnetConnections (subnetName: subnetConnection:
forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: "${cidr2ip ip} ${remotePeerName}.${subnetName}")) forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: {"${asIp ip}" = ["${remotePeerName}.${subnetName}"];}))
)))); ))));
systemd.network = { systemd.network = {
netdevs = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" { netdevs = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" {
@ -30,14 +30,13 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
}; };
wireguardConfig = { wireguardConfig = {
ListenPort = subnetConnection.listenPort; ListenPort = subnetConnection.listenPort;
# *PLEASE* do not use getPrivKeyfor anything but testing
PrivateKeyFile = getPrivKeyFile; PrivateKeyFile = getPrivKeyFile;
}; };
wireguardPeers = forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: { wireguardPeers = forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: {
wireguardPeerConfig = { wireguardPeerConfig = {
Endpoint = "${peerConnection.endpoint.ip}:${builtins.toString peerConnection.endpoint.port}"; Endpoint = "${peerConnection.endpoint.ip}:${builtins.toString peerConnection.endpoint.port}";
PublicKey = getPeerPubKey remotePeerName; PublicKey = getPeerPubKey remotePeerName;
AllowedIPs = map (ip: cidr2ip ip + (if match ".*:.*" ip != null then "/128" else "/32")) peerConnection.ipAddresses; AllowedIPs = map (ip: asCidr ip) peerConnection.ipAddresses;
PresharedKeyFile = getSubnetPSKFile subnetName; PresharedKeyFile = getSubnetPSKFile subnetName;
}; };
} }
@ -48,7 +47,7 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
}); });
networks = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" { networks = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "50-${shortName subnetName}" {
matchConfig.Name = "${shortName subnetName}"; matchConfig.Name = "${shortName subnetName}";
address = subnetConnection.ipAddresses; address = map (address: (asCidr' "64" "24" address)) subnetConnection.ipAddresses;
}); });
}; };
} // getProviderConfig } // getProviderConfig

@ -18,13 +18,13 @@ let
in in
with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName; with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
{ {
networking.extraHosts = concatStringsSep "\n" (concatLists ( concatLists (forEachAttrToList thisPeer.subnetConnections (subnetName: subnetConnection: networking.hosts = foldl' (mergeAttrs) {} (concatLists ( concatLists (forEachAttrToList thisPeer.subnetConnections (subnetName: subnetConnection:
forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: "${cidr2ip ip} ${remotePeerName}.${subnetName}")) forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: forEach peerConnection.ipAddresses (ip: {"${asIp ip}" = ["${remotePeerName}.${subnetName}"];}))
)))); ))));
networking.wireguard = { networking.wireguard = {
interfaces = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "${head (strings.splitString "." subnetName)}" interfaces = forEachAttr' thisPeer.subnetConnections (subnetName: subnetConnection: nameValuePair "${head (strings.splitString "." subnetName)}"
{ {
ips = subnetConnection.ipAddresses; ips = map (address: (asCidr' "64" "24" address)) subnetConnection.ipAddresses;
listenPort = subnetConnection.listenPort; listenPort = subnetConnection.listenPort;
privateKeyFile = getPrivKeyFile; privateKeyFile = getPrivKeyFile;
peers = forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection: peers = forEachAttrToList subnetConnection.peerConnections (remotePeerName: peerConnection:
@ -32,7 +32,7 @@ with getKeyProviderFuncs keyProviders inputs intermediateConfig localPeerName;
name = remotePeerName; name = remotePeerName;
publicKey = getPeerPubKey remotePeerName; publicKey = getPeerPubKey remotePeerName;
presharedKeyFile = getSubnetPSKFile subnetName; presharedKeyFile = getSubnetPSKFile subnetName;
allowedIPs = map ( ip: cidr2ip ip + (if match ".*:.*" ip != null then "/128" else "/32")) peerConnection.ipAddresses; allowedIPs = map ( ip: asCidr ip) peerConnection.ipAddresses;
endpoint = "${peerConnection.endpoint.ip}:${builtins.toString peerConnection.endpoint.port}"; endpoint = "${peerConnection.endpoint.ip}:${builtins.toString peerConnection.endpoint.port}";
} }
// (mergeIf peerConnection.endpoint "persistentKeepalive") // (mergeIf peerConnection.endpoint "persistentKeepalive")

@ -26,6 +26,9 @@
simple = import ./tests/simple.nix checkArgs; simple = import ./tests/simple.nix checkArgs;
mesh = import ./tests/mesh.nix checkArgs; mesh = import ./tests/mesh.nix checkArgs;
ring = import ./tests/ring.nix checkArgs; ring = import ./tests/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;
}); });
}; };
} }

@ -85,7 +85,10 @@ rec {
generateIPv6Subnet = subnetName: (addColonsToIPv6 (generateIPv6Prefix subnetName)) + "::/64"; generateIPv6Subnet = subnetName: (addColonsToIPv6 (generateIPv6Prefix subnetName)) + "::/64";
/** generates a full IPv6 address */ /** generates a full IPv6 address */
generateIPv6Address = subnetName: peerName: (addColonsToIPv6 ((generateIPv6Prefix subnetName) + (generateIPv6Suffix peerName))) + "/64"; generateIPv6Address = subnetName: peerName: (addColonsToIPv6 ((generateIPv6Prefix subnetName) + (generateIPv6Suffix peerName)));
/** generates a full IPv6 address with cidr */
generateIPv6Cidr = subnetName: peerName: (addColonsToIPv6 ((generateIPv6Prefix subnetName) + (generateIPv6Suffix peerName))) + "/64";
/** /**
* makes the intermediate config non-recursive, so it can be pretty printed and * makes the intermediate config non-recursive, so it can be pretty printed and
@ -127,5 +130,9 @@ rec {
}; };
mergeIf = attr: key: if builtins.hasAttr key attr then {"${key}" = attr."${key}";} else {}; mergeIf = attr: key: if builtins.hasAttr key attr then {"${key}" = attr."${key}";} else {};
cidr2ip = cidr: head (filter (item: item != []) (split "/" cidr)); asIp = cidr: head (filter (item: item != []) (split "/" cidr));
isIpv6 = ip: match ".*:.*" ip != null;
isCidr = cidr: 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";
} }

@ -0,0 +1,65 @@
{
version = "v1";
subnets = [
{
name = "manual";
endpoints = [
{
# No match mean match any
port = 51820;
}
];
}
];
groups = [
# groups field is expected, but can be empty
];
peers = [
{
name = "node1";
subnets = {
manual = {
ipAddresses = [
"10.0.0.1"
];
listenPort = 51820;
};
};
publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE=";
privateKeyFile = "/etc/wg-key";
#privateKey = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI="; # path is relative to the machine
endpoints = [
{
# no match can be any
ip = "node1";
}
];
}
{
name = "node2";
subnets = {
manual = {
ipAddresses = [
"10.0.0.2"
];
listenPort = 51820;
};
};
publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE=";
privateKeyFile = "/etc/wg-key";
#privateKey = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
endpoints = [
{
# no match can be any
ip = "node2";
}
];
}
];
connections = [
{
a = [{type= "subnet"; rule = "is"; value = "manual";}];
b = [{type= "subnet"; rule = "is"; value = "manual";}];
}
];
}

@ -0,0 +1,63 @@
{
version = "v1";
subnets = [
{
name = "manual";
endpoints = [
{
# No match mean match any
port = 51820;
}
];
}
];
groups = [
# groups field is expected, but can be empty
];
peers = [
{
name = "node1";
subnets = {
manual = {
ipAddresses = [
"auto" # "auto" explicitly generates an ipv6 address, opposed to implicitly via not having an `ipAddresses` property
];
listenPort = 51820;
};
};
publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE=";
privateKeyFile = "/etc/wg-key";
endpoints = [
{
# no match can be any
ip = "node1";
}
];
}
{
name = "node2";
subnets = {
manual = {
ipAddresses = [
"auto"
];
listenPort = 51820;
};
};
publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE=";
privateKeyFile = "/etc/wg-key";
endpoints = [
{
# no match can be any
ip = "node2";
}
];
}
];
connections = [
{
a = [{type= "subnet"; rule = "is"; value = "manual";}];
b = [{type= "subnet"; rule = "is"; value = "manual";}];
}
];
}

@ -0,0 +1,63 @@
{
version = "v1";
subnets = [
{
name = "manual";
endpoints = [
{
# No match mean match any
port = 51820;
}
];
}
];
groups = [
# groups field is expected, but can be empty
];
peers = [
{
name = "node1";
subnets = {
manual = {
ipAddresses = [
"fc00::1"
];
listenPort = 51820;
};
};
publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE=";
privateKeyFile = "/etc/wg-key";
endpoints = [
{
# no match can be any
ip = "node1";
}
];
}
{
name = "node2";
subnets = {
manual = {
ipAddresses = [
"fc00::2"
];
listenPort = 51820;
};
};
publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE=";
privateKeyFile = "/etc/wg-key";
endpoints = [
{
# no match can be any
ip = "node2";
}
];
}
];
connections = [
{
a = [{type= "subnet"; rule = "is"; value = "manual";}];
b = [{type= "subnet"; rule = "is"; value = "manual";}];
}
];
}

@ -25,7 +25,6 @@
}; };
publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE="; publicKey = "kdyzqV8cBQtDYeW6R1vUug0Oe+KaytHHDS7JoCp/kTE=";
privateKeyFile = "/etc/wg-key"; privateKeyFile = "/etc/wg-key";
#privateKey = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI="; # path is relative to the machine
endpoints = [ endpoints = [
{ {
# no match can be any # no match can be any
@ -42,7 +41,6 @@
}; };
publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE="; publicKey = "ztdAXTspQEZUNpxUbUdAhhRWbiL3YYWKSK0ZGdcsMHE=";
privateKeyFile = "/etc/wg-key"; privateKeyFile = "/etc/wg-key";
#privateKey = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
endpoints = [ endpoints = [
{ {
# no match can be any # no match can be any

@ -0,0 +1,53 @@
/*
* 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)
{
name = "manual ipv4 connection";
nodes = {
# `self` here is set by using specialArgs in `lib.nix`
node1 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node1";
aclConfig = import ./acls/manual-ipv4.nix;
};
environment.etc."wg-key" = {
text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI=";
};
networking.firewall.enable = false;
};
node2 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node2";
aclConfig = import ./acls/manual-ipv4.nix;
};
environment.etc."wg-key" = {
text = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
};
networking.firewall.enable = false;
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
node1.wait_for_unit("wireguard-manual-peer-node2")
node2.wait_for_unit("wireguard-manual-peer-node1")
node1.succeed("ping -c 1 node2 >&2")
node1.succeed("wg show >&2")
node2.succeed("ping -c 1 node1 >&2")
node2.succeed("wg show >&2")
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}

@ -0,0 +1,53 @@
/*
* 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)
{
name = "explicit auto ipv6 connection";
nodes = {
# `self` here is set by using specialArgs in `lib.nix`
node1 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node1";
aclConfig = import ./acls/manual-ipv6-auto.nix;
};
environment.etc."wg-key" = {
text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI=";
};
networking.firewall.enable = false;
};
node2 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node2";
aclConfig = import ./acls/manual-ipv6-auto.nix;
};
environment.etc."wg-key" = {
text = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
};
networking.firewall.enable = false;
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
node1.wait_for_unit("wireguard-manual-peer-node2")
node2.wait_for_unit("wireguard-manual-peer-node1")
node1.succeed("ping -c 1 node2 >&2")
node1.succeed("wg show >&2")
node2.succeed("ping -c 1 node1 >&2")
node2.succeed("wg show >&2")
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}

@ -0,0 +1,53 @@
/*
* 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)
{
name = "manual ipv6 connection";
nodes = {
# `self` here is set by using specialArgs in `lib.nix`
node1 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node1";
aclConfig = import ./acls/manual-ipv6.nix;
};
environment.etc."wg-key" = {
text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI=";
};
networking.firewall.enable = false;
};
node2 = { self, pkgs, ... }: {
virtualisation.vlans = [ 1 ];
imports = [ self.nixosModules.default ];
wirenix = {
enable = true;
keyProviders = ["acl"];
peerName = "node2";
aclConfig = import ./acls/manual-ipv6.nix;
};
environment.etc."wg-key" = {
text = "yG4mJiduoAvzhUJMslRbZwOp1gowSfC+wgY8B/Mul1M=";
};
networking.firewall.enable = false;
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
node1.wait_for_unit("wireguard-manual-peer-node2")
node2.wait_for_unit("wireguard-manual-peer-node1")
node1.succeed("ping -c 1 node2 >&2")
node1.succeed("wg show >&2")
node2.succeed("ping -c 1 node1 >&2")
node2.succeed("wg show >&2")
node1.succeed("ping -c 1 node2.manual")
node2.succeed("ping -c 1 node1.manual")
'';
}

@ -90,10 +90,13 @@
if local_name == "node1" or local_name == "node2": if local_name == "node1" or local_name == "node2":
for remote_node in set(nodes.keys()) - set([local_name]): for remote_node in set(nodes.keys()) - set([local_name]):
local_node.wait_for_unit(f"wireguard-mesh-peer-{remote_node}") local_node.wait_for_unit(f"wireguard-mesh-peer-{remote_node}")
node1.wait_for_unit("wireguard-mesh.target")
node2.wait_for_unit("wireguard-mesh.target")
node3.wait_for_unit("systemd-networkd-wait-online") node3.wait_for_unit("systemd-networkd-wait-online")
node4.wait_for_unit("systemd-networkd-wait-online") node4.wait_for_unit("systemd-networkd-wait-online")
for local_name, local_node in nodes.items(): for local_name, local_node in nodes.items():
local_node.succeed("wg show >&2") 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]): 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} >&2")
local_node.succeed(f"ping -c 1 {remote_name}.mesh >&2") local_node.succeed(f"ping -c 1 {remote_name}.mesh >&2")

@ -83,8 +83,12 @@
for local_name, local_node in nodes.items(): for local_name, local_node in nodes.items():
for remote_name in connections[local_name]: for remote_name in connections[local_name]:
local_node.wait_for_unit(f"wireguard-ring-peer-{remote_name}") local_node.wait_for_unit(f"wireguard-ring-peer-{remote_name}")
node1.wait_for_unit("wireguard-ring.target")
node2.wait_for_unit("wireguard-ring.target")
node3.wait_for_unit("wireguard-ring.target")
node4.wait_for_unit("wireguard-ring.target")
for local_name, local_node in nodes.items(): for local_name, local_node in nodes.items():
local_node.succeed("wg show >&2") local_node.succeed("wg showconf ring >&2")
for remote_name in set(nodes.keys()) - set([local_name]): 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} >&2")
if remote_name in connections[local_name]: if remote_name in connections[local_name]:

@ -17,6 +17,7 @@
peerName = "node1"; peerName = "node1";
aclConfig = import ./acls/simple.nix; aclConfig = import ./acls/simple.nix;
}; };
# Don't do this! This is for testing only!
environment.etc."wg-key" = { environment.etc."wg-key" = {
text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI="; text = "MIELhEc0I7BseAanhk/+LlY/+Yf7GK232vKWITExnEI=";
}; };

Loading…
Cancel
Save