Refactors SSH/GPG secrets handling
- Switches to per-user SSH configuration by retrieving user-specific secrets - Integrates GPG key creation and config into secret spec structure - Simplifies SSH config and key copying logic for better maintainability - Streamlines SMTP and firewall option mappings
This commit is contained in:
parent
a54522f072
commit
5695caa8cf
3 changed files with 151 additions and 109 deletions
|
@ -7,52 +7,35 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
# Generate local key paths for the config
|
||||
sshKeysMap = lib.mapAttrs (name: _: "${hostSpec.home}/.ssh/${name}") secretsSpec.ssh.privateKeys;
|
||||
## Get the current user's SSH config ##
|
||||
userSsh = secretsSpec.users.${hostSpec.user}.ssh;
|
||||
|
||||
# Create the SSH config file with local paths
|
||||
sshConfig = pkgs.writeText "ssh-config" ''
|
||||
Host git.ryot.foo
|
||||
IdentityFile ${sshKeysMap.git}
|
||||
|
||||
Host *
|
||||
ForwardAgent no
|
||||
AddKeysToAgent yes
|
||||
Compression no
|
||||
ServerAliveInterval 5
|
||||
ServerAliveCountMax 3
|
||||
HashKnownHosts no
|
||||
UserKnownHostsFile ~/.ssh/known_hosts
|
||||
ControlMaster no
|
||||
ControlPath ~/.ssh/master-%r@%n:%p
|
||||
ControlPersist no
|
||||
|
||||
IdentityFile ${sshKeysMap.pve}
|
||||
UpdateHostKeys ask
|
||||
'';
|
||||
## Generate local key paths for the config ##
|
||||
sshKeysMap = lib.mapAttrs (name: _: "${hostSpec.home}/.ssh/${name}") userSsh.privateKeys;
|
||||
in
|
||||
{
|
||||
home.file =
|
||||
{
|
||||
# SSH config file
|
||||
## SSH config file ##
|
||||
".ssh/config_source" = {
|
||||
source = sshConfig;
|
||||
source = userSsh.config;
|
||||
onChange = ''
|
||||
cp $HOME/.ssh/config_source $HOME/.ssh/config
|
||||
chmod 400 $HOME/.ssh/config
|
||||
'';
|
||||
};
|
||||
|
||||
## Known hosts ##
|
||||
".ssh/known_hosts_source" = {
|
||||
source = pkgs.writeText "known-hosts" (lib.concatStringsSep "\n" secretsSpec.ssh.knownHosts);
|
||||
source = pkgs.writeText "known-hosts" (lib.concatStringsSep "\n" userSsh.knownHosts);
|
||||
onChange = ''
|
||||
cp $HOME/.ssh/known_hosts_source $HOME/.ssh/known_hosts
|
||||
chmod 644 $HOME/.ssh/known_hosts
|
||||
'';
|
||||
};
|
||||
}
|
||||
# Dynamically add all SSH private keys using the existing store paths
|
||||
# Ensures the keys have correct permissions and are not symlinks
|
||||
|
||||
## Dynamically copy all SSH private keys from store ensuring symlinks are not used ##
|
||||
// lib.mapAttrs' (name: path: {
|
||||
name = ".ssh/${name}_source";
|
||||
value = {
|
||||
|
@ -62,5 +45,5 @@ in
|
|||
chmod 600 $HOME/.ssh/${name}
|
||||
'';
|
||||
};
|
||||
}) secretsSpec.ssh.privateKeys;
|
||||
}) userSsh.privateKeys;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
# Function to create a private key file in the Nix store with proper permissions
|
||||
## SSH key creation function ##
|
||||
mkSshKeyFile =
|
||||
name: content:
|
||||
pkgs.writeTextFile {
|
||||
|
@ -14,56 +14,24 @@ let
|
|||
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)
|
||||
'';
|
||||
};
|
||||
|
||||
## GPG key creation function ##
|
||||
mkGpgKeyFile =
|
||||
name: content:
|
||||
pkgs.writeTextFile {
|
||||
name = "gpg-key-${name}";
|
||||
text = content;
|
||||
executable = false;
|
||||
checkPhase = ''
|
||||
grep -q "BEGIN PGP PRIVATE KEY BLOCK" "$out" || (echo "Invalid GPG 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.listOf lib.types.str;
|
||||
description = "SSH known hosts entries";
|
||||
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 {
|
||||
|
@ -84,15 +52,74 @@ in
|
|||
type = lib.types.str;
|
||||
description = "Full name of the user";
|
||||
};
|
||||
sshKeys = lib.mkOption {
|
||||
|
||||
## SSH configuration ##
|
||||
ssh = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
publicKeys = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = "SSH public keys for the user";
|
||||
default = [ ];
|
||||
};
|
||||
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";
|
||||
readOnly = true;
|
||||
};
|
||||
config = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "SSH config file path";
|
||||
};
|
||||
knownHosts = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = "SSH known hosts entries";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = "SSH configuration for the user";
|
||||
};
|
||||
|
||||
## GPG configuration ##
|
||||
gpg = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
publicKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "GPG public key content";
|
||||
default = "";
|
||||
};
|
||||
privateKeyContents = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "GPG private key content";
|
||||
default = "";
|
||||
};
|
||||
privateKey = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "GPG private key file path";
|
||||
readOnly = true;
|
||||
};
|
||||
trust = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "GPG trust database content (base64)";
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = "GPG configuration for the user";
|
||||
};
|
||||
|
||||
## SMTP configuration ##
|
||||
smtp = lib.mkOption {
|
||||
type = lib.types.submodule (
|
||||
{ config, ... }:
|
||||
{
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
|
@ -116,8 +143,7 @@ in
|
|||
description = "Email address to send from";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
description = "SMTP configuration for the user";
|
||||
default = null;
|
||||
};
|
||||
|
@ -128,6 +154,7 @@ in
|
|||
default = { };
|
||||
};
|
||||
|
||||
## Firewall configurations by host ##
|
||||
firewall = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
|
@ -205,9 +232,41 @@ in
|
|||
description = "Firewall configuration by host";
|
||||
default = { };
|
||||
};
|
||||
|
||||
## API keys ##
|
||||
api = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
description = "API keys keyed by service name";
|
||||
default = { };
|
||||
};
|
||||
|
||||
config.secretsSpec.ssh.privateKeys = lib.mapAttrs (
|
||||
name: content: mkSshKeyFile name content
|
||||
) config.secretsSpec.ssh.privateKeyContents;
|
||||
## Docker environment variables ##
|
||||
docker = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.attrsOf lib.types.str);
|
||||
description = "Docker environment variables keyed by container name";
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config.secretsSpec.users = lib.mapAttrs (
|
||||
userName: userConfig:
|
||||
userConfig
|
||||
// {
|
||||
## Auto-generate SSH private key files ##
|
||||
ssh = userConfig.ssh // {
|
||||
privateKeys = lib.mapAttrs (
|
||||
name: content: mkSshKeyFile "${userName}-${name}" content
|
||||
) userConfig.ssh.privateKeyContents;
|
||||
};
|
||||
|
||||
## Auto-generate GPG private key file ##
|
||||
gpg = userConfig.gpg // {
|
||||
privateKey =
|
||||
if userConfig.gpg.privateKeyContents != "" then
|
||||
mkGpgKeyFile "${userName}-gpg" userConfig.gpg.privateKeyContents
|
||||
else
|
||||
null;
|
||||
};
|
||||
}
|
||||
) config.secretsSpec.users;
|
||||
}
|
||||
|
|
BIN
secrets.nix
BIN
secrets.nix
Binary file not shown.
Loading…
Add table
Reference in a new issue