Working on secrets

This commit is contained in:
Niten 2021-04-22 11:38:52 -07:00
parent 5d572b3726
commit 962abd52e7
4 changed files with 98 additions and 68 deletions

View File

@ -15,11 +15,6 @@ let
in { in {
config = { config = {
# TODO: remove?
nixpkgs.config.permittedInsecurePackages = [
"openssh-with-gssapi-8.4p1" # CVE-2021-28041
];
networking = { networking = {
interfaces = { interfaces = {
enp1s0 = { useDHCP = true; }; enp1s0 = { useDHCP = true; };
@ -64,11 +59,10 @@ in {
network-definition = config.fudo.networks.${domain-name}; network-definition = config.fudo.networks.${domain-name};
}; };
secrets = { secrets.limina = {
backplane-client-limina-passwd = { backplane-client-passwd = {
source-file = /srv/secrets/backplane-client/limina.passwd; source-file = /srv/secrets/backplane-client/limina.passwd;
target-file = "/srv/backplane/dns/client.passwd"; target-file = "/srv/backplane/dns/client.passwd";
target-host = "limina";
user = config.fudo.client.dns.user; user = config.fudo.client.dns.user;
}; };
}; };
@ -80,7 +74,7 @@ in {
user = "fudo-client"; user = "fudo-client";
external-interface = "enp1s0"; external-interface = "enp1s0";
password-file = password-file =
config.fudo.secrets.backplane-client-limina-passwd.target-file; config.fudo.secrets.limina.backplane-client-passwd.target-file;
}; };
garbage-collector = { garbage-collector = {

View File

@ -1,14 +1,47 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let primary-ip = "10.0.0.21"; let
primary-ip = "10.0.0.21";
deploy-group = "nixops-deploy";
secrets-path = /srv/secrets;
in { in {
config = { config = {
users.groups = { ${deploy-group} = { members = [ "niten" ]; }; };
# TODO: remove? systemd = let secrets-watcher-name = "secrets-ownership-fixer";
nixpkgs.config.permittedInsecurePackages = [ in {
"openssh-with-gssapi-8.4p1" # CVE-2021-28041 paths.${secrets-watcher-name} = {
description = "Watch ${secrets-path} and correct perms on change.";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = secrets-path;
Unit = "${secrets-watcher-name}.service";
};
};
services.${secrets-watcher-name} = {
wantedBy = [ "multi-user.target" ];
description = "Correct perms on ${secrets-path}.";
serviceConfig = {
ExecStart = pkgs.writeShellScript "${secrets-watcher-name}.sh" ''
chown -R root:${deploy-group} ${secrets-path}
chmod -R ug=rX,o= ${secrets-path}
'';
};
};
tmpfiles.rules = [
"L /root/.gnupg - - - - /state/root/gnupg"
# "L /root/.emacs.d - - - - /state/root/emacs.d"
"L /root/.ssh/id_rsa - - - - /state/root/ssh/id_rsa"
"L /root/.ssh/id_rsa.pub - - - - /state/root/ssh/id_rsa.pub"
"L /root/.ssh/known_hosts - - - - /state/root/ssh/known_hosts"
"L /etc/ssh/ssh_host_ed25519_key - - - - /state/ssh/ssh_host_ed25519_key"
"L /etc/ssh/ssh_host_rsa_key - - - - /state/ssh/ssh_host_rsa_key"
]; ];
};
environment.etc = { environment.etc = {
nixos.source = "/state/nixos"; nixos.source = "/state/nixos";
@ -35,16 +68,6 @@ in {
Defaults lecture = never Defaults lecture = never
''; '';
systemd.tmpfiles.rules = [
"L /root/.gnupg - - - - /state/root/gnupg"
# "L /root/.emacs.d - - - - /state/root/emacs.d"
"L /root/.ssh/id_rsa - - - - /state/root/ssh/id_rsa"
"L /root/.ssh/id_rsa.pub - - - - /state/root/ssh/id_rsa.pub"
"L /root/.ssh/known_hosts - - - - /state/root/ssh/known_hosts"
"L /etc/ssh/ssh_host_ed25519_key - - - - /state/ssh/ssh_host_ed25519_key"
"L /etc/ssh/ssh_host_rsa_key - - - - /state/ssh/ssh_host_rsa_key"
];
networking = { networking = {
defaultGateway = { defaultGateway = {
address = "10.0.0.1"; address = "10.0.0.1";

View File

@ -71,13 +71,24 @@ in {
fudo = { fudo = {
hosts.procul.external-interfaces = [ "extif0" ]; hosts.procul.external-interfaces = [ "extif0" ];
secrets = { secrets.procul = {
backplane-client-limina-passwd = { backplane-client-passwd = {
source-file = /srv/secrets/backplane-client/procul.passwd; source-file = /srv/secrets/backplane-client/procul.passwd;
target-file = "/srv/backplane/dns/client.passwd"; target-file = "/srv/backplane/dns/client.passwd";
target-host = "procul";
user = config.fudo.client.dns.user; user = config.fudo.client.dns.user;
}; };
postgres-keytab = {
source-file = /srv/secrets/kerberos/procul-postgres.keytab;
target-file = "/srv/postgres/secure/postgres.keytab";
user = "root";
};
gitea-database-password = {
source-file = /srv/secrets/gitea/procul-database.passwd;
target-file = "/srv/gitea/secure/database.passwd";
user = config.fudo.git.user;
};
}; };
client.dns = { client.dns = {
@ -86,7 +97,8 @@ in {
ipv6 = true; ipv6 = true;
user = "fudo-client"; user = "fudo-client";
external-interface = "extif0"; external-interface = "extif0";
password-file = "/srv/backplane/dns/client.passwd"; password-file =
config.fudo.secrets.procul.backplane-client-passwd.target-file;
}; };
auth.kdc = { auth.kdc = {
@ -183,12 +195,13 @@ in {
enable = true; enable = true;
ssl-certificate = (acme-certificate host-fqdn); ssl-certificate = (acme-certificate host-fqdn);
ssl-private-key = (acme-private-key host-fqdn); ssl-private-key = (acme-private-key host-fqdn);
keytab = "/srv/postgres/secure/postgres.keytab"; keytab = config.fudo.secrets.procul.postgres-keytab.target-file;
local-networks = local-networks; local-networks = local-networks;
users = { users = {
gituser = { gituser = {
password-file = "/srv/git/secure/db.passwd"; password-file =
config.fudo.secrets.procul.gitea-database-password.target-file;
databases = { databases = {
git = { git = {
access = "CONNECT"; access = "CONNECT";
@ -213,7 +226,8 @@ in {
state-dir = /srv/git/state; state-dir = /srv/git/state;
database = { database = {
user = "gituser"; user = "gituser";
password-file = /srv/git/secure/db.passwd; password-file =
config.fudo.secrets.procul.gitea-database-password.target-file;
hostname = "127.0.0.1"; hostname = "127.0.0.1";
name = "git"; name = "git";
}; };

View File

@ -2,12 +2,9 @@
with lib; with lib;
let let
all-secrets = config.fudo.secrets; encrypt-on-disk = { secret-name, target-host, source-file }:
encrypt-on-disk = name:
{ target-host, source-file }:
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
name = "${name}-secret"; name = "${target-host}-${secret-name}-secret";
phases = "installPhase"; phases = "installPhase";
buildInputs = [ pkgs.age ]; buildInputs = [ pkgs.age ];
installPhase = let key = config.fudo.hosts.${target-host}.ssh-pubkey; installPhase = let key = config.fudo.hosts.${target-host}.ssh-pubkey;
@ -16,41 +13,42 @@ let
''; '';
}; };
decrypt-script = name: decrypt-script = { secret-name, source-file, target-host, target-file
{ source-file, target-host, target-file, decrypt-key, user, group , decrypt-key, user, group, permissions }:
, permissions }: pkgs.writeShellScript
pkgs.writeShellScript "decrypt-fudo-secret-${name}.sh" '' "decrypt-fudo-secret-${target-host}-${secret-name}.sh" ''
rm -rf ${target-file} rm -rf ${target-file}
age -d -i ${decrypt-key} -o ${target-file} ${ age -d -i ${decrypt-key} -o ${target-file} ${
encrypt-on-disk name { inherit source-file target-host; } encrypt-on-disk { inherit secret-name source-file target-host; }
} }
chown ${user}:${group} ${target-file} chown ${user}:${group} ${target-file}
chmod ${permissions} ${target-file} chmod ${permissions} ${target-file}
''; '';
secret-service = name: secret-service = target-host: secret-name:
{ source-file, target-host, target-file, user, group, permissions { source-file, target-file, user, group, permissions, key-type ? "ed25519"
, key-type ? "ed25519" }: { }: {
description = "decrypt secret ${name} for ${target-host}."; description = "decrypt secret ${secret-name} for ${target-host}.";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStartPre = pkgs.writeShellScript "prepare-${name}-secret-dir.sh" '' ExecStartPre = pkgs.writeShellScript
"prepare-${target-host}-${secret-name}-secret-dir.sh" ''
TARGET_DIR=$(dirname ${target-file}) TARGET_DIR=$(dirname ${target-file})
if [[ ! -d "$TARGET_DIR" ]]; then if [[ ! -d "$TARGET_DIR" ]]; then
mkdir -p "$TARGET_DIR" mkdir -p "$TARGET_DIR"
fi fi
''; '';
ExecStop = pkgs.writeShellScript "clear-${name}-secret.sh" '' ExecStop = pkgs.writeShellScript "clear-${secret-name}-secret.sh" ''
rm -f ${target-file} rm -f ${target-file}
''; '';
ExecStart = let ExecStart = let
decrypt-keys = decrypt-keys =
filter (key: key.type == key-type) config.services.openssh.hostKeys; filter (key: key.type == key-type) config.services.openssh.hostKeys;
decrypt-key = head (map (key: key.path) decrypt-keys); decrypt-key = head (map (key: key.path) decrypt-keys);
in decrypt-script name { in decrypt-script {
inherit source-file target-host target-file decrypt-key user group inherit secret-name source-file target-host target-file decrypt-key
permissions; user group permissions;
}; };
}; };
path = [ pkgs.age ]; path = [ pkgs.age ];
@ -63,12 +61,6 @@ let
description = "File from which to load the secret."; description = "File from which to load the secret.";
}; };
target-host = mkOption {
type = str;
description =
"Host to which the secret belongs (determins SSH key to encrypt).";
};
target-file = mkOption { target-file = mkOption {
type = str; type = str;
description = description =
@ -97,19 +89,26 @@ let
in { in {
options.fudo.secrets = with types; options.fudo.secrets = with types;
mkOption { mkOption {
type = attrsOf (submodule secretOpts); type = attrsOf (attrsOf (submodule secretOpts));
description = "Map of secrets to secret config."; description = "Map of hosts, to secrets, to secret config.";
default = { }; default = { };
example = {
my-host = {
my-host-secret = {
source-file = /path/to/file/on/this/host;
target-file = "/target/path/on/host";
user = "some-user";
};
};
};
}; };
config = { config = {
systemd.services = let systemd.services = let
hostname = config.instance.hostname; hostname = config.instance.hostname;
host-secrets = host-secrets = config.fudo.secrets.${hostname};
filterAttrs (secret: secretOpts: secretOpts.target-host == hostname)
all-secrets;
in mapAttrs' (secret: secretOpts: in mapAttrs' (secret: secretOpts:
(nameValuePair "fudo-secret-${secret}" (nameValuePair "fudo-secret-${hostname}-${secret}"
(secret-service secret secretOpts))) host-secrets; (secret-service hostname secret secretOpts))) host-secrets;
}; };
} }