From 914bb2e6dbe2094ca81e57234ff78ca713ff35a0 Mon Sep 17 00:00:00 2001 From: Elias Vasylenko Date: Sat, 20 Dec 2025 22:02:16 +0000 Subject: [PATCH] nixos/radicle: Update radicle to support systemd creds more completely via ImportCredential By default it will look for the cred names supported by the upstream package, but alternate cred names can be chosen to produce a rename on the ImportCredential. --- .../manual/release-notes/rl-2605.section.md | 2 + nixos/lib/systemd-unit-options.nix | 4 -- nixos/modules/services/misc/radicle.nix | 69 +++++++++++++------ nixos/tests/radicle-ci-broker.nix | 5 +- nixos/tests/radicle.nix | 5 +- pkgs/test/systemd/nixos/default.nix | 1 - 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index b20d1902c5c9..2c8db7efde8a 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -306,3 +306,5 @@ See . ``` **Do not set this globally!** This makes your setup inherently less secure. + +- `services.radicle` now supports importing the private key and passphrase as systemd creds. diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix index 5e85a9c2a1d9..8538c8519c53 100644 --- a/nixos/lib/systemd-unit-options.nix +++ b/nixos/lib/systemd-unit-options.nix @@ -3,12 +3,8 @@ let inherit (systemdUtils.lib) assertValueOneOf - automountConfig checkUnitConfig makeJobScript - mountConfig - serviceConfig - unitConfig unitNameType ; diff --git a/nixos/modules/services/misc/radicle.nix b/nixos/modules/services/misc/radicle.nix index 7979bd451b27..feb5eb1d7bb4 100644 --- a/nixos/modules/services/misc/radicle.nix +++ b/nixos/modules/services/misc/radicle.nix @@ -2,6 +2,7 @@ config, lib, pkgs, + utils, ... }: let @@ -15,6 +16,11 @@ let RAD_HOME = HOME; }; + credentials = { + privateKey = "xyz.radicle.node.secret"; + privateKeyPassphrase = "xyz.radicle.node.passphrase"; + }; + # Convenient wrapper to run `rad` in the namespaces of `radicle-node.service` rad-system = pkgs.writeShellScriptBin "rad-system" '' set -o allexport @@ -131,16 +137,34 @@ in services.radicle = { enable = lib.mkEnableOption "Radicle Seed Node"; package = lib.mkPackageOption pkgs "radicle-node" { }; - privateKeyFile = lib.mkOption { - # Note that a key encrypted by systemd-creds is not a path but a str. - type = with lib.types; either path str; + privateKey = lib.mkOption { + type = with lib.types; nullOr (either path str); + default = null; description = '' - Absolute file path to an SSH private key, + An SSH private key (as an absolute file path or Systemd credential name), usually generated by `rad auth`. - If it contains a colon (`:`) the string before the colon - is taken as the credential name - and the string after as a path encrypted with `systemd-creds`. + If set to the default value of `null`, radicle will import the private key from a credential + named `${credentials.privateKey}`. + + If configured as a credential name it will be imported via `ImportCredential=` in the service configuration. + Refer to the systemd-creds documentation for more details + ''; + }; + privateKeyPassphrase = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + description = '' + A passphrase for an SSH private key (as a Systemd credential name), + usually provided on generation of the key with `rad auth`. + + If set to the default value of `null`, radicle will optionally import the passphrase from a + credential named `${credentials.privateKeyPassphrase}`. + + If the passphrase is not set, radicle will prompt for it. + + If configured as a credential name it will be imported via `ImportCredential=` in the service configuration. + Refer to the systemd-creds documentation for more details ''; }; publicKey = lib.mkOption { @@ -304,23 +328,28 @@ in # Give only access to the private key to radicle-node. { serviceConfig = - let - keyCred = builtins.split ":" "${cfg.privateKeyFile}"; - in - if lib.length keyCred > 1 then + if cfg.privateKey == null then { - LoadCredentialEncrypted = [ cfg.privateKeyFile ]; - # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths= - BindReadOnlyPaths = [ - "/run/credentials/radicle-node.service/${lib.head keyCred}:${env.RAD_HOME}/keys/radicle" - ]; + ImportCredential = [ credentials.privateKey ]; + } + else if lib.types.path.check cfg.privateKey then + { + LoadCredential = [ "${credentials.privateKey}:${cfg.privateKey}" ]; } else { - LoadCredential = [ "radicle:${cfg.privateKeyFile}" ]; - BindReadOnlyPaths = [ - "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" - ]; + ImportCredential = [ "${cfg.privateKey}:${credentials.privateKey}" ]; + }; + } + { + serviceConfig = + if cfg.privateKeyPassphrase == null then + { + ImportCredential = [ credentials.privateKeyPassphrase ]; + } + else + { + ImportCredential = [ "${cfg.privateKeyPassphrase}:${credentials.privateKeyPassphrase}" ]; }; } ]; diff --git a/nixos/tests/radicle-ci-broker.nix b/nixos/tests/radicle-ci-broker.nix index 6fb0953daac8..19f48aa33fe0 100644 --- a/nixos/tests/radicle-ci-broker.nix +++ b/nixos/tests/radicle-ci-broker.nix @@ -19,9 +19,12 @@ in meta.maintainers = with lib.maintainers; [ defelo ]; nodes.seed = { + virtualisation.credentials = { + "xyz.radicle.node.secret".source = "${seed-ssh-keys.snakeOilEd25519PrivateKey}"; + }; + services.radicle = { enable = true; - privateKeyFile = seed-ssh-keys.snakeOilEd25519PrivateKey; publicKey = seed-ssh-keys.snakeOilEd25519PublicKey; node.openFirewall = true; settings = { diff --git a/nixos/tests/radicle.nix b/nixos/tests/radicle.nix index e802be63e83d..74ee577a1911 100644 --- a/nixos/tests/radicle.nix +++ b/nixos/tests/radicle.nix @@ -76,9 +76,12 @@ in { imports = [ commonHostConfig ]; + virtualisation.credentials = { + "xyz.radicle.node.secret".source = "${seed-ssh-keys.snakeOilEd25519PrivateKey}"; + }; + services.radicle = { enable = true; - privateKeyFile = seed-ssh-keys.snakeOilEd25519PrivateKey; publicKey = seed-ssh-keys.snakeOilEd25519PublicKey; node = { openFirewall = true; diff --git a/pkgs/test/systemd/nixos/default.nix b/pkgs/test/systemd/nixos/default.nix index 1590fd1a47fc..ff3fcd211307 100644 --- a/pkgs/test/systemd/nixos/default.nix +++ b/pkgs/test/systemd/nixos/default.nix @@ -1,7 +1,6 @@ { pkgs, lib, - stdenv, ... }: