nixos-config/lib/fudo/secrets.nix

170 lines
5.1 KiB
Nix
Raw Normal View History

2021-04-20 17:53:25 -07:00
{ config, lib, pkgs, ... }:
with lib;
let
2021-04-29 21:39:21 -07:00
cfg = config.fudo.secrets;
2021-09-30 08:40:47 -07:00
encrypt-on-disk = { secret-name, target-host, target-pubkey, source-file }:
2021-04-20 17:53:25 -07:00
pkgs.stdenv.mkDerivation {
2021-04-22 11:38:52 -07:00
name = "${target-host}-${secret-name}-secret";
2021-04-20 17:53:25 -07:00
phases = "installPhase";
buildInputs = [ pkgs.age ];
2021-09-30 08:40:47 -07:00
installPhase = ''
age -a -r "${target-pubkey}" -o $out ${source-file}
2021-04-20 17:53:25 -07:00
'';
};
2021-04-22 11:38:52 -07:00
decrypt-script = { secret-name, source-file, target-host, target-file
2021-09-30 08:40:47 -07:00
, host-master-key, user, group, permissions }:
2021-04-22 11:38:52 -07:00
pkgs.writeShellScript
"decrypt-fudo-secret-${target-host}-${secret-name}.sh" ''
2021-04-20 17:53:25 -07:00
rm -rf ${target-file}
2021-09-30 08:40:47 -07:00
age -d -i ${host-master-key.key-path} -o ${target-file} ${
encrypt-on-disk {
inherit secret-name source-file target-host;
target-pubkey = host-master-key.public-key;
}
2021-04-20 17:53:25 -07:00
}
chown ${user}:${group} ${target-file}
chmod ${permissions} ${target-file}
'';
2021-04-22 11:38:52 -07:00
secret-service = target-host: secret-name:
2021-09-30 08:40:47 -07:00
{ source-file, target-file, user, group, permissions }: {
2021-04-22 11:38:52 -07:00
description = "decrypt secret ${secret-name} for ${target-host}.";
2021-04-20 17:53:25 -07:00
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
2021-04-22 11:38:52 -07:00
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
'';
2021-04-20 17:53:25 -07:00
ExecStart = let
2021-09-30 08:40:47 -07:00
host-master-key = config.fudo.hosts.${target-host}.master-key;
2021-04-22 11:38:52 -07:00
in decrypt-script {
2021-09-30 08:40:47 -07:00
inherit secret-name source-file target-host target-file host-master-key
2021-04-22 11:38:52 -07:00
user group permissions;
2021-04-20 17:53:25 -07:00
};
};
path = [ pkgs.age ];
};
secretOpts = { ... }: {
options = with types; {
source-file = mkOption {
2021-09-30 08:40:47 -07:00
type = path; # CAREFUL: this will copy the file to nixstore...keep on deploy host
2021-04-20 17:53:25 -07:00
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";
};
};
};
2021-04-29 21:39:21 -07:00
nix-build-users = let usernames = attrNames config.users.users;
in filter (user: (builtins.match "^nixbld[0-9]{1,2}$" user) != null)
usernames;
2021-04-20 17:53:25 -07:00
in {
2021-04-29 21:39:21 -07:00
options.fudo.secrets = with types; {
2021-07-20 18:28:12 -07:00
enable = mkOption {
type = bool;
description = "Include secrets in the build (disable when secrets are unavailable)";
default = true;
};
2021-04-29 21:39:21 -07:00
host-secrets = mkOption {
2021-04-22 11:38:52 -07:00
type = attrsOf (attrsOf (submodule secretOpts));
2021-04-29 21:39:21 -07:00
description = "Map of hosts to host secrets";
2021-04-20 17:53:25 -07:00
default = { };
};
2021-04-29 21:39:21 -07:00
secret-users = mkOption {
type = listOf str;
description = "List of users with read-access to secrets.";
default = [ ];
};
secret-group = mkOption {
type = str;
description = "Group to which secrets will belong.";
default = "nixops-secrets";
};
secret-paths = mkOption {
type = listOf str;
description =
"Paths which contain (only) secrets. The contents will be reabable by the secret-group.";
default = [ ];
};
};
2021-07-20 18:28:12 -07:00
config = mkIf cfg.enable {
2021-04-29 21:39:21 -07:00
users.groups = {
${cfg.secret-group} = { members = cfg.secret-users ++ nix-build-users; };
};
systemd = let
2021-04-20 17:53:25 -07:00
hostname = config.instance.hostname;
2021-04-29 21:39:21 -07:00
host-secrets = if (hasAttr hostname cfg.host-secrets) then
cfg.host-secrets.${hostname}
2021-04-22 12:03:44 -07:00
else
{ };
2021-04-29 21:39:21 -07:00
host-secret-services = mapAttrs' (secret: secretOpts:
(nameValuePair "fudo-secret-${hostname}-${secret}"
(secret-service hostname secret secretOpts))) host-secrets;
in {
services = host-secret-services // {
fudo-secrets-watcher = {
wantedBy = [ "multi-user.target" ];
description =
"Ensure access for group ${cfg.secret-group} to fudo secret paths.";
serviceConfig = {
ExecStart = pkgs.writeShellScript "fudo-secrets-watcher.sh"
(concatStringsSep "\n" (map (path: ''
chown -R root:${cfg.secret-group} ${path}
chmod -R u=rwX,g=rX,o= ${path}
'') cfg.secret-paths));
};
};
};
paths.fudo-secrets-watcher = mkIf ((length cfg.secret-paths) > 0) {
wantedBy = [ "multi-user.target" ];
description = "Watch fudo secret paths, and correct perms on changes.";
pathConfig = {
PathChanged = cfg.secret-paths;
Unit = "fudo-secrets-watcher.service";
};
};
tmpfiles.rules = map (path: "d '${path}' - root ${cfg.secret-group} - -")
cfg.secret-paths;
};
2021-04-20 17:53:25 -07:00
};
}