{ config, pkgs, lib, ... }: with lib; let secretsDir = "/secrets/"; # TODO: make this an option secrets = (import ./make-secrets.nix { inherit config; }).activeSecrets; setupSecrets = concatStrings (mapAttrsToList (n: v: '' setupSecret ${n} ${v.user} ${v.group} ${v.permissions} ${optionalString (v.keyFile != null) (baseNameOf v.keyFile)} '') secrets); in { options.nix-bitcoin.setup-secrets = mkEnableOption "Set permissions for secrets generated by 'generate-secrets.sh'"; config = mkIf config.nix-bitcoin.setup-secrets { systemd.targets.nix-bitcoin-secrets = { requires = [ "setup-secrets.service" ]; after = [ "setup-secrets.service" ]; }; # Operation of this service: # - Create missing secrets that are composed of attrs from secrets.nix # - 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 = { serviceConfig = { Type = "oneshot"; RemainAfterExit = true; } // config.nix-bitcoin-services.defaultHardening; script = '' setupSecret() { file="$1" user="$2" group="$3" permissions="$4" srcFile="$5" if [[ ! -e $file ]]; then if [[ $srcFile ]]; then if [[ ! -e $srcFile ]]; then echo "Error: Secret source file '$srcFile' is missing" exit 1 fi mv "$srcFile" "$file" else createFile "$file" fi fi chown "$user:$group" "$file" chmod "$permissions" "$file" processedFiles+=("$file") } createFile() { file="$1" # 'nix eval' requires filesystem or daemon access to a store even if nothing is built. # Use a private store so that 'nix eval' always succeeds regardless of the # execution environment, like a container. # This tmp dir is automatically removed by systemd via PrivateTmp [[ $store ]] || store="$(mktemp -d)" secretsFile="$(realpath secrets.nix)" \ ${pkgs.nix}/bin/nix eval --raw --store "$store" "( (import ${./make-secrets.nix} { secretsFile = builtins.getEnv \"secretsFile\"; }).allSecrets.$file.text )" > "$file" } dir="${secretsDir}" if [[ ! -e $dir ]]; then echo "Error: Secrets dir '$dir' is missing" exit 1 fi chown root: "$dir" cd "$dir" processedFiles=() ${setupSecrets} # 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" ''; }; }; }