nix-bitcoin/modules/secrets/secrets.nix
Erik Arvstedt 55d87490ec
secrets: make configuration more robust
- Fail at evaluation when secrets setup is not configured.
  Previously, bitcoind failed at runtime due to the missing secrets target.

- Fail at evaluation when conflicting secrets setup methods are used.
  This happens when `secretsSetupMethod` has more than one definition.
2021-03-15 12:42:52 +00:00

153 lines
4.3 KiB
Nix

{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.nix-bitcoin;
in
{
options.nix-bitcoin = {
secretsDir = mkOption {
type = types.path;
default = "/etc/nix-bitcoin-secrets";
description = "Directory to store secrets";
};
setupSecrets = mkOption {
type = types.bool;
default = false;
description = ''
Set permissions for existing secrets in `nix-bitcoin.secretsDir`.
'';
};
generateSecrets = mkOption {
type = types.bool;
default = false;
description = ''
Automatically generate all required secrets.
Make sure to create a backup of the generated secrets.
'';
};
# Currently, this is used only by ../deployment/nixops.nix
deployment.secretsDir = mkOption {
type = types.path;
description = ''
Directory of local secrets that are transferred to the nix-bitcoin node on deployment
'';
};
secrets = mkOption {
default = {};
type = with types; attrsOf (submodule (
{ config, ... }: {
options = {
user = mkOption {
type = str;
default = "root";
};
group = mkOption {
type = str;
default = config.user;
};
permissions = mkOption {
type = str;
default = "0440";
};
};
}
));
};
secretsSetupMethod = mkOption {
type = types.str;
default = throw ''
Error: No secrets setup method has been defined.
To fix this, choose one of the following:
- Use one of the deployment methods in ${toString ./../deployment}
- Set `nix-bitcoin.generateSecrets = true` to automatically generate secrets
- Set `nix-bitcoin.secretsSetupMethod = "manual"` if you want to manually setup secrets
'';
};
};
config = {
# This target is active when secrets have been setup successfully.
systemd.targets.nix-bitcoin-secrets = mkIf (cfg.secretsSetupMethod != "manual") {
# This ensures that the secrets target is always activated when switching
# configurations.
# In this way `switch-to-configuration` is guaranteed to show an error
# when activating the secrets target fails on deployment.
wantedBy = [ "multi-user.target" ];
};
nix-bitcoin.setupSecrets = mkIf cfg.generateSecrets true;
nix-bitcoin.secretsSetupMethod = mkIf cfg.setupSecrets "setup-secrets";
# Operation of this service:
# - Set owner and permissions for all used secrets
# - Make all other secrets accessible to root only
# For all steps make sure that no secrets are copied to the nix store.
#
systemd.services.setup-secrets = mkIf cfg.setupSecrets {
requiredBy = [ "nix-bitcoin-secrets.target" ];
before = [ "nix-bitcoin-secrets.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
${optionalString cfg.generateSecrets ''
mkdir -p "${cfg.secretsDir}"
cd "${cfg.secretsDir}"
chown root: .
chmod 0700 .
${cfg.pkgs.generate-secrets}
''}
setupSecret() {
file="$1"
user="$2"
group="$3"
permissions="$4"
if [[ ! -e $file ]]; then
echo "Error: Secret file '$file' is missing"
exit 1
fi
chown "$user:$group" "$file"
chmod "$permissions" "$file"
processedFiles+=("$file")
}
dir="${cfg.secretsDir}"
if [[ ! -e $dir ]]; then
echo "Error: Secrets dir '$dir' is missing"
exit 1
fi
chown root: "$dir"
cd "$dir"
processedFiles=()
${
concatStrings (mapAttrsToList (n: v: ''
setupSecret ${n} ${v.user} ${v.group} ${v.permissions} }
'') cfg.secrets)
}
# Make all other files accessible to root only
unprocessedFiles=$(comm -23 <(printf '%s\n' *) <(printf '%s\n' "''${processedFiles[@]}" | sort))
IFS=$'\n'
chown root: $unprocessedFiles
chmod 0440 $unprocessedFiles
# Now make the secrets dir accessible to other users
chmod 0751 "$dir"
'';
};
};
}