secrets: migrate all hosts from sops to OpenBao vault
Replace sops-nix secrets with OpenBao vault secrets across all hosts. Hardcode root password hash, add extractKey option to vault-secrets module, update Terraform with secrets/policies for all hosts, and create AppRole provisioning playbook. Hosts migrated: ha1, monitoring01, ns1, ns2, http-proxy, nix-cache01 Wave 1 hosts (nats1, jelly01, pgdb1) get AppRole policies only. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
{ pkgs, config, ... }: {
|
||||
{ pkgs, config, ... }:
|
||||
{
|
||||
programs.zsh.enable = true;
|
||||
sops.secrets.root_password_hash = { };
|
||||
sops.secrets.root_password_hash.neededForUsers = true;
|
||||
|
||||
users.users.root = {
|
||||
shell = pkgs.zsh;
|
||||
hashedPasswordFile = config.sops.secrets.root_password_hash.path;
|
||||
hashedPassword = "$y$j9T$N09APWqKc4//z9BoGyzSb0$3dMUzojSmo3/10nbIfShd6/IpaYoKdI21bfbWER3jl8";
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwfb2jpKrBnCw28aevnH8HbE5YbcMXpdaVv2KmueDu6 torjus@gunter"
|
||||
];
|
||||
|
||||
@@ -73,6 +73,16 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
extractKey = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Extract a single key from the vault secret JSON and write it as a
|
||||
plain file instead of a directory of files. When set, outputDir
|
||||
becomes a file path rather than a directory path.
|
||||
'';
|
||||
};
|
||||
|
||||
services = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
@@ -152,14 +162,35 @@ in
|
||||
RemainAfterExit = true;
|
||||
|
||||
# Fetch the secret
|
||||
ExecStart = pkgs.writeShellScript "fetch-${name}" ''
|
||||
ExecStart = pkgs.writeShellScript "fetch-${name}" (''
|
||||
set -euo pipefail
|
||||
|
||||
# Set Vault environment variables
|
||||
export VAULT_ADDR="${cfg.vaultAddress}"
|
||||
export VAULT_SKIP_VERIFY="${if cfg.skipTlsVerify then "1" else "0"}"
|
||||
'' + (if secretCfg.extractKey != null then ''
|
||||
# Fetch to temporary directory, then extract single key
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap "rm -rf $TMPDIR" EXIT
|
||||
|
||||
# Fetch secret using vault-fetch
|
||||
${vault-fetch}/bin/vault-fetch \
|
||||
"${secretCfg.secretPath}" \
|
||||
"$TMPDIR" \
|
||||
"${secretCfg.cacheDir}"
|
||||
|
||||
# Extract the specified key and write as a single file
|
||||
if [ ! -f "$TMPDIR/${secretCfg.extractKey}" ]; then
|
||||
echo "ERROR: Key '${secretCfg.extractKey}' not found in secret" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure parent directory exists
|
||||
mkdir -p "$(dirname "${secretCfg.outputDir}")"
|
||||
cp "$TMPDIR/${secretCfg.extractKey}" "${secretCfg.outputDir}"
|
||||
chown ${secretCfg.owner}:${secretCfg.group} "${secretCfg.outputDir}"
|
||||
chmod ${secretCfg.mode} "${secretCfg.outputDir}"
|
||||
'' else ''
|
||||
# Fetch secret as directory of files
|
||||
${vault-fetch}/bin/vault-fetch \
|
||||
"${secretCfg.secretPath}" \
|
||||
"${secretCfg.outputDir}" \
|
||||
@@ -168,7 +199,7 @@ in
|
||||
# Set ownership and permissions
|
||||
chown -R ${secretCfg.owner}:${secretCfg.group} "${secretCfg.outputDir}"
|
||||
chmod ${secretCfg.mode} "${secretCfg.outputDir}"/*
|
||||
'';
|
||||
''));
|
||||
|
||||
# Logging
|
||||
StandardOutput = "journal";
|
||||
@@ -216,7 +247,10 @@ in
|
||||
[ "d /run/secrets 0755 root root -" ] ++
|
||||
[ "d /var/lib/vault/cache 0700 root root -" ] ++
|
||||
flatten (mapAttrsToList (name: secretCfg: [
|
||||
"d ${secretCfg.outputDir} 0755 root root -"
|
||||
# When extractKey is set, outputDir is a file path - create parent dir instead
|
||||
(if secretCfg.extractKey != null
|
||||
then "d ${dirOf secretCfg.outputDir} 0755 root root -"
|
||||
else "d ${secretCfg.outputDir} 0755 root root -")
|
||||
"d ${secretCfg.cacheDir} 0700 root root -"
|
||||
]) cfg.secrets);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user