meshing fixed

This commit is contained in:
asyncnomi 2025-07-21 23:41:18 +02:00
parent c2ec3c672b
commit 395a34d811
4 changed files with 113 additions and 75 deletions

View file

@ -1,8 +1,14 @@
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
let
# Import nodes
nodes = import ./../../nodes.nix;
myPeer = nodes."${config.hostName}";
myId = myPeer.id;
myZone = myPeer.zone;
# And mappings
mapping = import ./../../mapping.nix;
buildSecret = zone: id: {
"wg-private-zone-${toString zone}-id-${toString id}" = {
@ -13,48 +19,69 @@ let
};
generatedSecrets = lib.mapAttrsToList (name: node: buildSecret node.zone node.id) nodes;
generateWireGuardInterfaces = nodesConfig: let
myPeer = nodesConfig."${config.hostName}";
myZone = myPeer.zone;
myId = myPeer.id;
# Filter itself out of the peer list
peerConfigs = lib.filterAttrs (_peerName: peerConfig: peerConfig.id != myId) nodesConfig;
# We'll make one if per peer, this is more flexible
interfacePeers = lib.flatten (lib.mapAttrsToList (peerName: peerConfig: let
remoteId = peerConfig.id;
shorten = peerName: let
parts = lib.splitString "-" peerName;
shortened = lib.concatStrings (map (part: lib.substring 0 1 part) parts);
in shortened;
# The mesh is for now only IPv4 based
if4 = {
"mesh-${peerName}" = {
ips = [
"172.19.${toString remoteId}.${toString myId}/32"
"fc00::${toString remoteId}:${toString myId}/128"
# Filter itself out of the peer list
peerConfigs = lib.filterAttrs (_peerName: peerConfig: peerConfig.id != myId) nodes;
# We'll make one if per peer, this is more flexible
interfacePeers = lib.flatten (lib.mapAttrsToList (peerName: peerConfig: let
remoteId = peerConfig.id;
remoteZone = peerConfig.zone;
# The mesh is for now only IPv4 based
if4 = {
"mesh-${shorten peerName}-${toString remoteZone}-${toString remoteId}" = {
ips = [
"172.19.${toString remoteId}.${toString myId}/32"
"fc00::${toString remoteId}:${toString myId}/128"
];
privateKeyFile = config.age.secrets."wg-private-zone-${toString myZone}-id-${toString myId}".path;
listenPort = 51000 + remoteId;
peers = [{
name = "${peerName}-ip4";
publicKey = peerConfig.wg-pub;
allowedIPs = [
"172.19.${toString myId}.${toString remoteId}/32"
"fc00::${toString myId}:${toString remoteId}/128"
# Allow mgmt transport
"172.19.128.0/17"
"fc00:f::/96"
];
privateKeyFile = config.age.secrets."wg-private-zone-${toString myZone}-id-${toString myId}".path;
listenPort = 51000 + remoteId;
peers = [{
name = "${peerName}-ip4";
publicKey = peerConfig.wg-pub;
allowedIPs = [
"172.19.${toString myId}.${toString remoteId}/32"
"fc00::${toString myId}:${toString remoteId}/128"
# Allow mgmt transport
"172.19.128.0/17"
"fc00:f::/96"
];
persistentKeepalive = 25;
}];
};
persistentKeepalive = 25;
}];
# Throw away route created by wireguard
table = "off";
};
};
in if4) peerConfigs);
in if4) peerConfigs);
interfaces = builtins.foldl' (acc: set: acc // set) {} interfacePeers;
in interfaces;
wireguardInterfaces = builtins.foldl' (acc: set: acc // set) {} interfacePeers;
wireguardInterfaces = generateWireGuardInterfaces nodes;
generateRoute = peerName: peerConfig: ''
# Static route to declare static wireguard peer inner tunnel ip (that has been thrown away to the "off" table)
${pkgs.iproute2}/bin/ip route replace 172.19.${toString myId}.${toString peerConfig.id} dev mesh-${shorten peerName}-${toString peerConfig.zone}-${toString peerConfig.id} scope link
${pkgs.iproute2}/bin/ip -6 route replace fc00::${toString myId}:${toString peerConfig.id} dev mesh-${shorten peerName}-${toString peerConfig.zone}-${toString peerConfig.id} scope link
# Return path for mgmt trafic
${if !(lib.elem myPeer mapping.bastion) && (lib.elem peerName mapping.bastion) then ''
${pkgs.iproute2}/bin/ip route replace 172.19.${toString (peerConfig.zone + 127)}.0/24 via 172.19.${toString myPeer.id}.${toString peerConfig.id} dev mesh-${shorten peerName}-${toString peerConfig.zone}-${toString peerConfig.id}
${pkgs.iproute2}/bin/ip -6 route replace fc00:f::${toString (peerConfig.zone + 127)}:0/96 via fc00::${toString myPeer.id}:${toString peerConfig.id} dev mesh-${shorten peerName}-${toString peerConfig.zone}-${toString peerConfig.id}
'' else ""}
'';
generateAfter = peerName: peerConfig: "wireguard-mesh-${shorten peerName}-${toString peerConfig.zone}-${toString peerConfig.id}.target";
routes = lib.mapAttrsToList (name: peer: generateRoute name peer) peerConfigs;
afters = lib.mapAttrsToList (name: peer: generateAfter name peer) peerConfigs;
wireguardStaticRoute = pkgs.writeShellScriptBin "wireguardStaticRoute" ''
${lib.concatStrings routes}
'';
in
{
age.secrets = lib.lists.foldl' (acc: set: lib.attrsets.recursiveUpdate acc set) {} generatedSecrets;
@ -62,17 +89,27 @@ in
# Networkd backend introduce in 25.05
# No independant target are generated
# when using networkd as a backend
# If custom systemd ordering is needed
# between wg interface and the rest of
# networking: switch to false here
# Note: if you turn it off it will break
# the custom routing for mgmt rt path
# trafic handle by networkd, see networking.nix
networking.wireguard.useNetworkd = true;
# We need them, see below
networking.wireguard.useNetworkd = false;
# Return all WireGuard interfaces for each node
networking.wireguard.interfaces = wireguardInterfaces;
# Execute custom routing for wireguard
systemd.services.wireguardStaticRouting = {
wantedBy = [ "multi-user.target" ];
# Thus we need specific target to be reached to run it
after = afters;
description = "Add custom ip route for wireguard.";
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${wireguardStaticRoute}/bin/wireguardStaticRoute";
};
};
# Open UDP port for wireguard traffic
networking.firewall.allowedUDPPorts = lib.range 51000 52000;
networking.firewall.allowedUDPPorts = lib.range 51000 (51000 + builtins.head (
builtins.sort (a: b: a > b) (
lib.mapAttrsToList (name: node: node.id) nodes)));
}

View file

@ -15,19 +15,6 @@ let
# And routes, the gateway is assumed to be in subnet, otherwise GatewayOnLink is required
route4 = if supportsIPv4 myNode then [{ Gateway = myNode.gIp4; }] else [];
route6 = if supportsIPv6 myNode then [{ Gateway = myNode.gIp6; }] else [];
# Return route for mgmt traffic
rtwg4 = if myNode.id == 1 then [] else
map (node: {
Gateway = "172.19.${toString node.zone}.1";
Destination = "172.19.${toString (node.zone + 127)}.0/24";
}) (lib.attrValues (lib.filterAttrs (name: node: node.id == 1) nodes));
rtwg6 = if myNode.id == 1 then [] else
map (node: {
Gateway = "fc00::${toString node.zone}:1";
Destination = "fc00:f::${toString (node.zone + 127)}:0/96";
}) (lib.attrValues (lib.filterAttrs (name: node: node.id == 1) nodes));
in
{
networking.hostName = config.hostName;
@ -47,11 +34,6 @@ in
# make the routes on this interface a dependency for network-online.target
linkConfig.RequiredForOnline = "routable";
};
# This interface is generated by networkd backend for wireguard
# See mesh.nix
"mesh" = {
routes = rtwg4 ++ rtwg6;
};
};
config.addRouteTablesToIPRoute2 = true;