{ config, hostName, lib, pkgs, ... }: with lib; let listenPort = 51820; wgSubnet = "2a03:4000:52:ada:1"; wgSubnetLength = 80; wgHostLength = wgSubnetLength + 16; batSubnet = "2a03:4000:52:ada:2"; batSubnetLength = 80; batHostLength = batSubnetLength + 16; links = mkLinks [ { from = "vidhar"; to = "surtr"; endpointHost = "202.61.241.61"; PersistentKeepalive = 25; } { from = "sif"; to = "surtr"; endpointHost = "202.61.241.61"; PersistentKeepalive = 25; } { from = "sif"; to = "vidhar"; endpointHost = "192.168.2.168"; PersistentKeepalive = 25; } ]; wgHostIPs = { surtr = "${wgSubnet}::/${toString wgHostLength}"; vidhar = "${wgSubnet}:1::/${toString wgHostLength}"; sif = "${wgSubnet}:2::/${toString wgHostLength}"; }; greHostMACPrefixes = { surtr = "02:00:01:00:00"; vidhar = "02:00:01:00:01"; sif = "02:00:01:00:02"; }; batHostMACs = { surtr = "02:00:00:00:00:00"; vidhar = "02:00:00:01:00:00"; sif = "02:00:00:02:00:00"; }; batHostIPs = { surtr = ["${batSubnet}::/${toString batHostLength}"]; vidhar = ["${batSubnet}:1::/${toString batHostLength}"]; sif = ["${batSubnet}:2::/${toString batHostLength}"]; }; mkPublicKeyPath = host: ./hosts + "/${host}.pub"; mkPrivateKeyPath = host: ./hosts + "/${host}.priv"; kernel = config.boot.kernelPackages; publicKeyPath = mkPublicKeyPath hostName; privateKeyPath = mkPrivateKeyPath hostName; inNetwork = pathExists privateKeyPath && pathExists publicKeyPath; hostLinks = filter ({ from, to, ... }: from == hostName || to == hostName) links; linkToPeer = opts@{from, to, ...}: let other = if from == hostName then to else from; in { AllowedIPs = wgHostIPs.${other}; PublicKey = trim (readFile (mkPublicKeyPath other)); } // (optionalAttrs (from == hostName) (linkCfgFilterCustom opts // linkMkEndpointCfg opts)); linkCfgFilterCustom = filterAttrs (n: _v: !(elem n ["from" "to" "endpointHost"])); linkMkEndpointCfg = opts@{from, ...}: optionalAttrs (opts ? "endpointHost" && from == hostName) { Endpoint = "${opts.endpointHost}:${toString listenPort}"; }; linkToGreDev = opts@{from, to, ...}: let other = if from == hostName then to else from; in nameValuePair "yggre-${other}" { netdevConfig = { Name = "yggre-${other}"; Kind = "ip6gretap"; }; tunnelConfig = { Local = stripSubnet wgHostIPs.${hostName}; Remote = stripSubnet wgHostIPs.${other}; }; }; linkToGreNetwork = ix: opts@{from, to, ...}: let other = if from == hostName then to else from; in nameValuePair "yggre-${other}" { matchConfig = { Name = "yggre-${other}"; }; linkConfig = { MACAddress = "${greHostMACPrefixes.${hostName}}:${toHexByte ix}"; RequiredForOnline = false; }; networkConfig = { BatmanAdvanced = "yggdrasil"; LinkLocalAddressing = "no"; }; }; trim = str: if hasSuffix "\n" str then trim (removeSuffix "\n" str) else str; stripSubnet = addr: let matchRes = builtins.match "^(.*)/[0-9]+$" addr; in if matchRes == null then addr else elemAt matchRes 0; optIx = optName: xs: let withOpts = listToAttrs (imap0 (ix: x: nameValuePair x.name (x.value // { ${optName} = ix; })) (filter (x: x.value.${optName} or false) (imap0 (ix: nameValuePair (toString ix)) xs))); withoutOpts = listToAttrs (map (nv: nameValuePair nv.name (removeAttrs nv.value [optName])) (filter (x: !(x.value.${optName} or false)) (imap0 (ix: nameValuePair (toString ix)) xs))); in genList (ix: withOpts.${toString ix} or withoutOpts.${toString ix}) (length xs); mkLinks = id; toHexByte = n: let hex = toHexString n; in if (stringLength hex < 2) then "0${hex}" else hex; in { config = { assertions = [ { assertion = inNetwork || !(pathExists privateKeyPath || pathExists publicKeyPath); message = "yggdrasil-wg: Either both public and private keys must exist or neither."; } { assertion = !inNetwork || (wgHostIPs ? "${hostName}"); message = "yggdrasil-wg: Entry in wgHostIPs must exist."; } ] ++ map ({from, to, ...}: let other = if from == hostName then to else from; in { assertion = pathExists (mkPublicKeyPath other); message = "yggdrasil-wg: This host (${hostName}) has a link with ‘${other}’, but no public key is available for ‘${other}’."; }) hostLinks; systemd.network = mkIf inNetwork { enable = true; netdevs = { yggdrasil-wg = { netdevConfig = { Name = "yggdrasil-wg"; Kind = "wireguard"; }; wireguardConfig = { PrivateKeyFile = config.sops.secrets."yggdrasil-wg.priv".path; ListenPort = listenPort; }; wireguardPeers = map (opts@{to, from, ...}: { wireguardPeerConfig = linkToPeer opts; }) hostLinks; }; yggdrasil = { netdevConfig = { Name = "yggdrasil"; Kind = "batadv"; }; }; } // listToAttrs (map linkToGreDev hostLinks); networks = { yggdrasil-wg = { name = "yggdrasil-wg"; matchConfig = { Name = "yggdrasil-wg"; }; address = [wgHostIPs.${hostName}]; routes = [ { routeConfig = { Destination = "${wgSubnet}::/${toString wgSubnetLength}"; }; } ]; linkConfig = { RequiredForOnline = false; }; networkConfig = { Tunnel = map (opts@{from, to, ...}: let other = if from == hostName then to else from; in "yggre-${other}") hostLinks; }; }; yggdrasil = { name = "yggdrasil"; matchConfig = { Name = "yggdrasil"; }; address = batHostIPs.${hostName}; routes = [ { routeConfig = { Destination = "${batSubnet}::/${toString batSubnetLength}"; }; } ]; linkConfig = { MACAddress = "${batHostMACs.${hostName}}"; RequiredForOnline = false; }; }; } // listToAttrs (imap0 linkToGreNetwork hostLinks); }; sops.secrets = { "yggdrasil-wg.priv" = mkIf (pathExists privateKeyPath) { format = "binary"; sopsFile = privateKeyPath; mode = "0640"; owner = "root"; group = "systemd-network"; }; }; networking.hosts = mkIf inNetwork (listToAttrs (concatMap ({name, value}: map (ip: nameValuePair (stripSubnet ip) ["${name}.yggdrasil"]) value) (mapAttrsToList nameValuePair batHostIPs))); boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard ++ [kernel.batman_adv]; environment.systemPackages = with pkgs; [ wireguard-tools batctl ]; }; }