{ pkgs, lib, config, ... }: with lib; let cfg = config.security.systemd-creds; in { options.security.systemd-creds = { target = mkOption { type = types.str; description = '' Destination address of the target host able to encrypt the credentials. Used by the default {option}`security.systemd-creds.shell`. ''; default = "root@${config.networking.hostName}.${config.networking.domain}"; defaultText = mdLiteral "root@\${config.networking.hostName}.\${config.networking.domain}"; example = mdLiteral "''${config.networking.hostName}.wg"; }; decrypt = mkOption { type = with types; listOf str; description = mdDoc '' Command to get the cleartext of a credential. `credBase` is derived from the path of the credential in `LoadCredentialEncrypted=`, by removing the `builtins.storeDir` prefix and then one directory level. ''; apply = concatStringsSep " "; default = [ "gpg" "--batch" "--decrypt" "\${credBase%.cred}.gpg" ]; example = [ "pass" "\${credBase%.cred}" ]; }; shell = mkOption { type = with types; listOf str; description = mdDoc '' Command to get a shell on the target host. ''; apply = concatStringsSep " "; default = [ "ssh" "-o" "StrictHostKeyChecking=yes" "-o" "ControlMaster=auto" "-o" "ControlPersist=16s" "\"\${SYSTEMD_CREDS_TARGET:-${cfg.target}}\"" ]; defaultText = mdLiteral '' ssh -o StrictHostKeyChecking=yes \ -o ControlMaster=auto \ -o ControlPersist=16s \ \"''${SYSTEMD_CREDS_TARGET:-root@''${config.security.systemd-creds.target}}\" ''; example = [ "sudo" ]; }; encrypt = mkOption { type = with types; listOf str; description = mdDoc '' Command to run `systemd-creds encrypt` on the target host. ::: {.warning} Beware that the files `/etc/machine-id` and `/var/lib/systemd/credential.secret` on the target host, are both used to encrypt and decrypt when using the `host` key mechanism. Meaning that reinstalling the system on a new drive without restoring those two files will require to reencrypt the credentials. ::: ''; apply = concatStringsSep " "; default = [ "systemd-creds" "encrypt" "--name" "\"$credID\"" "--with-key=auto" ]; example = [ "sudo" "systemd-creds" "encrypt" "--with-key=host" ]; }; install = mkOption { type = with types; listOf str; apply = concatStringsSep " "; description = mdDoc '' Command to install the encrypted credential on the orchestrating host. ''; default = [ "install" "-D" "-m" "640" "/dev/stdin" "\"$credBase\"" ]; }; reencrypt = mkOption { type = types.lines; apply = pkgs.writeShellScriptBin "systemd-creds-encrypt-${replaceStrings ["@"] ["-"] cfg.target}"; description = mdDoc '' Encrypt credentials referenced in the `LoadCredentialEncrypted=` of enabled systemd services, by running `systemd-creds` on the {option}`security.systemd-creds.target` host. Only *non-existing* credential files are considered, unless the `SYSTEMD_CREDS_FORCE_REENCRYPT` envvar is set to a non-empty value. Credential directories are not supported. Example of use: ```console $ sudo wg genkey | gpg --encrypt --sign --recipient @$USER --output wireguard/wg-intra/privateKey.gpg $ git add wireguard/wg-intra/privateKey.gpg ``` ```nix { config, pkgs, lib, inputs, ... }: { systemd.services."wireguard-wg-intra".serviceConfig.LoadCredentialEncrypted = [ "privateKey:''${inputs.self}/wireguard/wg-intra/privateKey.cred" ]; } ``` ```console $ nix run .#nixosConfigurations.''${hostName}.config.security.systemd-creds.reencrypt $ git add wireguard/wg-intra/privateKey.cred ``` ::: {.warning} To be able to access the relative path of the `.cred` file, `inputs.self` has to be used in `LoadCredentialEncrypted=`. Note that `inputs` is a `config._module.args` or `specialArgs` usually set in your `flake.nix`. In other words, using `''${wireguard/wg-intra/privatekey}` here, would not work, because it drops the `wireguard/wg-intra/` part. ::: ''; }; }; config = { security.systemd-creds.reencrypt = '' shopt -s extglob globstar nullglob set -o pipefail set -eux '' + concatMapStringsSep "\n" (service: concatMapStringsSep "\n" (credential: let cred = splitString ":" credential; credID = elemAt cred 0; credPath = elemAt cred 1; in '' credID=${escapeShellArg credID} credPath=${escapeShellArg credPath} credBase=''${credPath#${builtins.storeDir}/*/} if test "''${SYSTEMD_CREDS_FORCE_REENCRYPT:+set}" \ -o ! -s "$credBase" \ -o -e "''${credBase%.cred}.gpg" -a "$credBase" -ot "''${credBase%.cred}.gpg"; then { ${cfg.decrypt}; } | { ${cfg.shell} -- ${cfg.encrypt} - -; } | { ${cfg.install}; } fi '' ) (toList service.serviceConfig.LoadCredentialEncrypted)) (attrValues (filterAttrs (_serviceName: service: service.enable && hasAttr "LoadCredentialEncrypted" service.serviceConfig ) config.systemd.services ) ); }; }