node: Support systemd credential for secret

While it is possible to use systemd credentials via

    LoadCredential=xyz.radicle.node.secret:…
    ExecStart=radicle-node … --secret "${CREDENTIALS_DIRECTORY}/xyz.radicle.node.secret"

Make usage more convenient and directly support passing the secret key
via a systemd credential. The ID of the credential must be

    xyz.radicle.node.secret

and is not user-configurable.

A systemd service unit file might contain:

    LoadCredential=xyz.radicle.node.secret:…
    ExecStart=radicle-node …

This requires just a bit of plumbing in `radicle-node`.

The preference order for the path of the secret key is:
 1. The command line argument `--secret`.
 2. The systemd credential.
 3. The configuration file.
 4. The default location to preserve backward compatibility.

The reason to prefer the systemd credential over the configuration file
is that it uses a mechanism that is influenced by the environment of the
process, which is deemed "closer at runtime" or "more dynamic" than a
configuration file. Ad-hoc overrides are still possible via the
commandline argument.
This commit is contained in:
Lorenz Leutgeb 2025-10-03 09:06:25 +02:00 committed by Fintan Halpenny
parent 0c513e981f
commit ae39f24b58
2 changed files with 21 additions and 2 deletions

View File

@ -15,6 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `rad issue` now uses `clap` to parse its command-line arguments.
This affects error reporting as well as help output.
- `radicle-node` now supports systemd Credentials (refer to
<https://systemd.io/CREDENTIALS> for more information) to load
the secret key, in addition to the commandline argument
`--secret` (higher priority than the credential) and the
configuration file (lower priority than the credential).
The identifier of the credential is "xyz.radicle.node.secret".
## Fixed Bugs

View File

@ -237,8 +237,21 @@ fn execute(options: Options) -> Result<(), ExecutionError> {
let passphrase = profile::env::passphrase();
let secret_path = options
.secret
let secret_path = options.secret;
#[cfg(all(feature = "systemd", target_os = "linux"))]
let secret_path = secret_path.or_else(|| {
const ID: &str = "xyz.radicle.node.secret";
match radicle_systemd::credential::path(ID) {
Err(err) => {
log::warn!(target: "node", "Failed to obtain path of the secret key via systemd credential with ID '{ID}': {err}");
None
},
Ok(path) => path
}
});
let secret_path = secret_path
.or_else(|| config.node.secret.clone())
.unwrap_or_else(|| home.keys().join("radicle"));