diff --git a/default.nix b/default.nix index 8a01594..9213fa7 100644 --- a/default.nix +++ b/default.nix @@ -59,10 +59,9 @@ let in { - nodes = - pkgs.lib.concatMapAttrs - (site: builtins.foldl' (acc: host: acc // { ${host} = "${host}.${site}.infra.dgnum.eu"; }) { }) - (import ./meta/infrastructure.nix); + nodes = builtins.mapAttrs (host: { site, ... }: "${host}.${site}.infra.dgnum.eu") ( + import ./meta/nodes.nix + ); shells = { default = pkgs.mkShell { diff --git a/hive.nix b/hive.nix index c94568a..6991c61 100644 --- a/hive.nix +++ b/hive.nix @@ -1,42 +1,26 @@ let sources = import ./npins; - metadata = import ./meta; lib = import (sources.nix-lib + "/src/trivial.nix"); patch = import sources.nix-patches { patchFile = ./patches; }; - mkNode = node: _: { + nodes' = import ./meta/nodes.nix; + nodes = builtins.attrNames nodes'; + + mkNode = node: { # Import the base configuration for each node - imports = builtins.map (lib.mkRel ./machines/${node}) [ + imports = builtins.map (lib.mkRel (./machines/${node})) [ "_configuration.nix" "_hardware-configuration.nix" ]; - - # Include default secrets - age-secrets.sources = [ ./machines/${node}/secrets ]; - - # Deployment config is specified in meta.nodes.${node}.deployment - inherit (metadata.nodes.${node}) deployment; - - # Set NIX_PATH to the patched version of nixpkgs - nix.nixPath = [ "nixpkgs=${mkNixpkgs node}" ]; - nix.optimise.automatic = true; - - # Allow unfree packages - nixpkgs.config.allowUnfree = true; - - # Use the stateVersion declared in the metadata - system = { - inherit (metadata.nodes.${node}) stateVersion; - }; }; mkNixpkgs = node: patch.mkNixpkgsSrc rec { src = sources.${version}; - version = "nixos-${metadata.nodes.${node}.nixpkgs}"; + version = "nixos-${nodes'.${node}.nixpkgs or (import ./meta/nixpkgs.nix)}"; }; mkNixpkgs' = node: import (mkNixpkgs node) { }; @@ -44,31 +28,56 @@ let ### # Function to create arguments based on the node # - mkArgs = node: { + mkArgs = node: rec { lib = import sources.nix-lib { inherit (mkNixpkgs' node) lib; keysRoot = ./keys; }; - }; - nodes = builtins.attrNames metadata.nodes; + meta = (import ./meta).meta lib; + }; in +# nodes = builtins.attrNames metadata.nodes; { meta = { nodeNixpkgs = lib.mapSingleFuse mkNixpkgs' nodes; specialArgs = { inherit sources; - meta = metadata; }; nodeSpecialArgs = lib.mapSingleFuse mkArgs nodes; }; - defaults = _: { - # Import the default modules - imports = [ ./modules ]; - }; + defaults = + { + pkgs, + meta, + name, + ... + }: + { + # Import the default modules + imports = [ ./modules ]; + + # Include default secrets + age-secrets.sources = [ (./machines + "/${name}/secrets") ]; + + # Deployment config is specified in meta.nodes.${node}.deployment + inherit (meta.nodes.${name}) deployment; + + # Set NIX_PATH to the patched version of nixpkgs + nix.nixPath = [ "nixpkgs=${pkgs.path}" ]; + nix.optimise.automatic = true; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Use the stateVersion declared in the metadata + system = { + inherit (meta.nodes.${name}) stateVersion; + }; + }; } // (lib.mapSingleFuse mkNode nodes) diff --git a/meta/default.nix b/meta/default.nix index cbbfa6e..8724d91 100644 --- a/meta/default.nix +++ b/meta/default.nix @@ -3,60 +3,23 @@ # accessible through the specialArg meta in the config. let - ### - # Transforms data from : - # { - # zone01 = [ node01 node02 ]; - # zone02 = [ node03 ]; - # } - # to : - # { - # node01 = zone01; - # node02 = zone01; - # node03 = zone02; - # } - locations = builtins.foldl' (a: loc: a // loc) { } ( - builtins.concatLists ( - builtins.attrValues (builtins.mapAttrs (zone: builtins.map (n: { ${n} = zone; })) infra) - ) - ); - - ### - # Add computed data about the nodes : - # - zone - # - deployment tags - # - network information - mkNode = - node: attrs: - attrs - // { - zone = locations.${node}; - deployment = - let - old = attrs.deployment; - in - old - // { - tags = (old.tags or [ ]) ++ [ "infra-${locations.${node}}" ]; - targetHost = old.targetHost or (builtins.head network.${node}.addresses.public); - }; - }; - - infra = import ./infrastructure.nix; - members = import ./members.nix; - network = import ./network.nix; - - nodes = builtins.mapAttrs mkNode (import ./nodes.nix); - - meta = { - inherit - infra - members - network - nodes - ; - }; - dns = args: import ./dns.nix (args // { inherit meta; }); + + meta = + lib: + (lib.evalModules { + modules = [ + ./options.nix + { + network = import ./network.nix; + nodes = import ./nodes.nix; + organization = import ./organization.nix; + } + ]; + class = "dgnumMeta"; + }).config; in -meta // { inherit dns; } + +{ + inherit dns meta; +} diff --git a/meta/dns.nix b/meta/dns.nix index 2ae8a47..716ad8f 100644 --- a/meta/dns.nix +++ b/meta/dns.nix @@ -10,19 +10,21 @@ let inherit (dns.lib.combinators) mx spf ttl; + meta' = meta lib; + mkCNAME = host: { CNAME = [ host ]; }; mkRecord = host: let - net = meta.network.${host}; + net = meta'.network.${host}; in { A = net.addresses.publicV4; AAAA = net.addresses.publicV6; }; - mkHosted = server: mapSingleFuse (_: mkCNAME "${server}.${meta.nodes.${server}.zone}.infra"); + mkHosted = server: mapSingleFuse (_: mkCNAME "${server}.${meta'.nodes.${server}.site}.infra"); cnames = builtins.mapAttrs (_: to: { CNAME = [ to ]; }) { dev = "dev.pages.codeberg.page."; diff --git a/meta/infrastructure.nix b/meta/infrastructure.nix deleted file mode 100644 index 8968307..0000000 --- a/meta/infrastructure.nix +++ /dev/null @@ -1,25 +0,0 @@ -# Description of the infrastructure - -{ - # Salle serveur sous le pavillon Pasteur - par01 = [ - "compute01" - "storage01" - ]; - - # Jourdan - par02 = [ - "geo01" - "geo02" - "vault01" - ]; - - # Luj's infra - par03 = [ "rescue01" ]; - - # VMs du SPI/NPS/Whatever - dmi01 = [ - "web01" - "web02" - ]; -} diff --git a/meta/network.nix b/meta/network.nix index 479d1d5..0a3e1e7 100644 --- a/meta/network.nix +++ b/meta/network.nix @@ -1,55 +1,4 @@ -let - mkDefaultInterface = - _: attrs: - { - ipv4 = [ ]; - ipv6 = [ ]; - gateways = [ ]; - } - // attrs; - - mkBase = - config: config // { interfaces = builtins.mapAttrs mkDefaultInterface (config.interfaces or { }); }; - - getAddresses = version: interface: builtins.map (builtins.getAttr "address") interface.${version}; - - filterIPv4 = ip: builtins.substring 0 7 ip != "192.168"; - filterIPv6 = _: true; - - mkNet = - _: value: - let - base = mkBase value; - in - base - // { - addresses = - let - _addresses = - builtins.foldl' - ( - { ipv4, ipv6 }: - net: { - ipv4 = ipv4 ++ getAddresses "ipv4" net; - ipv6 = ipv6 ++ getAddresses "ipv6" net; - } - ) - { - ipv4 = [ ]; - ipv6 = [ ]; - } - (builtins.attrValues base.interfaces); - in - _addresses - // rec { - publicV4 = builtins.filter filterIPv4 _addresses.ipv4; - publicV6 = builtins.filter filterIPv6 _addresses.ipv6; - public = publicV4 ++ publicV6; - }; - }; -in - -builtins.mapAttrs mkNet { +{ compute01 = { interfaces = { eno1 = { @@ -65,6 +14,7 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.146.254" ]; + enableDefaultDNS = true; }; }; @@ -82,15 +32,15 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.210.254" ]; + + dns = [ + "129.199.96.11" + "129.199.72.99" + ]; }; }; hostId = "b88fee0c"; - - dns = [ - "129.199.96.11" - "129.199.72.99" - ]; }; geo02 = { @@ -104,15 +54,15 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.210.254" ]; + + dns = [ + "129.199.96.11" + "129.199.72.99" + ]; }; }; hostId = "45d65237"; - - dns = [ - "129.199.96.11" - "129.199.72.99" - ]; }; storage01 = { @@ -130,6 +80,7 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.146.254" ]; + enableDefaultDNS = true; }; }; @@ -166,6 +117,7 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.129.1" ]; + enableDefaultDNS = true; }; }; @@ -183,6 +135,7 @@ builtins.mapAttrs mkNet { ]; gateways = [ "129.199.129.1" ]; + enableDefaultDNS = true; }; }; @@ -198,6 +151,7 @@ builtins.mapAttrs mkNet { prefixLength = 64; } ]; + ipv4 = [ { address = "192.168.0.232"; @@ -205,6 +159,7 @@ builtins.mapAttrs mkNet { } ]; gateways = [ "192.168.0.1" ]; + enableDefaultDNS = true; }; }; hostId = "007f0200"; diff --git a/meta/nixpkgs.nix b/meta/nixpkgs.nix new file mode 100644 index 0000000..9e641df --- /dev/null +++ b/meta/nixpkgs.nix @@ -0,0 +1,2 @@ +# Default version of nixpkgs to use +"23.11" diff --git a/meta/nodes.nix b/meta/nodes.nix index b823393..048b631 100644 --- a/meta/nodes.nix +++ b/meta/nodes.nix @@ -10,32 +10,30 @@ # nixpkgs = "unstable" or "22.11"; # nixpkgs version # } -let - mkNode = - _: attrs: - { - adminGroups = [ ]; - admins = [ ]; +/* Liste des différents sites : + - dmi01 -> VM du NPSPI + - par01 -> Salle serveur sous le pavillon Pasteur + - par02 -> Local DGNum Jourdan + - par03 -> VM de Luj +*/ - deployment = { }; - - nixpkgs = "23.11"; - } - // attrs; -in - -builtins.mapAttrs mkNode { +{ web01 = { + site = "dmi01"; + deployment.tags = [ "web" ]; stateVersion = "23.05"; }; compute01 = { + site = "par01"; + stateVersion = "23.05"; }; geo01 = { + site = "par02"; deployment.tags = [ "geo" ]; stateVersion = "24.05"; @@ -43,6 +41,7 @@ builtins.mapAttrs mkNode { }; geo02 = { + site = "par02"; deployment.tags = [ "geo" ]; stateVersion = "24.05"; @@ -50,20 +49,27 @@ builtins.mapAttrs mkNode { }; storage01 = { + site = "par01"; stateVersion = "23.11"; }; vault01 = { + site = "par02"; + stateVersion = "23.11"; nixpkgs = "unstable"; }; web02 = { + site = "dmi01"; + stateVersion = "24.05"; nixpkgs = "unstable"; }; rescue01 = { + site = "par03"; + stateVersion = "23.11"; }; } diff --git a/meta/options.nix b/meta/options.nix new file mode 100644 index 0000000..322d497 --- /dev/null +++ b/meta/options.nix @@ -0,0 +1,256 @@ +{ lib, ... }@args: + +let + inherit (lib) + mkEnableOption + mkDefault + mkIf + mkOption + ; + + inherit (lib.types) + attrs + attrsOf + ints + listOf + nullOr + str + submodule + ; + + addressType = + max: + submodule { + options = { + address = mkOption { + type = str; + description = "IP address."; + }; + prefixLength = mkOption { + type = ints.between 8 max; + description = "Length of the prefix used in the local network."; + }; + }; + }; +in + +{ + options = { + organization = { + members = mkOption { + type = attrsOf ( + submodule { + options = { + name = mkOption { + type = str; + description = '' + Name of the member. + ''; + }; + + email = mkOption { + type = str; + description = '' + Main e-mail address of the member. + ''; + }; + }; + } + ); + + description = '' + Members of the DGNum organization. + ''; + }; + + groups = mkOption { + type = attrsOf (listOf str); + description = '' + Groups of the DGNum organization. + ''; + }; + }; + + nodes = mkOption { + type = attrsOf ( + submodule ( + { config, name, ... }: + { + options = { + deployment = mkOption { + type = attrs; + default = { }; + }; + + stateVersion = mkOption { + type = str; + description = '' + State version of the node. + ''; + }; + + nixpkgs = mkOption { + type = str; + default = import ./nixpkgs.nix; + description = '' + Version of nixpkgs to use. + ''; + }; + + admins = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of members to be given root access to this node. + ''; + }; + + adminGroups = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of groups to be given root access to this node. + ''; + }; + + site = mkOption { + type = str; + description = '' + Geographical site where the node is located. + ''; + }; + }; + + config = { + deployment = { + tags = [ "infra-${config.site}" ]; + targetHost = + let + ip = with args.config.network.${name}.addresses; ipv4 ++ ipv6; + in + mkIf (ip != [ ]) (mkDefault (builtins.head ip)); + }; + }; + } + ) + ); + description = '' + Nodes of the infrastructure. + ''; + }; + + network = mkOption { + type = attrsOf ( + submodule ( + { config, ... }: + { + options = { + interfaces = mkOption { + type = attrsOf ( + submodule ( + { config, ... }: + { + options = { + ipv4 = mkOption { + type = listOf (addressType 32); + default = [ ]; + description = '' + List of ipv4 addresses assigned to the interface. + ''; + }; + + ipv6 = mkOption { + type = listOf (addressType 64); + default = [ ]; + description = '' + List of ipv6 addresses assigned to the interface. + ''; + }; + + gateways = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of gateways used by the interface. + ''; + }; + + DHCP = mkOption { + type = nullOr str; + default = null; + description = "Whether to enable DHCP on the interface."; + }; + + dns = mkOption { + type = listOf str; + default = [ ]; + }; + + enableDefaultDNS = mkEnableOption "default DNS servers."; + }; + + config.dns = mkIf config.enableDefaultDNS [ + "1.1.1.1#cloudflare-dns.com" + "8.8.8.8#dns.google" + "1.0.0.1#cloudflare-dns.com" + "8.8.4.4#dns.google" + "2606:4700:4700::1111#cloudflare-dns.com" + "2001:4860:4860::8888#dns.google" + "2606:4700:4700::1001#cloudflare-dns.com" + "2001:4860:4860::8844#dns.google" + ]; + } + ) + ); + }; + + addresses = { + ipv4 = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of public ipv4 addresses of the node. + ''; + }; + + ipv6 = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of public ipv6 addresses of the node. + ''; + }; + }; + + hostId = mkOption { + type = str; + description = '' + Network Id of the node. + ''; + }; + }; + + config = + let + getAddresses = + version: builtins.concatMap (int: builtins.map (builtins.getAttr "address") int.${version}); + in + { + addresses = { + ipv4 = builtins.filter (ip: builtins.substring 0 7 ip != "192.168") ( + getAddresses "ipv4" (builtins.attrValues config.interfaces) + ); + ipv6 = builtins.filter (_: true) ((getAddresses "ipv4") (builtins.attrValues config.interfaces)); + }; + }; + } + ) + ); + description = '' + Network configuration for the different machines. + ''; + }; + }; + + config = { }; +} diff --git a/meta/members.nix b/meta/organization.nix similarity index 96% rename from meta/members.nix rename to meta/organization.nix index 4007ed8..981ec01 100644 --- a/meta/members.nix +++ b/meta/organization.nix @@ -1,7 +1,8 @@ /* To add a new member add an attribute to `members` Then add the key to the required groups. */ -let + +{ members = { mdebray = { name = "Maurice Debray"; @@ -50,7 +51,4 @@ let bureau = [ "gdd" ]; }; -in -{ - inherit groups members; } diff --git a/meta/verify.nix b/meta/verify.nix new file mode 100644 index 0000000..80d374a --- /dev/null +++ b/meta/verify.nix @@ -0,0 +1,6 @@ +let + sources = import ../npins; + pkgs = import sources.nixpkgs { }; +in + +builtins.deepSeq ((import ./.).meta pkgs.lib) { } diff --git a/modules/dgn-access-control.nix b/modules/dgn-access-control.nix index b8f15f5..1fbece8 100644 --- a/modules/dgn-access-control.nix +++ b/modules/dgn-access-control.nix @@ -51,9 +51,9 @@ let nodeMeta = meta.nodes.${name}; admins = - meta.members.groups.root + meta.organization.groups.root ++ nodeMeta.admins - ++ (builtins.concatMap (g: meta.members.groups.${g}) nodeMeta.adminGroups); + ++ (builtins.concatMap (g: meta.organization.groups.${g}) nodeMeta.adminGroups); cfg = config.dgn-access-control; in diff --git a/modules/dgn-network.nix b/modules/dgn-network.nix index 0c7eff7..19deac1 100644 --- a/modules/dgn-network.nix +++ b/modules/dgn-network.nix @@ -7,12 +7,7 @@ }: let - inherit (lib) - mapAttrs' - mkEnableOption - mkIf - optionalAttrs - ; + inherit (lib) mapAttrs' mkEnableOption mkIf; net' = meta.network.${name}; @@ -31,20 +26,7 @@ let address = builtins.map mkAddress (net.ipv4 ++ net.ipv6); routes = builtins.map mkRoute net.gateways; - # Add default DNS servers - dns = - net'.dns or [ - "1.1.1.1#cloudflare-dns.com" - "8.8.8.8#dns.google" - "1.0.0.1#cloudflare-dns.com" - "8.8.4.4#dns.google" - "2606:4700:4700::1111#cloudflare-dns.com" - "2001:4860:4860::8888#dns.google" - "2606:4700:4700::1001#cloudflare-dns.com" - "2001:4860:4860::8844#dns.google" - ]; - - networkConfig = optionalAttrs (net ? DHCP) { inherit (net) DHCP; }; + inherit (net) DHCP dns; }; }; @@ -60,7 +42,7 @@ in inherit (net') hostId; hostName = name; - domain = "${meta.nodes.${name}.zone}.infra.dgnum.eu"; + domain = "${meta.nodes.${name}.site}.infra.dgnum.eu"; useNetworkd = true; firewall.logRefusedConnections = false;