From 3862bd6b147f64ab9bb49b90e77a71525a41d30a Mon Sep 17 00:00:00 2001 From: oddlama Date: Sat, 20 May 2023 18:24:30 +0200 Subject: [PATCH] feat: remove the need to specify cidrs in wireguard addresses and properly derive allowed ips --- README.md | 2 +- hosts/common/core/system.nix | 26 ++++++++++--- modules/microvms.nix | 9 ++--- modules/wireguard.nix | 75 ++++++++++++++++-------------------- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index b212e30..f2842d7 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This is my personal nix config. - `envoy/` - Hetzner Cloud server. Primarily used as my mailserver and VPN provider. - `zackbiene/` - ODROID N2+. Hosts IoT and Home Automation stuff and fully isolates that stuff from my internal network. - not yet ready to be publicized: my main development machine, the powerful home server, some services ... (still in transition from gentoo :/) -- `modules/` additional NixOS modules that are not yet upstreamed. +- `modules/` additional NixOS modules that are not yet upstreamed, or specific to this setup. - `nix/` library functions and plumbing - `apps/` Additional runnable actions for this flake - `default.nix` Collects all apps and generates a definition for a specified system diff --git a/hosts/common/core/system.nix b/hosts/common/core/system.nix index 5901f36..14bb5b6 100644 --- a/hosts/common/core/system.nix +++ b/hosts/common/core/system.nix @@ -67,15 +67,22 @@ # > net.cidr.canonicalize "192.168.1.100/24" # "192.168.1.0/24" canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x); - # coercev4 :: [cidr4] -> (cidr4 | null) + # coercev4 :: [cidr4 | ipv4] -> (cidr4 | null) # - # Returns the smallest cidr network that includes all given addresses + # Returns the smallest cidr network that includes all given addresses. + # If no cidr mask is given, /32 is assumed. # # Examples: # # > net.cidr.coercev4 ["192.168.1.1/24" "192.168.6.1/32"] # "192.168.0.0/21" - coercev4 = addrs: let + coercev4 = addrs_: let + # Append /32 if necessary + addrs = map (x: + if lib.hasInfix "/" x + then x + else "${x}/32") + addrs_; # The smallest occurring length is the first we need to start checking, since # any greater cidr length represents a smaller address range which # wouldn't contain all of the original addresses. @@ -101,15 +108,22 @@ if addrs == [] then null else libWithNet.net.cidr.make bestLength firstIp; - # coercev6 :: [cidr6] -> (cidr6 | null) + # coercev6 :: [cidr6 | ipv6] -> (cidr6 | null) # - # Returns the smallest cidr network that includes all given addresses + # Returns the smallest cidr network that includes all given addresses. + # If no cidr mask is given, /128 is assumed. # # Examples: # # > net.cidr.coercev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"] # "fd00:c000::/18" - coercev6 = addrs: let + coercev6 = addrs_: let + # Append /128 if necessary + addrs = map (x: + if lib.hasInfix "/" x + then x + else "${x}/128") + addrs_; # The smallest occurring length is the first we need to start checking, since # any greater cidr length represents a smaller address range which # wouldn't contain all of the original addresses. diff --git a/modules/microvms.nix b/modules/microvms.nix index c414591..6536ef0 100644 --- a/modules/microvms.nix +++ b/modules/microvms.nix @@ -193,9 +193,8 @@ via = nodeName; keepalive = false; }; - cidrv4 = "${net.cidr.host vmCfg.id cfg.networking.wireguard.cidrv4}/32"; - cidrv6 = "${net.cidr.host vmCfg.id cfg.networking.wireguard.cidrv6}/128"; - # TODO check error: addresses = ["10.22.22.2/30"]; + ipv4 = net.cidr.host vmCfg.id cfg.networking.wireguard.cidrv4; + ipv6 = net.cidr.host vmCfg.id cfg.networking.wireguard.cidrv6; }; }; }; @@ -402,8 +401,8 @@ in { inherit (cfg.networking) host; inherit (cfg.networking.wireguard) openFirewallRules port; }; - cidrv4 = net.cidr.hostCidr 1 cfg.networking.wireguard.cidrv4; - cidrv6 = net.cidr.hostCidr 1 cfg.networking.wireguard.cidrv6; + ipv4 = net.cidr.host 1 cfg.networking.wireguard.cidrv4; + ipv6 = net.cidr.host 1 cfg.networking.wireguard.cidrv6; }; } // extraLib.mergeToplevelConfigs ["disko" "microvm" "systemd"] (mapAttrsToList microvmConfig vms) diff --git a/modules/wireguard.nix b/modules/wireguard.nix index 0c4ecf8..52f33d1 100644 --- a/modules/wireguard.nix +++ b/modules/wireguard.nix @@ -86,6 +86,23 @@ # Adds context information to the assertions for this network assertionPrefix = "Wireguard network '${wgName}' on '${nodeName}'"; + + # Calculates which traffic should be routed to a given server node + # Usually we just want to allow other peers to route traffic + # for our "children" through us, additional to traffic to us of course. + # If a server exposes additional network access (global, lan, ...), + # these can be added aswell. TODO (do that) + serverAllowedIPs = serverNode: let + snCfg = wgCfgOf serverNode; + in + map (net.cidr.make 128) ( + # The server accepts traffic to it's own address + snCfg.addresses + # plus traffic for any of its external peers + ++ attrValues snCfg.server.externalPeers + # plus traffic for any client that is connected via that server + ++ map (n: (wgCfgOf n).addresses) (filter (n: (wgCfgOf n).client.via == serverNode) associatedClientNodes) + ); in { assertions = [ { @@ -157,16 +174,13 @@ if isServer then # Always include all other server nodes. - map (serverNode: { - wireguardPeerConfig = let - snCfg = wgCfgOf serverNode; - in { + map (serverNode: let + snCfg = wgCfgOf serverNode; + in { + wireguardPeerConfig = { PublicKey = builtins.readFile (peerPublicKeyPath serverNode); PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName serverNode}.path; - # The allowed ips of a server node are it's own addreses, - # plus each external peer's addresses, - # plus each client's addresses that is connected via that node. - AllowedIPs = snCfg.addresses; + AllowedIPs = serverAllowedIPs serverNode; Endpoint = "${snCfg.server.host}:${toString snCfg.server.port}"; }; }) @@ -192,7 +206,7 @@ { PublicKey = builtins.readFile (peerPublicKeyPath clientNode); PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName clientNode}.path; - AllowedIPs = clientCfg.addresses; + AllowedIPs = map (net.cidr.make 128) clientCfg.addresses; } // optionalAttrs clientCfg.keepalive { PersistentKeepalive = 25; @@ -207,7 +221,7 @@ PublicKey = builtins.readFile (peerPublicKeyPath wgCfg.client.via); PresharedKeyFile = config.rekey.secrets.${peerPresharedKeySecret nodeName wgCfg.client.via}.path; # TODO this should be 0.0.0.0 if the client wants to route all traffic - AllowedIPs = (wgCfgOf wgCfg.client.via).addresses; + AllowedIPs = serverAllowedIPs wgCfg.client.via; }; } ]; @@ -301,44 +315,23 @@ in { description = mdDoc "The order priority used when creating systemd netdev and network files."; }; - cidrv4 = mkOption { - type = - if config.client.via != null - then net.types.cidrv4-in nodes.${config.client.via}.config.extra.wireguard.${name}.cidrv4 - else net.types.cidrv4; - description = mdDoc '' - The ipv4 host address (with cidr mask) to configure for this interface. - The cidr mask determines this peers allowed address range as configured on other peers. - The mask should usually be fully restricted (/32) when no external clients are configured - and no other node uses this as a via. - ''; + ipv4 = mkOption { + type = net.types.ipv4; + description = mdDoc "The ipv4 address for this machine."; }; - cidrv6 = mkOption { - type = - if config.client.via != null - then net.types.cidrv6-in nodes.${config.client.via}.config.extra.wireguard.${name}.cidrv6 - else net.types.cidrv6; - description = mdDoc '' - The ipv6 host address (with cidr mask) to configure for this interface. - The cidr mask determines this peers allowed address range as configured on other peers. - The mask should usually be fully restricted (/128) when no external clients are configured - and no other node uses this as a via. - ''; + ipv6 = mkOption { + type = net.types.ipv6; + description = mdDoc "The ipv6 address for this machine."; }; addresses = mkOption { - type = types.listOf ( - if config.client.via != null - then net.types.cidr-in nodes.${config.client.via}.config.extra.wireguard.${name}.addresses - else net.types.cidr - ); - default = [config.cidrv4 config.cidrv6]; + type = types.listOf net.types.ip; + default = [config.ipv4 config.ipv6]; description = mdDoc '' - The addresses (with cidr mask) to configure for this interface. - The cidr mask determines this peers allowed address range as configured on other peers. + The ip addresses (v4 and/or v6) to use for this machine. The actual network cidr will automatically be derived from all network participants. - By default this will just include {option}`cidrv4` and {option}`cidrv6` as configured. + By default this will just include {option}`ipv4` and {option}`ipv6` as configured. ''; }; };