Use systemd credential only when running as a systemd unit, defer to manual entry otherwise #1123

Closed
opened 2025-07-31 12:31:57 +00:00 by Afetzner · 6 comments

What I'd like to do and why

Borgmatic allows users to pass systemd credential instead of manually entering the repo passphrase by adding
encryption_passphrase: "{credential systemd borgmatic.pw}" to the config.yaml file. Now, when systemd invokes borgmatic, it pulls the repo password from a secure systemd credential file. However, when the user manually invokes Borgmatic, it will fail since it can't pull the password since it not running as a systemd unit. Ideally, the user would still be able to manually enter a password when manually invoking Borgmatic.

This issue is noted in the documentation under "provide-your-passwords":
"Be aware that when using this systemd {credential ...} feature, you may no longer be able to run certain borgmatic actions outside of the systemd service, as the credentials are only available from within the context of that service. So for instance, borgmatic list necessarily relies on the encryption_passphrase in order to access the Borg repository, but list shouldn't need to load any credentials for your database or monitoring hooks."

Other notes / implementation ideas

Adding the following three lines to borgmatic.hooks.credential.systemd.load_credential before checking for the credentials_directory is a crude check to see if the command is running as a systemd unit or from a user command:

is_systemd_unit = os.environ.get("INVOCATION_ID") is not None
if not is_systemd_unit:
return None

The code checks for an INVOCATION_ID environment variable, which is set by systemd. If the environment variable is defined, the process is probably a systemd unit. If the environment variable is not set, the process is not a systemd unit, the load_credential hook returns None before trying to read the credential file, and then defers to user input for the repo password

### What I'd like to do and why Borgmatic allows users to pass systemd credential instead of manually entering the repo passphrase by adding encryption_passphrase: "{credential systemd borgmatic.pw}" to the config.yaml file. Now, when systemd invokes borgmatic, it pulls the repo password from a secure systemd credential file. However, when the user manually invokes Borgmatic, it will fail since it can't pull the password since it not running as a systemd unit. Ideally, the user would still be able to manually enter a password when manually invoking Borgmatic. This issue is noted in the documentation under "provide-your-passwords": "Be aware that when using this systemd {credential ...} feature, you may no longer be able to run certain borgmatic actions outside of the systemd service, as the credentials are only available from within the context of that service. So for instance, borgmatic list necessarily relies on the encryption_passphrase in order to access the Borg repository, but list shouldn't need to load any credentials for your database or monitoring hooks." ### Other notes / implementation ideas Adding the following three lines to borgmatic.hooks.credential.systemd.load_credential before checking for the credentials_directory is a crude check to see if the command is running as a systemd unit or from a user command: is_systemd_unit = os.environ.get("INVOCATION_ID") is not None if not is_systemd_unit: return None The code checks for an INVOCATION_ID environment variable, which is set by systemd. If the environment variable is defined, the process is probably a systemd unit. If the environment variable is not set, the process is not a systemd unit, the load_credential hook returns None before trying to read the credential file, and then defers to user input for the repo password

I've just run into the same issue! Is there a known work-around, so I can run borgmatic list and validate my backups are working in the meantime?

I've just run into the same issue! Is there a known work-around, so I can run `borgmatic list` and validate my backups are working in the meantime?

Just to share a workaround I figured out: instead of using encryption_passphrase: "{credential systemd borgmatic.pw}", you can use:

encryption_passcommand: "systemd-creds decrypt /etc/credstore.encrypted/borgmatic.pw -"

This decrypts the password and outputs it to stdout. I've tested it manually (e.g. with borgmatic list) and via systemd (with systemctl start borgmatic.service. Will double check it runs fine on its own tomorrow morning, but it's looking promising!

(Note that the location of the credential file may differ on your system so just double check)

Just to share a workaround I figured out: instead of using `encryption_passphrase: "{credential systemd borgmatic.pw}"`, you can use: ```yaml encryption_passcommand: "systemd-creds decrypt /etc/credstore.encrypted/borgmatic.pw -" ``` This decrypts the password and outputs it to stdout. I've tested it manually (e.g. with `borgmatic list`) and via systemd (with `systemctl start borgmatic.service`. Will double check it runs fine on its own tomorrow morning, but it's looking promising! (Note that the location of the credential file may differ on your system so just double check)
Owner

Thanks for sharing the workaround. I may be able to update the systemd hook so that it automatically falls back to systemd-creds decrypt when CREDENTIALS_DIRECTORY is not set. This would have the benefit of not requiring the user to type in any credentials manually (as it sounds like is the case with the INVOCATION_ID check), but the downside is that it would probably only work when run as root. (Or I suppose it could detect whether it's run as root and add --user as appropriate, but that would only work if the credential was created as the same non-root user.)

Another caveat is that it would either have to hardcode the /etc/credstore.encrypted/ path (or make that default overridable with a configuration option).

Thanks for sharing the workaround. I may be able to update the systemd hook so that it automatically falls back to `systemd-creds decrypt` when `CREDENTIALS_DIRECTORY` is not set. This would have the benefit of not requiring the user to type in any credentials manually (as it sounds like is the case with the `INVOCATION_ID` check), but the downside is that it would probably only work when run as root. (Or I suppose it could detect whether it's run as root and add `--user` as appropriate, but *that* would only work if the credential was created as the same non-root user.) Another caveat is that it would either have to hardcode the `/etc/credstore.encrypted/` path (or make that default overridable with a configuration option).
Owner

Okay, this is implemented in main and will be part of the next release! As part of this feature, I added configuration options to override the default systemd-creds command and/or the /etc/credstore.encrypted directory. Please let me know how it works for you in practice.

Okay, this is implemented in main and will be part of the next release! As part of this feature, I added configuration options to override the default `systemd-creds` command and/or the `/etc/credstore.encrypted` directory. Please let me know how it works for you in practice.

Wow, amazing, thank you @witten! I'll try it out and let you know how I get on.

Wow, amazing, thank you @witten! I'll try it out and let you know how I get on.
Owner

Released in borgmatic 2.0.9!

Released in borgmatic 2.0.9!
Sign in to join this conversation.
No milestone
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
borgmatic-collective/borgmatic#1123
No description provided.