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
|
let
|
||||||
# Generate local key paths for the config
|
## Get the current user's SSH config ##
|
||||||
sshKeysMap = lib.mapAttrs (name: _: "${hostSpec.home}/.ssh/${name}") secretsSpec.ssh.privateKeys;
|
userSsh = secretsSpec.users.${hostSpec.user}.ssh;
|
||||||
|
|
||||||
# Create the SSH config file with local paths
|
## Generate local key paths for the config ##
|
||||||
sshConfig = pkgs.writeText "ssh-config" ''
|
sshKeysMap = lib.mapAttrs (name: _: "${hostSpec.home}/.ssh/${name}") userSsh.privateKeys;
|
||||||
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
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.file =
|
home.file =
|
||||||
{
|
{
|
||||||
# SSH config file
|
## SSH config file ##
|
||||||
".ssh/config_source" = {
|
".ssh/config_source" = {
|
||||||
source = sshConfig;
|
source = userSsh.config;
|
||||||
onChange = ''
|
onChange = ''
|
||||||
cp $HOME/.ssh/config_source $HOME/.ssh/config
|
cp $HOME/.ssh/config_source $HOME/.ssh/config
|
||||||
chmod 400 $HOME/.ssh/config
|
chmod 400 $HOME/.ssh/config
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## Known hosts ##
|
||||||
".ssh/known_hosts_source" = {
|
".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 = ''
|
onChange = ''
|
||||||
cp $HOME/.ssh/known_hosts_source $HOME/.ssh/known_hosts
|
cp $HOME/.ssh/known_hosts_source $HOME/.ssh/known_hosts
|
||||||
chmod 644 $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: {
|
// lib.mapAttrs' (name: path: {
|
||||||
name = ".ssh/${name}_source";
|
name = ".ssh/${name}_source";
|
||||||
value = {
|
value = {
|
||||||
|
@ -62,5 +45,5 @@ in
|
||||||
chmod 600 $HOME/.ssh/${name}
|
chmod 600 $HOME/.ssh/${name}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}) secretsSpec.ssh.privateKeys;
|
}) userSsh.privateKeys;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
# Function to create a private key file in the Nix store with proper permissions
|
## SSH key creation function ##
|
||||||
mkSshKeyFile =
|
mkSshKeyFile =
|
||||||
name: content:
|
name: content:
|
||||||
pkgs.writeTextFile {
|
pkgs.writeTextFile {
|
||||||
|
@ -14,56 +14,24 @@ let
|
||||||
text = content;
|
text = content;
|
||||||
executable = false;
|
executable = false;
|
||||||
checkPhase = ''
|
checkPhase = ''
|
||||||
# Verify it's a valid SSH key (optional)
|
|
||||||
grep -q "BEGIN OPENSSH PRIVATE KEY" "$out" || (echo "Invalid SSH key format"; exit 1)
|
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
|
in
|
||||||
{
|
{
|
||||||
options.secretsSpec = {
|
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 {
|
users = lib.mkOption {
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
lib.types.submodule {
|
lib.types.submodule {
|
||||||
|
@ -84,15 +52,74 @@ in
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "Full name of the user";
|
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;
|
type = lib.types.listOf lib.types.str;
|
||||||
description = "SSH public keys for the user";
|
description = "SSH public keys for the user";
|
||||||
default = [ ];
|
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 {
|
smtp = lib.mkOption {
|
||||||
type = lib.types.submodule (
|
type = lib.types.submodule {
|
||||||
{ config, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
host = lib.mkOption {
|
host = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
@ -116,8 +143,7 @@ in
|
||||||
description = "Email address to send from";
|
description = "Email address to send from";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
);
|
|
||||||
description = "SMTP configuration for the user";
|
description = "SMTP configuration for the user";
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
@ -128,6 +154,7 @@ in
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## Firewall configurations by host ##
|
||||||
firewall = lib.mkOption {
|
firewall = lib.mkOption {
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
lib.types.submodule {
|
lib.types.submodule {
|
||||||
|
@ -205,9 +232,41 @@ in
|
||||||
description = "Firewall configuration by host";
|
description = "Firewall configuration by host";
|
||||||
default = { };
|
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 (
|
## Docker environment variables ##
|
||||||
name: content: mkSshKeyFile name content
|
docker = lib.mkOption {
|
||||||
) config.secretsSpec.ssh.privateKeyContents;
|
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