diff --git a/machines/nixos/vault01/k-radius/default.nix b/machines/nixos/vault01/k-radius/default.nix index 0c27125..85ffd4a 100644 --- a/machines/nixos/vault01/k-radius/default.nix +++ b/machines/nixos/vault01/k-radius/default.nix @@ -2,7 +2,16 @@ # # SPDX-License-Identifier: EUPL-1.2 -{ config, ... }: +{ + config, + lib, + meta, + ... +}: + +let + inherit (lib) filterAttrs mapAttrsToList; +in { imports = [ ./module.nix ]; @@ -40,13 +49,14 @@ radius_required_groups = [ "radius_access@sso.dgnum.eu" ]; # A mapping between Kanidm groups and VLANS - radius_groups = map ( - { vlan, ... }: + radius_groups = mapAttrsToList ( + _: + { id, ... }: { - inherit vlan; - spn = "vlan_${toString vlan}@sso.dgnum.eu"; + vlan = id; + spn = "vlan_${builtins.toString id}@sso.dgnum.eu"; } - ) config.networking.vlans-info; + ) (filterAttrs (_: { userOnly, ... }: userOnly) meta.isp.vlans); }; authTokenFile = config.age.secrets."radius-auth_token_file".path; diff --git a/machines/nixos/vault01/networking.nix b/machines/nixos/vault01/networking.nix index 3fb2f3f..1471ba3 100644 --- a/machines/nixos/vault01/networking.nix +++ b/machines/nixos/vault01/networking.nix @@ -3,165 +3,38 @@ # SPDX-License-Identifier: EUPL-1.2 { - pkgs, - lib, - meta, - name, config, + lib, + pkgs, + meta, ... }: let - inherit (lib) mapAttrs' mkOption nameValuePair; - inherit (lib.types) listOf attrs; + inherit (lib) + concatMapStringsSep + filterAttrs + mapAttrs' + nameValuePair + ; - uplink = { - ip = "10.120.33.250"; - prefix = 30; - - router = "10.120.33.249"; - }; - - mkNetwork = - name: - { - address ? [ ], - extraNetwork ? { }, - ... - }: - nameValuePair "10-${name}" ({ inherit name address; } // extraNetwork); + mkNetwork = name: { settings, ... }: nameValuePair "10-${name}" ({ inherit name; } // settings); mkNetdev = name: - { Id, ... }: + { id, ... }: nameValuePair "10-${name}" { netdevConfig = { Name = name; Kind = "vlan"; }; - vlanConfig.Id = Id; + vlanConfig.Id = id; }; - mkUserVlan = - { - vlan, - netIP, - servIP, - interfaceName, - ... - }: - { - name = interfaceName; - value = { - Id = vlan; - extraNetwork = { - networkConfig = { - LinkLocalAddressing = "no"; - DHCPServer = "yes"; - }; - linkConfig = { - Promiscuous = true; - MTUBytes = 1500; - }; - addresses = [ - { - Address = "${servIP}/27"; - AddPrefixRoute = false; - } - ]; - routes = [ - { - Destination = "${netIP}/27"; - Table = "user"; - } - ]; - }; - }; - }; - - userVlans = builtins.genList (id: rec { - vlan = 4094 - id; - prefix24nb = (id + 1) / 8; - prefix27nb = (id + 1 - prefix24nb * 8) * 32; - netIP = "10.0.${toString prefix24nb}.${toString prefix27nb}"; - servIP = "10.0.${toString prefix24nb}.${toString (prefix27nb + 1)}"; - interfaceName = "vlan-user-${toString vlan}"; - prefixLen = 27; - }) 850; - - vlans = { - vlan-uplink-cri = { - Id = 223; - address = with uplink; [ "${ip}/${builtins.toString prefix}" ]; - - extraNetwork = { - routes = [ - { - # Get the public ip from the metadata - PreferredSource = builtins.head meta.network.${name}.addresses.ipv4; - Gateway = uplink.router; - } - ]; - linkConfig.MTUBytes = 1500; - }; - }; - - vlan-admin = { - Id = 3000; - address = [ "fd26:baf9:d250:8000::1/64" ]; - }; - - vlan-admin-ap = { - Id = 3001; - address = [ - "fd26:baf9:d250:8001::1/64" - # FIXME: ipv4 is temporary for APs in production - "10.0.253.1/24" - ]; - extraNetwork = { - networkConfig = { - IPv6SendRA = true; - DHCPServer = "yes"; - }; - ipv6Prefixes = [ - { - AddressAutoconfiguration = false; - OnLink = false; - Prefix = "fd26:baf9:d250:8001::/64"; - } - ]; - }; - }; - - vlan-apro = { - Id = 2000; - address = [ "10.0.255.1/24" ]; - - extraNetwork = { - networkConfig.DHCPServer = "yes"; - linkConfig.MTUBytes = 1500; - }; - }; - - vlan-hypervisor = { - Id = 2001; - address = [ "10.0.254.1/24" ]; - - extraNetwork = { - networkConfig.DHCPServer = "yes"; - linkConfig.MTUBytes = 1500; - }; - }; - } // builtins.listToAttrs (map mkUserVlan userVlans); + vlans = mapAttrs' (name: nameValuePair "vlan-${name}") meta.isp.vlans; in + { - options.networking.vlans-info = mkOption { - type = listOf attrs; - description = '' - Information about vlans for log analysis. - ''; - readOnly = true; - }; config = { systemd = { network = { @@ -274,7 +147,7 @@ in } ]; }; - } // mapAttrs' mkNetdev vlans; + } // (mapAttrs' mkNetdev vlans); }; services = { @@ -296,25 +169,26 @@ in systemd-networkd.serviceConfig.LimitNOFILE = 4096; - net-checker = { - path = [ - pkgs.iputils - pkgs.systemd - ]; - script = '' - if ping -c 1 8.8.8.8 > /dev/null || ping -c 1 1.1.1.1 > /dev/null; then - echo network is up - ${lib.concatMapStringsSep "\n " ( - { interfaceName, ... }: "networkctl up ${interfaceName}" - ) userVlans} - else - echo network is down - ${lib.concatMapStringsSep "\n " ( - { interfaceName, ... }: "networkctl down ${interfaceName}" - ) userVlans} - fi - ''; - }; + net-checker = + let + userVlans = builtins.attrNames (filterAttrs (_: { userOnly, ... }: userOnly) vlans); + networkctl = action: concatMapStringsSep "\n " (name: "networkctl ${action} ${name}") userVlans; + in + { + path = [ + pkgs.iputils + pkgs.systemd + ]; + script = '' + if ping -c 1 8.8.8.8 > /dev/null || ping -c 1 1.1.1.1 > /dev/null; then + echo network is up + ${networkctl "up"} + else + echo network is down + ${networkctl "down"} + fi + ''; + }; }; timers.net-checker = { @@ -324,18 +198,6 @@ in }; networking = { - vlans-info = [ - { - vlan = 2001; - netIP = "10.0.254.0"; - prefixLen = 24; - } - { - vlan = 3001; - netIP = "10.0.253.0"; - prefixLen = 24; - } - ] ++ userVlans; nftables = { enable = true; tables = { diff --git a/machines/nixos/vault01/ulogd/default.nix b/machines/nixos/vault01/ulogd/default.nix index 026f0db..eee39c5 100644 --- a/machines/nixos/vault01/ulogd/default.nix +++ b/machines/nixos/vault01/ulogd/default.nix @@ -6,8 +6,10 @@ config, lib, pkgs, + meta, ... }: + { services = { ulogd = { @@ -59,7 +61,7 @@ }; environment.defaultPackages = [ (pkgs.callPackage ./fill-vlan_prefixes.nix { - inherit (config.networking) vlans-info; + inherit (meta.isp) vlans; postgresql = config.services.postgresql.package; }) (pkgs.callPackage ./nat-request-daddr.nix { diff --git a/machines/nixos/vault01/ulogd/fill-vlan_prefixes.nix b/machines/nixos/vault01/ulogd/fill-vlan_prefixes.nix index 5286c08..11be617 100644 --- a/machines/nixos/vault01/ulogd/fill-vlan_prefixes.nix +++ b/machines/nixos/vault01/ulogd/fill-vlan_prefixes.nix @@ -6,34 +6,29 @@ lib, writeShellApplication, writeText, - vlans-info, + vlans, postgresql, }: + let - inherit (lib) concatMapStringsSep; - sql-script = writeText "vlan-filling.sql" '' - DROP TABLE IF EXISTS vlan_prefixes; - CREATE TABLE vlan_prefixes ( - vlan_id smallint PRIMARY KEY UNIQUE NOT NULL, - prefix inet NOT NULL - ); - INSERT INTO vlan_prefixes VALUES - ${concatMapStringsSep ",\n " ( - { - vlan, - netIP, - prefixLen, - ... - }: - "(${toString vlan}, inet '${netIP}/${toString prefixLen}')" - ) vlans-info} - ; - ''; + inherit (lib) attrValues filter concatMapStringsSep; in + writeShellApplication { name = "fill-vlan_prefixes"; runtimeInputs = [ postgresql ]; text = '' - psql -d ulogd -U ulogd -f ${sql-script} + psql -d ulogd -U ulogd -f ${writeText "vlan-filling.sql" '' + DROP TABLE IF EXISTS vlan_prefixes; + CREATE TABLE vlan_prefixes ( + vlan_id smallint PRIMARY KEY UNIQUE NOT NULL, + prefix inet NOT NULL + ); + INSERT INTO vlan_prefixes VALUES + ${concatMapStringsSep ",\n " ( + vlan: "(${builtins.toString vlan.id}, inet '${vlan.internal.cidr}')" + ) (filter ({ internal, ... }: internal.cidr != null) (attrValues vlans))} + ; + ''} ''; } diff --git a/meta/default.nix b/meta/default.nix index 090f30f..42d1fb6 100644 --- a/meta/default.nix +++ b/meta/default.nix @@ -11,6 +11,7 @@ lib: (lib.evalModules { modules = lib.extra.mkImports ./. [ + "isp" "network" "nodes" "options" diff --git a/meta/isp/default.nix b/meta/isp/default.nix new file mode 100644 index 0000000..bfc89b6 --- /dev/null +++ b/meta/isp/default.nix @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: 2024 Tom Hubrecht +# SPDX-FileCopyrightText: 2025 Lubin Bailly +# +# SPDX-License-Identifier: EUPL-1.2 + +{ config, lib, ... }: + +let + inherit (lib) genList listToAttrs nameValuePair; + + mkCIDR = address: prefix: "${address}/${builtins.toString prefix}"; +in + +{ + imports = [ ./module.nix ]; + + isp = { + vlans = + { + uplink-cri = { + id = 223; + settings = { + address = [ (mkCIDR "10.120.33.250" 30) ]; + routes = [ + { + PreferredSource = builtins.head config.network.vault01.addresses.ipv4; + Gateway = "10.120.33.249"; + } + ]; + linkConfig.MTUBytes = 1500; + }; + }; + + admin = { + id = 3000; + settings = { + address = [ "fd26:baf9:d250:8000::1/64" ]; + }; + }; + + admin-ap = { + id = 3001; + settings = { + address = [ + "fd26:baf9:d250:8001::1/64" + # FIXME: ipv4 is temporary for APs in production + "10.0.253.1/24" + ]; + networkConfig = { + IPv6SendRA = true; + DHCPServer = "yes"; + }; + ipv6Prefixes = [ + { + AddressAutoconfiguration = false; + OnLink = false; + Prefix = "fd26:baf9:d250:8001::/64"; + } + ]; + }; + internal = { + network = "10.0.253.0"; + prefix = 24; + }; + }; + + apro = { + id = 2000; + settings = { + address = [ "10.0.255.1/24" ]; + networkConfig.DHCPServer = "yes"; + linkConfig.MTUBytes = 1500; + }; + }; + + hypervisor = { + id = 2001; + settings = { + address = [ "10.0.254.1/24" ]; + networkConfig.DHCPServer = "yes"; + linkConfig.MTUBytes = 1500; + }; + internal = { + network = "10.0.254.0"; + prefix = 24; + }; + }; + } + // listToAttrs ( + genList ( + base: + let + id = (4096 - 2) - base; + range24 = (base + 1) / 8; + range27 = (base + 1 - range24 * 8) * 32; + in + nameValuePair "user-${builtins.toString id}" rec { + inherit id; + internal = { + network = "10.0.${builtins.toString range24}.${builtins.toString range27}"; + address = "10.0.${builtins.toString range24}.${builtins.toString (range27 + 1)}"; + prefix = 27; + }; + settings = { + networkConfig = { + LinkLocalAddressing = "no"; + DHCPServer = "yes"; + }; + linkConfig = { + Promiscuous = true; + MTUBytes = 1500; + }; + addresses = [ + { + Address = mkCIDR internal.address internal.prefix; + AddPrefixRoute = false; + } + ]; + routes = [ + { + Destination = mkCIDR internal.network internal.prefix; + Table = "user"; + } + ]; + }; + userOnly = true; + } + ) 850 + ); + }; +} diff --git a/meta/isp/module.nix b/meta/isp/module.nix new file mode 100644 index 0000000..8c707c7 --- /dev/null +++ b/meta/isp/module.nix @@ -0,0 +1,106 @@ +# SPDX-FileCopyrightText: 2024 Tom Hubrecht +# +# SPDX-License-Identifier: EUPL-1.2 + +{ lib, ... }: + +let + inherit (lib) + mkDefault + mkIf + mkOption + ; + + inherit (lib.types) + attrs + attrsOf + bool + ints + nullOr + submodule + str + ; +in + +{ + options.isp = { + vlans = mkOption { + type = attrsOf ( + submodule ( + { config, ... }: + { + options = { + id = mkOption { + type = ints.between 0 (4096 - 1); + description = '' + The VLAN id to use. + ''; + }; + + settings = mkOption { + type = attrs; + default = { }; + description = '' + Settings for the configuration of networkd. + ''; + }; + + internal = { + network = mkOption { + type = nullOr str; + description = '' + The internal network address of this VLAN. + ''; + }; + + prefix = mkOption { + type = nullOr (ints.between 0 32); + default = if config.userOnly then 27 else null; + description = '' + The prefix length of the network associated to this VLAN. + ''; + }; + + address = mkOption { + type = nullOr str; + description = '' + The router address in the VLAN. It should be the first ipv4 in the network. + ''; + }; + + cidr = mkOption { + type = nullOr str; + default = + with config.internal; + if (prefix != null && network != null) then "${network}/${builtins.toString prefix}" else null; + description = '' + The CIDR notation of the network associated to the VLAN. + ''; + }; + }; + + userOnly = mkOption { + type = bool; + default = false; + description = '' + Whether this VLAN is only used by a user in the context of the IPS. + I.e. this is not an administration VLAN. + ''; + }; + }; + + # The address is null by default when not on a user VLAN + config.internal = mkIf (!config.userOnly) { + address = mkDefault null; + network = mkDefault null; + }; + } + ) + ); + default = [ ]; + description = '' + The list of VLANs known to our ISP. + ''; + }; + }; +}