diff --git a/hosts/common/core/default.nix b/hosts/common/core/default.nix index 4ed8ff7..f5b2e2e 100644 --- a/hosts/common/core/default.nix +++ b/hosts/common/core/default.nix @@ -19,6 +19,20 @@ ../../../modules/wireguard.nix ]; + # IP address math library + # https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba + # Plus some extensions by us + lib = let + libWithNet = (import "${inputs.lib-net}/net.nix" {inherit lib;}).lib; + in + lib.recursiveUpdate libWithNet { + net.cidr = rec { + hostCidr = n: x: "${libWithNet.net.cidr.host n x}/${libWithNet.net.cidr.length x}"; + ip = x: lib.head (lib.splitString "/" x); + canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x); + }; + }; + # Setup secret rekeying parameters rekey = { inherit diff --git a/hosts/common/core/net.nix b/hosts/common/core/net.nix index 40bc9b7..b42f47c 100644 --- a/hosts/common/core/net.nix +++ b/hosts/common/core/net.nix @@ -1,4 +1,5 @@ { + config, lib, pkgs, nodeName, @@ -8,6 +9,7 @@ inherit (lib) concatStringsSep + head mapAttrsToList mkDefault mkForce @@ -17,10 +19,59 @@ in { hostName = mkDefault nodeName; useDHCP = mkForce false; useNetworkd = true; - wireguard.enable = true; dhcpcd.enable = false; - nftables.enable = true; - firewall.enable = true; + + nftables = { + firewall.enable = true; + stopRuleset = mkDefault '' + table inet filter { + chain input { + type filter hook input priority filter; policy drop; + ct state invalid drop + ct state {established, related} accept + + iifname lo accept + meta l4proto ipv6-icmp accept + meta l4proto icmp accept + tcp dport ${toString (head config.services.openssh.ports)} accept + } + chain forward { + type filter hook forward priority filter; policy drop; + } + chain output { + type filter hook output priority filter; policy accept; + } + } + ''; + }; + + nftables.firewall = { + zones = lib.mkForce { + local.localZone = true; + }; + + rules = lib.mkForce { + icmp = { + early = true; + after = ["ct"]; + from = "all"; + to = ["local"]; + extraLines = [ + "ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept" + "ip protocol icmp icmp type { echo-request, router-advertisement } accept" + #"ip6 saddr fe80::/10 ip6 daddr fe80::/10 udp dport 546 accept" + ]; + }; + + ssh = { + early = true; + after = ["ct"]; + from = "all"; + to = ["local"]; + allowedTCPPorts = config.services.openssh.ports; + }; + }; + }; }; # Rename known network interfaces diff --git a/hosts/common/core/resolved.nix b/hosts/common/core/resolved.nix index 9e75dcb..0e610e1 100644 --- a/hosts/common/core/resolved.nix +++ b/hosts/common/core/resolved.nix @@ -1,10 +1,7 @@ {lib, ...}: { - networking = { - firewall = { - allowedTCPPorts = [5355]; - allowedUDPPorts = [5353 5355]; - }; - networkmanager.dns = "systemd-resolved"; + networking.firewall = { + allowedTCPPorts = [5355]; + allowedUDPPorts = [5353 5355]; }; services.resolved = { @@ -16,7 +13,7 @@ "8.8.8.8" "2001:4860:4860::8844" ]; - llmnr = "true"; + llmnr = "true"; # Microsoft's version of mDNS extraConfig = '' Domains=~. MulticastDNS=true diff --git a/hosts/ward/meta.nix b/hosts/ward/meta.nix index 4aeb0af..35f371c 100644 --- a/hosts/ward/meta.nix +++ b/hosts/ward/meta.nix @@ -3,7 +3,7 @@ system = "x86_64-linux"; microVmHost = true; physicalConnections = { - "10-lan1" = "LAN 1"; - "10-lan2" = "LAN 2"; + "10-lan" = "LAN"; + "10-wan" = "WAN"; }; } diff --git a/hosts/ward/net.nix b/hosts/ward/net.nix index 23298e1..9150760 100644 --- a/hosts/ward/net.nix +++ b/hosts/ward/net.nix @@ -1,27 +1,140 @@ { + config, lib, nodeSecrets, ... -}: { - networking.hostId = "49ce3b71"; +}: let + inherit (config.lib.net) cidr; + + net.lan.ipv4cidr = "192.168.100.1/24"; + net.lan.ipv6cidr = "fd01::1/64"; +in { + networking.hostId = nodeSecrets.networking.hostId; systemd.network.networks = { - "10-lan1" = { - DHCP = "yes"; - matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan1.mac; + "10-lan" = { + address = [net.lan.ipv4cidr net.lan.ipv6cidr]; + matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan.mac; networkConfig.IPv6PrivacyExtensions = "kernel"; dhcpV4Config.RouteMetric = 10; dhcpV6Config.RouteMetric = 10; }; - "10-lan2" = { + "10-wan" = { DHCP = "yes"; - matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan2.mac; + #address = [ + # "192.168.178.2/24" + # "fd00::1/64" + #]; + #gateway = [ + #]; + matchConfig.MACAddress = nodeSecrets.networking.interfaces.wan.mac; networkConfig.IPv6PrivacyExtensions = "kernel"; dhcpV4Config.RouteMetric = 20; dhcpV6Config.RouteMetric = 20; }; }; + networking.nftables.firewall = { + zones = lib.mkForce { + lan = { + interfaces = ["lan"]; + ipv4Addresses = [(cidr.canonicalize net.lan.ipv4cidr)]; + ipv6Addresses = [(cidr.canonicalize net.lan.ipv6cidr)]; + }; + wan = { + interfaces = ["wan"]; + # TODO ipv4Addresses = [ net.wan.netv4 ]; + # TODO ipv6Addresses = [ net.wan.netv6 ]; + ipv4Addresses = ["192.168.1.0/22"]; + ipv6Addresses = ["fd00::/64"]; + }; + }; + + rules = lib.mkForce { + masquerade-wan = { + from = ["lan"]; + to = ["wan"]; + masquerade = true; + }; + + outbound = { + from = ["lan"]; + to = ["lan" "wan"]; + late = true; # Only accept after any rejects have been processed + verdict = "accept"; + }; + + wan-to-local = { + from = ["wan"]; + to = ["local"]; + }; + + lan-to-local = { + from = ["lan"]; + to = ["local"]; + + inherit + (config.networking.firewall) + allowedTCPPorts + allowedUDPPorts + ; + }; + }; + }; + + services.kea = { + dhcp4 = { + enable = true; + settings = { + lease-database = { + name = "/var/lib/kea/dhcp4.leases"; + persist = true; + type = "memfile"; + }; + valid-lifetime = 4000; + renew-timer = 1000; + rebind-timer = 2000; + interfaces-config = { + interfaces = ["lan"]; + service-sockets-max-retries = -1; + }; + option-data = [ + { + name = "domain-name-servers"; + data = "1.1.1.1, 8.8.8.8"; + } + ]; + subnet4 = [ + { + interface = "lan"; + subnet = cidr.canonicalize net.lan.ipv4cidr; + pools = [ + {pool = "192.168.100.20 - 192.168.100.250";} + ]; + option-data = [ + { + name = "routers"; + data = cidr.ip net.lan.ipv4cidr; + } + ]; + #reservations = [ + # { + # duid = "aa:bb:cc:dd:ee:ff"; + # ip-address = cidr.ip net.lan.ipv4cidr; + # } + #]; + } + ]; + }; + }; + #dhcp6 = { + # enable = true; + #}; + }; + systemd.services.kea-dhcp4-server.after = [ + "sys-subsystem-net-devices-lan.device" + ]; + #extra.wireguard.vms = { # server = { # enable = true; diff --git a/hosts/ward/secrets/secrets.nix.age b/hosts/ward/secrets/secrets.nix.age index 9ef1da0..7990cb5 100644 Binary files a/hosts/ward/secrets/secrets.nix.age and b/hosts/ward/secrets/secrets.nix.age differ diff --git a/hosts/zackbiene/net.nix b/hosts/zackbiene/net.nix index 4f1af4b..bdcd9a5 100644 --- a/hosts/zackbiene/net.nix +++ b/hosts/zackbiene/net.nix @@ -1,4 +1,14 @@ -{nodeSecrets, ...}: { +{ + lib, + config, + nodeSecrets, + ... +}: let + inherit (config.lib.net) cidr; + + net.iot.ipv4cidr = "10.90.0.1/24"; + net.iot.ipv6cidr = "fd90::1/64"; +in { networking.hostId = nodeSecrets.networking.hostId; systemd.network.networks = { @@ -10,9 +20,27 @@ dhcpV6Config.RouteMetric = 10; }; "10-wlan1" = { - DHCP = "no"; + address = [net.iot.ipv4cidr net.iot.ipv6cidr]; matchConfig.MACAddress = nodeSecrets.networking.interfaces.wlan1.mac; - address = ["10.90.0.1/24"]; + }; + }; + + networking.nftables.firewall = { + zones = lib.mkForce { + lan.interfaces = ["lan1"]; + }; + + rules = lib.mkForce { + int-to-local = { + from = ["lan"]; + to = ["local"]; + + inherit + (config.networking.firewall) + allowedTCPPorts + allowedUDPPorts + ; + }; }; }; } diff --git a/modules/wireguard.nix b/modules/wireguard.nix index d3eac73..6e7ddf7 100644 --- a/modules/wireguard.nix +++ b/modules/wireguard.nix @@ -24,6 +24,7 @@ mkIf mkOption mkEnableOption + net optionalAttrs optionals splitString diff --git a/nix/generate-node.nix b/nix/generate-node.nix index 8d4e90f..078b6f6 100644 --- a/nix/generate-node.nix +++ b/nix/generate-node.nix @@ -26,7 +26,6 @@ in secrets = self.secrets.content; nodeSecrets = self.secrets.content.nodes.${nodeName}; nixos-hardware = nixos-hardware.nixosModules; - nixos-nftables-firewall = nixos-nftables-firewall.nixosModules; #impermanence = impermanence.nixosModules; }; imports = @@ -36,6 +35,7 @@ in #impermanence.nixosModules.default agenix.nixosModules.default agenix-rekey.nixosModules.default + nixos-nftables-firewall.nixosModules.default ] ++ optionals nodeMeta.microVmHost [ microvm.nixosModules.host diff --git a/nix/lib.nix b/nix/lib.nix index 9bdcfde..73228a7 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -15,11 +15,13 @@ flatten foldl' genAttrs + head mapAttrs' mergeAttrs nameValuePair partition removeSuffix + splitString substring unique ;