From 2f83f842708f0d1da3f5e552bd81b98aa399c8bc Mon Sep 17 00:00:00 2001 From: tophC7 Date: Tue, 22 Apr 2025 21:07:07 -0400 Subject: [PATCH] Add secret spec & update host config - Import secrets modules into main configuration - Rename and rearrange host specification options - Introduce new host options: desktop, shell, and pool - Create secret spec with SSH, API, Docker, user, and firewall submodules --- flake.nix | 6 ++ lib/default.nix | 1 + modules/common/host-spec.nix | 67 ++++++++---- modules/common/secret-spec.nix | 181 +++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 18 deletions(-) create mode 100644 modules/common/secret-spec.nix diff --git a/flake.nix b/flake.nix index 88b13a9..2fd6ee9 100644 --- a/flake.nix +++ b/flake.nix @@ -121,6 +121,12 @@ self.overlays.default ]; } + + # Import secrets + ./modules/common/secret-spec.nix + ./secrets.nix + + # Host-specific configuration ./hosts/nixos/${host} ]; }; diff --git a/lib/default.nix b/lib/default.nix index cab49a8..b4ec6eb 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -3,6 +3,7 @@ { # use path relative to the root of the project relativeToRoot = lib.path.append ../.; + scanPaths = path: builtins.map (f: (path + "/${f}")) ( diff --git a/modules/common/host-spec.nix b/modules/common/host-spec.nix index 14c64ef..2fdacb2 100644 --- a/modules/common/host-spec.nix +++ b/modules/common/host-spec.nix @@ -7,34 +7,36 @@ }: { options.hostSpec = { - username = lib.mkOption { - type = lib.types.str; - description = "The username of the host"; - }; - password = lib.mkOption { - type = lib.types.str; - description = "Password of the host"; - }; hostName = lib.mkOption { type = lib.types.str; description = "The hostname of the host"; }; + + username = lib.mkOption { + type = lib.types.str; + description = "The username for the host's user"; + }; + + password = lib.mkOption { + type = lib.types.str; + description = "Hashed password for the host's user"; + }; + email = lib.mkOption { - type = lib.types.attrsOf lib.types.str; + type = lib.types.str; description = "The email of the user"; }; - domain = lib.mkOption { - type = lib.types.str; - description = "The domain of the host"; - }; - userFullName = lib.mkOption { - type = lib.types.str; - description = "The full name of the user"; - }; + handle = lib.mkOption { type = lib.types.str; description = "The handle of the user (eg: github user)"; }; + + userFullName = lib.mkOption { + type = lib.types.str; + description = "The full name of the user"; + }; + home = lib.mkOption { type = lib.types.str; description = "The home directory of the user"; @@ -44,20 +46,49 @@ in if pkgs.stdenv.isLinux then "/home/${user}" else "/Users/${user}"; }; + isARM = lib.mkOption { type = lib.types.bool; default = false; description = "Used to indicate a host that is aarch64"; }; + isMinimal = lib.mkOption { type = lib.types.bool; default = false; - description = "Used to indicate a minimal host"; + description = "Used to indicate a minimal configuration host"; }; + isServer = lib.mkOption { type = lib.types.bool; default = false; description = "Used to indicate a server host"; }; + + desktop = lib.mkOption { + type = lib.types.nullOr ( + lib.types.enum [ + "Gnome" + "Hyprland" + ] + ); + default = if config.hostSpec.isServer then null else "Gnome"; + description = "Desktop environment (Gnome, Hyprland or null)"; + }; + + shell = lib.mkOption { + type = lib.types.enum [ + pkgs.fish + pkgs.bash + ]; + default = pkgs.fish; + description = "Default shell (pkgs.fish or pkgs.bash)"; + }; + + pool = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether it mounts pool from PVE"; + }; }; } diff --git a/modules/common/secret-spec.nix b/modules/common/secret-spec.nix new file mode 100644 index 0000000..8389148 --- /dev/null +++ b/modules/common/secret-spec.nix @@ -0,0 +1,181 @@ +# Specifications For Secret Data Structures +{ + pkgs, + config, + lib, + ... +}: +let + # Function to create a private key file in the Nix store with proper permissions + mkSshKeyFile = + name: content: + pkgs.writeTextFile { + name = "ssh-key-${name}"; + text = content; + executable = false; + checkPhase = '' + # Verify it's a valid SSH key (optional) + grep -q "BEGIN OPENSSH PRIVATE KEY" "$out" || (echo "Invalid SSH key format"; exit 1) + ''; + }; +in +{ + options.secretsSpec = { + ssh = lib.mkOption { + type = lib.types.submodule { + options = { + # privateKeys are set up automagically 🌠, see config below + privateKeyContents = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = "SSH private key contents keyed by name"; + default = { }; + }; + privateKeys = lib.mkOption { + type = lib.types.attrsOf lib.types.path; + description = "SSH private key file paths keyed by name"; + # default = { }; + readOnly = true; + }; + publicKeys = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = "SSH public keys keyed by name"; + default = { }; + }; + knownHosts = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = "SSH known hosts entries keyed by hostname"; + default = { }; + }; + }; + }; + default = { }; + description = "SSH key related secrets"; + }; + + api = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = "API keys keyed by service name"; + default = { }; + }; + + docker = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrsOf lib.types.str); + description = "Docker environment variables keyed by container name"; + default = { }; + }; + + users = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + password = lib.mkOption { + type = lib.types.str; + description = "Hashed password for the user"; # nix-shell -p whois --run 'mkpasswd --method=sha-512 --rounds=656000' + }; + email = lib.mkOption { + type = lib.types.str; + description = "Email address for the user"; + }; + handle = lib.mkOption { + type = lib.types.str; + description = "The handle of the user (eg: github user)"; + }; + fullName = lib.mkOption { + type = lib.types.str; + description = "Full name of the user"; + }; + sshKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "SSH public keys for the user"; + default = [ ]; + }; + }; + } + ); + description = "User information secrets"; + default = { }; + }; + + firewall = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + allowedTCPPorts = lib.mkOption { + type = lib.types.listOf lib.types.port; + description = "Allowed TCP ports for this host"; + default = [ ]; + # example = [ + # 22 + # 80 + # 443 + # ]; + }; + allowedTCPPortRanges = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + from = lib.mkOption { + type = lib.types.port; + description = "Starting port in range"; + }; + to = lib.mkOption { + type = lib.types.port; + description = "Ending port in range"; + }; + }; + } + ); + description = "Allowed TCP port ranges for this host"; + default = [ ]; + # example = [ + # { + # [REDACTED] + # [REDACTED] + # } + # ]; + }; + allowedUDPPorts = lib.mkOption { + type = lib.types.listOf lib.types.port; + description = "Allowed UDP ports for this host"; + default = [ ]; + # example = [ + # 53 + # 123 + # ]; + }; + allowedUDPPortRanges = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + from = lib.mkOption { + type = lib.types.port; + description = "Starting port in range"; + }; + to = lib.mkOption { + type = lib.types.port; + description = "Ending port in range"; + }; + }; + } + ); + description = "Allowed UDP port ranges for this host"; + default = [ ]; + # example = [ + # { + # [REDACTED] + # [REDACTED] + # } + # ]; + }; + }; + } + ); + description = "Firewall configuration by host"; + default = { }; + }; + }; + + config.secretsSpec.ssh.privateKeys = lib.mapAttrs ( + name: content: mkSshKeyFile name content + ) config.secretsSpec.ssh.privateKeyContents; +}