2021-04-20 17:53:25 -07:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
all-secrets = config.fudo.secrets;
|
|
|
|
|
|
|
|
encrypt-on-disk = name:
|
|
|
|
{ target-host, source-file }:
|
|
|
|
pkgs.stdenv.mkDerivation {
|
|
|
|
name = "${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 = name:
|
|
|
|
{ source-file, target-host, target-file, decrypt-key, user, group
|
|
|
|
, permissions }:
|
|
|
|
pkgs.writeShellScript "decrypt-fudo-secret-${name}.sh" ''
|
|
|
|
rm -rf ${target-file}
|
|
|
|
age -d -i ${decrypt-key} -o ${target-file} ${
|
|
|
|
encrypt-on-disk name { inherit source-file target-host; }
|
|
|
|
}
|
|
|
|
chown ${user}:${group} ${target-file}
|
|
|
|
chmod ${permissions} ${target-file}
|
|
|
|
'';
|
|
|
|
|
|
|
|
secret-service = name:
|
|
|
|
{ source-file, target-host, target-file, user, group, permissions
|
|
|
|
, key-type ? "ed25519" }: {
|
|
|
|
description = "decrypt secret ${name} for ${target-host}.";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "oneshot";
|
2021-04-21 10:34:46 -07:00
|
|
|
ExecStartPre = pkgs.writeShellScript "prepare-${name}-secret-dir.sh" ''
|
2021-04-20 17:53:25 -07:00
|
|
|
TARGET_DIR=$(dirname ${target-file})
|
|
|
|
if [[ ! -d "$TARGET_DIR" ]]; then
|
|
|
|
mkdir -p "$TARGET_DIR"
|
|
|
|
fi
|
|
|
|
'';
|
2021-04-21 10:34:46 -07:00
|
|
|
ExecStop = pkgs.writeShellScript "clear-${name}-secret.sh" ''
|
|
|
|
rm -f ${target-file}
|
|
|
|
'';
|
2021-04-20 17:53:25 -07:00
|
|
|
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 name {
|
|
|
|
inherit 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-host = mkOption {
|
|
|
|
type = str;
|
|
|
|
description =
|
|
|
|
"Host to which the secret belongs (determins SSH key to encrypt).";
|
|
|
|
};
|
|
|
|
|
|
|
|
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 (submodule secretOpts);
|
|
|
|
description = "Map of secrets to secret config.";
|
|
|
|
default = { };
|
|
|
|
};
|
|
|
|
|
|
|
|
config = {
|
|
|
|
systemd.services = let
|
|
|
|
hostname = config.instance.hostname;
|
|
|
|
host-secrets =
|
|
|
|
filterAttrs (secret: secretOpts: secretOpts.target-host == hostname)
|
|
|
|
all-secrets;
|
|
|
|
in mapAttrs' (secret: secretOpts:
|
|
|
|
(nameValuePair "fudo-secret-${secret}"
|
|
|
|
(secret-service secret secretOpts))) host-secrets;
|
|
|
|
};
|
|
|
|
}
|