nixos-config/lib/fudo/secrets.nix
2021-04-22 11:38:52 -07:00

115 lines
3.4 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
encrypt-on-disk = { secret-name, target-host, source-file }:
pkgs.stdenv.mkDerivation {
name = "${target-host}-${secret-name}-secret";
phases = "installPhase";
buildInputs = [ pkgs.age ];
installPhase = let key = config.fudo.hosts.${target-host}.ssh-pubkey;
in ''
age -a -r "${key}" -o $out ${source-file}
'';
};
decrypt-script = { secret-name, source-file, target-host, target-file
, decrypt-key, user, group, permissions }:
pkgs.writeShellScript
"decrypt-fudo-secret-${target-host}-${secret-name}.sh" ''
rm -rf ${target-file}
age -d -i ${decrypt-key} -o ${target-file} ${
encrypt-on-disk { inherit secret-name source-file target-host; }
}
chown ${user}:${group} ${target-file}
chmod ${permissions} ${target-file}
'';
secret-service = target-host: secret-name:
{ source-file, target-file, user, group, permissions, key-type ? "ed25519"
}: {
description = "decrypt secret ${secret-name} for ${target-host}.";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStartPre = pkgs.writeShellScript
"prepare-${target-host}-${secret-name}-secret-dir.sh" ''
TARGET_DIR=$(dirname ${target-file})
if [[ ! -d "$TARGET_DIR" ]]; then
mkdir -p "$TARGET_DIR"
fi
'';
ExecStop = pkgs.writeShellScript "clear-${secret-name}-secret.sh" ''
rm -f ${target-file}
'';
ExecStart = let
decrypt-keys =
filter (key: key.type == key-type) config.services.openssh.hostKeys;
decrypt-key = head (map (key: key.path) decrypt-keys);
in decrypt-script {
inherit secret-name source-file target-host target-file decrypt-key
user group permissions;
};
};
path = [ pkgs.age ];
};
secretOpts = { ... }: {
options = with types; {
source-file = mkOption {
type = path;
description = "File from which to load the secret.";
};
target-file = mkOption {
type = str;
description =
"Target file on the host; the secret will be decrypted to this file.";
};
user = mkOption {
type = str;
description = "User (on target host) to which the file will belong.";
};
group = mkOption {
type = str;
description = "Group (on target host) to which the file will belong.";
default = "nogroup";
};
permissions = mkOption {
type = str;
description = "Permissions to set on the target file.";
default = "0400";
};
};
};
in {
options.fudo.secrets = with types;
mkOption {
type = attrsOf (attrsOf (submodule secretOpts));
description = "Map of hosts, to secrets, to secret config.";
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 = {
systemd.services = let
hostname = config.instance.hostname;
host-secrets = config.fudo.secrets.${hostname};
in mapAttrs' (secret: secretOpts:
(nameValuePair "fudo-secret-${hostname}-${secret}"
(secret-service hostname secret secretOpts))) host-secrets;
};
}