From 2a36964fcc75de65b1d57de5e314575572b84076 Mon Sep 17 00:00:00 2001 From: Joakim Repomaa Date: Mon, 3 Feb 2025 15:32:09 +0200 Subject: [PATCH] refactor vlan and firewall config into modules fix bug with vlans module fix vlans --- flake.nix | 6 +- hosts/apu/configuration.nix | 160 ++++++++++++----------------------- modules/default.nix | 7 ++ modules/firewall.nix | 33 ++++++++ modules/vlans.nix | 161 ++++++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+), 112 deletions(-) create mode 100644 modules/default.nix create mode 100644 modules/firewall.nix create mode 100644 modules/vlans.nix diff --git a/flake.nix b/flake.nix index f1cdab7..feaa4bd 100644 --- a/flake.nix +++ b/flake.nix @@ -49,19 +49,19 @@ freun-dev = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = { inherit inputs; }; - modules = [ ./hosts/freun.dev ]; + modules = [ ./modules ./hosts/freun.dev ]; }; radish = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = { inherit inputs; }; - modules = [ ./hosts/radish ]; + modules = [ ./modules ./hosts/radish ]; }; apu = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; specialArgs = { inherit inputs; }; - modules = [ ./hosts/apu ]; + modules = [ ./modules ./hosts/apu ]; }; }; }; diff --git a/hosts/apu/configuration.nix b/hosts/apu/configuration.nix index 8aaf3d1..f170052 100644 --- a/hosts/apu/configuration.nix +++ b/hosts/apu/configuration.nix @@ -4,100 +4,6 @@ { lib, pkgs, config, ... }: let - services = { - ssh = { tcp = [ 22 ]; }; - dhcp = { udp = [ 67 68 ]; }; - dns = { udp = [ 53 ]; }; - web = { tcp = [ 80 443 ]; }; - }; - - vlans = { - koti = { - id = 10; - ipv6 = true; - availableServices = with services; [ dhcp dns ssh ]; - }; - gast = { - id = 20; - ipv6 = true; - staticLeases = [ - { macAddress = "dc:a6:32:05:08:5d"; address = "10.20.1.235"; } - ]; - availableServices = with services; [ dhcp dns ]; - }; - iot = { - id = 30; - availableServices = with services; [ dhcp dns ]; - }; - cfg = { - id = 40; - staticLeases = [ - { macAddress = "8c:3b:ad:c5:b8:ee"; address = "10.40.0.10"; } - ]; - availableServices = with services; [ dhcp dns ]; - }; - }; - - pvid = vlans.cfg.id; - vlanIds = lib.map ({ id, ... }: id) (lib.attrValues vlans); - vlanRange = { - min = lib.foldr lib.min 999 vlanIds; - max = lib.foldr lib.max 0 vlanIds; - }; - - buildVlanNetdev = name: { id, ... }: { - name = "20-${name}"; - value = { - netdevConfig = { - Name = name; - Kind = "vlan"; - MACAddress = "00:0d:b9:49:d2:${toString id}"; - }; - vlanConfig = { Id = id; }; - }; - }; - - buildStaticLease = { macAddress, address }: '' - [DHCPServerStaticLease] - MACAddress=${macAddress} - Address=${address} - ''; - - buildBridgeVLANConfig = id: { VLAN = id; }; - - buildVlanNetwork = name: { id, ipv6 ? false, staticLeases ? [ ], ... }: { - name = "30-${name}"; - value = { - matchConfig = { - Name = name; - }; - networkConfig = { - Address = "10.${toString id}.0.1/23"; - IPMasquerade = "ipv4"; - DHCPServer = true; - IPv6AcceptRA = false; - IPv6SendRA = ipv6; - DHCPPrefixDelegation = ipv6; - }; - dhcpServerConfig = { - PoolOffset = 255; - DNS = "10.${toString id}.0.1"; - }; - extraConfig = lib.concatLines (lib.map buildStaticLease staticLeases); - }; - }; - - buildFirewallRules = name: { availableServices ? [ ], ... }: { - allowedUDPPorts = lib.flatten (lib.map ({ udp ? [ ], ... }: udp) availableServices); - allowedTCPPorts = lib.flatten (lib.map ({ tcp ? [ ], ... }: tcp) availableServices); - }; - - vlanNetdevs = lib.mapAttrs' buildVlanNetdev vlans; - vlanNetworks = lib.mapAttrs' buildVlanNetwork vlans; - firewallRules = lib.mapAttrs buildFirewallRules ( - vlans // { "tailscale*".availableServices = with services; [ ssh web ]; } - ); - nginxVhost = options: { http2 = true; forceSSL = true; @@ -129,6 +35,51 @@ in }; }; + modules.vlans = { + enable = true; + networks = { + koti = { + id = 10; + ipv6 = true; + }; + + gast = { + id = 20; + ipv6 = true; + staticLeases = { + "dc:a6:32:05:08:5d" = "10.20.1.235"; + }; + }; + + iot = { + id = 30; + }; + + cfg = { + id = 40; + staticLeases = { + "8c:3b:ad:c5:b8:ee" = "10.40.0.10"; + }; + }; + }; + + bridge = { + enable = true; + pvid = config.modules.vlans.networks.cfg.id; + netdev = "20-lan"; + network = "30-lan"; + bindNetwork = "30-bind-lan"; + }; + }; + + modules.firewall.rules = { + koti = [ "dhcp" "dns" "ssh" ]; + gast = [ "dhcp" "dns" ]; + iot = [ "dhcp" "dns" ]; + cfg = [ "dhcp" "dns" ]; + "tailscale*" = [ "ssh" "web" ]; + }; + systemd.network = { enable = true; config.networkConfig.IPv6Forwarding = true; @@ -147,18 +98,16 @@ in linkConfig.Name = "intern1"; }; }; + netdevs = { "20-lan" = { netdevConfig = { Name = "lan"; Kind = "bridge"; }; - bridgeConfig = { - VLANFiltering = true; - DefaultPVID = pvid; - }; }; - } // vlanNetdevs; + }; + networks = { "30-bind-lan" = { matchConfig = { @@ -167,13 +116,8 @@ in networkConfig = { Bridge = "lan"; }; - extraConfig = '' - [BridgeVLAN] - VLAN=${toString vlanRange.min}-${toString vlanRange.max} - EgressUntagged=${toString pvid} - PVID=${toString pvid} - ''; }; + "30-lan" = { matchConfig = { Name = "lan"; @@ -182,9 +126,8 @@ in IPv6AcceptRA = false; ConfigureWithoutCarrier = true; }; - vlan = lib.attrNames vlans; - bridgeVLANs = lib.map buildBridgeVLANConfig vlanIds; }; + "30-wan" = { matchConfig = { Name = "extern0"; @@ -202,7 +145,7 @@ in Use6RD = true; }; }; - } // vlanNetworks; + }; }; services.networkd-dispatcher = { @@ -316,7 +259,6 @@ in networking.nftables.enable = true; networking.firewall.enable = true; networking.useDHCP = false; - networking.firewall.interfaces = firewallRules; system.stateVersion = "24.05"; } diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..8288022 --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,7 @@ +{ ... }: +{ + imports = [ + ./vlans.nix + ./firewall.nix + ]; +} diff --git a/modules/firewall.nix b/modules/firewall.nix new file mode 100644 index 0000000..4a193fb --- /dev/null +++ b/modules/firewall.nix @@ -0,0 +1,33 @@ +{ lib, config, ... }: +let + services = { + ssh = { tcp = [ 22 ]; }; + dhcp = { udp = [ 67 68 ]; }; + dns = { udp = [ 53 ]; }; + web = { tcp = [ 80 443 ]; }; + }; + + rulesForServices = enabledServices: + lib.foldr + (service: { allowedUDPPorts, allowedTCPPorts }: { + allowedUDPPorts = allowedUDPPorts ++ services.${service}.udp or [ ]; + allowedTCPPorts = allowedTCPPorts ++ services.${service}.tcp or [ ]; + }) + { allowedUDPPorts = [ ]; allowedTCPPorts = [ ]; } + enabledServices; + + cfg = config.modules.firewall.rules; +in +{ + options.modules.firewall.rules = lib.mkOption { + type = lib.types.attrsOf (lib.types.listOf (lib.types.enum (lib.attrNames services))); + default = { }; + }; + + config = lib.mkIf (lib.length (lib.attrNames cfg) > 0) { + networking.firewall = { + enable = lib.mkDefault true; + interfaces = lib.mapAttrs (_: enabledServices: rulesForServices enabledServices) cfg; + }; + }; +} diff --git a/modules/vlans.nix b/modules/vlans.nix new file mode 100644 index 0000000..b156818 --- /dev/null +++ b/modules/vlans.nix @@ -0,0 +1,161 @@ +{ lib, config, ... }: +let + types = { + portList = lib.types.listOf lib.types.int; + + service = lib.types.submodule { + options = { + tcp = lib.mkOption { + type = types.portList; + default = [ ]; + }; + udp = lib.mkOption { + type = types.portList; + default = [ ]; + }; + }; + }; + + vlan = lib.types.submodule { + options = { + id = lib.mkOption { + type = lib.types.int; + }; + staticLeases = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + }; + ipv6 = lib.mkEnableOption "ipv6"; + }; + }; + + bridge = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Enable bridge"; + + pvid = lib.mkOption { + type = lib.types.int; + }; + bindNetwork = lib.mkOption { + type = lib.types.str; + }; + network = lib.mkOption { + type = lib.types.str; + }; + netdev = lib.mkOption { + type = lib.types.str; + }; + }; + }; + }; + + cfg = config.modules.vlans; + vlans = lib.mapAttrsToList (attrName: { name ? attrName, ... }@vlan: ({ inherit name; } // vlan)) cfg.networks; + vlanAttrs = lib.listToAttrs (lib.map ({ name, ... }@value: { inherit name value; }) vlans); + + buildNetdev = name: { id, ... }: { + name = "20-${name}"; + value = { + netdevConfig = { + Name = name; + Kind = "vlan"; + MACAddress = "00:0d:b9:49:d2:${toString id}"; + }; + vlanConfig = { Id = id; }; + }; + }; + + buildStaticLease = macAddress: address: '' + [DHCPServerStaticLease] + MACAddress=${macAddress} + Address=${address} + ''; + + buildNetwork = name: { id, ipv6, staticLeases, ... }: { + name = "30-${name}"; + value = { + matchConfig = { + Name = name; + }; + networkConfig = { + Address = "10.${toString id}.0.1/23"; + IPMasquerade = "ipv4"; + DHCPServer = true; + IPv6AcceptRA = false; + IPv6SendRA = ipv6; + DHCPPrefixDelegation = ipv6; + }; + dhcpServerConfig = { + PoolOffset = 255; + DNS = "10.${toString id}.0.1"; + }; + extraConfig = lib.concatLines (lib.mapAttrsToList buildStaticLease staticLeases); + }; + }; + + vlanIds = lib.mapAttrsToList (_: { id, ... }: id) vlanAttrs; + + vlanRange = { + min = lib.foldr lib.min 999 vlanIds; + max = lib.foldr lib.max 0 vlanIds; + }; + + bridgeVLANConfig = '' + [BridgeVLAN] + VLAN=${toString vlanRange.min}-${toString vlanRange.max} + EgressUntagged=${toString cfg.bridge.pvid} + PVID=${toString cfg.bridge.pvid} + ''; + + bridgeNetDev = + if (cfg.bridge.enable) then { + "${cfg.bridge.netdev}".bridgeConfig = { + VLANFiltering = true; + DefaultPVID = cfg.bridge.pvid; + }; + } else { }; + + bridgeBindNetwork = + if (cfg.bridge.enable) then { + ${cfg.bridge.bindNetwork}.extraConfig = bridgeVLANConfig; + } else { }; + + bridgeNetwork = + if (cfg.bridge.enable) then { + "${cfg.bridge.network}" = { + vlan = lib.map ({ name, ... }: name) vlans; + bridgeVLANs = lib.map ({ id, ... }: { VLAN = id; }) vlans; + }; + } else { }; + + netdevs = lib.mergeAttrsList [ + (lib.mapAttrs' buildNetdev vlanAttrs) + bridgeNetDev + ]; + + networks = lib.mergeAttrsList [ + (lib.mapAttrs' buildNetwork vlanAttrs) + bridgeBindNetwork + bridgeNetwork + ]; +in +{ + options.modules.vlans = { + enable = lib.mkEnableOption "Enable VLANs"; + + networks = lib.mkOption { + type = lib.types.attrsOf types.vlan; + }; + + bridge = lib.mkOption { + type = types.bridge; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.network = { + enable = lib.mkDefault true; + inherit netdevs networks; + }; + }; +}