# SPDX-FileCopyrightText: 2024 Tom Hubrecht # SPDX-FileContributor: Maurice Debray # SPDX-FileContributor: Ryan Lahfa # # SPDX-License-Identifier: EUPL-1.2 { config, lib, ... }@args: let inherit (lib) mkEnableOption mkDefault mkIf mkOption optionalAttrs ; inherit (lib.types) attrs attrsOf enum ints listOf nullOr positive singleLineStr str submodule unspecified ; 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."; }; }; }; vpnKeyType = submodule { options = { id = mkOption { type = positive; description = '' Unique ID that will be used to guess IP address ''; }; key = mkOption { type = str; description = '' Public key of the user for this VPN ''; }; }; }; org = config.organization; nixpkgs = import ./nixpkgs.nix; in { options = { organization = { members = mkOption { type = attrsOf ( submodule ( { name, ... }: { options = { name = mkOption { type = str; description = '' Name of the member. ''; }; email = mkOption { type = str; description = '' Main e-mail address of the member. ''; }; username = mkOption { type = str; default = name; description = '' The username used for authentication. WARNING: Must be the same as the ens login! ''; }; sshKeys = lib.mkOption { type = listOf singleLineStr; description = '' A list of verbatim OpenSSH public keys that should be added to the user's authorized keys. ''; example = [ "ssh-rsa AAAAB3NzaC1yc2etc/etc/etcjwrsh8e596z6J0l7 example@host" "ssh-ed25519 AAAAC3NzaCetcetera/etceteraJZMfk3QPfQ foo@bar" ]; }; vpnKeys = mkOption { type = attrsOf vpnKeyType; default = { }; description = "Attribute sets to define vpn keys of the user"; }; }; } ) ); description = '' Members of the DGNum organization. ''; }; groups = mkOption { type = attrsOf (listOf str); description = '' Groups of the DGNum organization. ''; }; external = mkOption { type = attrsOf (listOf str); description = '' External services used by the DGNum organization. ''; }; services = mkOption { type = attrsOf (submodule { options = { admins = mkOption { type = listOf str; default = [ ]; description = '' List of administrators of the service. ''; }; adminGroups = mkOption { type = listOf str; default = [ ]; description = '' List of administrator groups of the service. ''; }; }; }); description = '' Administrator access of the different DGNum services, it is mainly indicative as most services cannot configure this statically. ''; }; }; nodes = mkOption { type = attrsOf ( submodule ( { config, name, ... }: { options = { deployment = mkOption { type = attrs; default = { }; }; stateVersion = mkOption { type = nullOr str; description = '' State version of the node. ''; }; nixpkgs = { version = mkOption { type = enum nixpkgs.versions; description = '' Version of nixpkgs to use. ''; example = "unstable"; }; system = mkOption { type = enum nixpkgs.systems; description = '' Type of system for this node, will impact how it is evaluated and deployed. ''; example = "zyxel-nwa50ax"; }; }; nix-modules = mkOption { type = listOf str; default = [ ]; description = '' List of modules to import from [nix-modules](https://git.hubrecht.ovh/hubrecht/nix-modules). ''; }; hashedPassword = mkOption { type = str; description = '' The hashed password for the root account. ''; }; sshKeys = lib.mkOption { type = listOf singleLineStr; description = '' A list of verbatim OpenSSH public keys used by the machine ssh server. ''; example = [ "ssh-rsa AAAAB3NzaC1yc2etc/etc/etcjwrsh8e596z6J0l7 example@host" "ssh-ed25519 AAAAC3NzaCetcetera/etceteraJZMfk3QPfQ foo@bar" ]; }; 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. ''; }; vm-cluster = mkOption { type = nullOr str; default = null; description = "VM cluster where the VM is located"; }; }; config = { deployment = { tags = [ "infra-${config.site}" config.nixpkgs.system ]; } // (optionalAttrs (builtins.hasAttr name args.config.network) { 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; 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. ''; }; netbirdIp = mkOption { type = nullOr str; description = '' IP address of the node in the netbird network. ''; }; vpnKeys = mkOption { type = attrsOf vpnKeyType; default = { }; description = "Attribute sets to define vpn keys of the machine"; }; }; 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 "ipv6") (builtins.attrValues config.interfaces)); }; }; } ) ); description = '' Network configuration for the different machines. ''; }; assertions = mkOption { type = listOf unspecified; internal = true; default = [ ]; description = '' This option allows modules to express conditions that must hold for the evaluation of the system configuration to succeed, along with associated error messages for the user. ''; }; }; config = let members = builtins.attrNames org.members; groups = builtins.attrNames org.groups; nameExists = list: f: groups: builtins.attrValues ( builtins.mapAttrs (name: members: { assertion = builtins.all (x: builtins.elem x list) members; message = f name; }) groups ); membersExists = nameExists members; groupsExists = nameExists groups; extract = name: builtins.mapAttrs (_: builtins.getAttr name); in { assertions = builtins.concatLists [ # Check that all group members exist (membersExists ( name: "A member of the ${name} group was not found in the members list." ) org.groups) # Check that all node admins exist (membersExists (name: "A member of the node ${name} admins was not found in the members list.") ( extract "admins" config.nodes )) # Check that all node adminGroups exist (groupsExists (name: "A member of the node ${name} adminGroups was not found in the groups list.") ( extract "adminGroups" config.nodes )) # Check that all services admins exist (membersExists (name: "A member of the service ${name} admins was not found in the members list.") ( extract "admins" org.services )) # Check that all services adminGroups exist (groupsExists ( name: "A member of the service ${name} adminGroups was not found in the groups list." ) (extract "adminGroups" org.services)) # Check that all external services admins exist (membersExists ( name: "A member of the external service ${name} admins was not found in the members list." ) org.external) ]; }; }