From fbb20a56e84a2c6585b4f91628fb49363ab4a2c1 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 16 Aug 2026 22:53:50 +0200 Subject: [PATCH] fix: SetCredential with an empty value is dropped by systemd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `swarm-controller.service` carried `SetCredential=queue-client.secret:` — an empty value, which systemd's parser refuses: /etc/systemd/system/swarm-controller.service:38: Invalid syntax, ignoring: queue-client.secret: So the line was dropped on every daemon-reload, and `LoadCredential=` was fatal again — precisely the failure that default was added to prevent. On a hive where authelia has not yet minted the secret, the controller refuses to start rather than coming up with the queue unconfigured. It looked correct for days because the credential file happened to exist, so the fail-soft was never exercised. Measured with `systemd-analyze verify`: an empty value is rejected, any non-empty one is accepted. The placeholder is a real word rather than filler — it reaches the token request as the client secret, so authelia refuses it and the journal names something an operator can act on. Gate: state/eval-setcredential.sh, with the parent commit as its mutation. --- nix/host-modules/swarm-controller.nix | 29 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index a17d1242..90c2ca67 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -464,17 +464,28 @@ in ] ++ lib.optional (cfg.forgeTokenFile != null) "forge-token:${cfg.forgeTokenFile}"; - # The empty default that makes the above non-fatal. `LoadCredential=` - # takes priority over `SetCredential=`, so this is only ever seen - # when the file is missing — and in that case systemd starts the unit - # instead of refusing to. The controller then serves its HTTP surface - # with the queue unconfigured, which is a supported shape it already - # knows how to report. + # The placeholder default that makes the above non-fatal. + # `LoadCredential=` takes priority over `SetCredential=`, so this is + # only ever seen when the file is missing — and in that case systemd + # starts the unit instead of refusing to. The controller then serves + # its HTTP surface with the queue unconfigured, which is a supported + # shape it already knows how to report. # - # Safe to put in a unit file precisely because it carries nothing: + # ⚠️ THE VALUE MUST BE NON-EMPTY. `SetCredential=:` with an empty + # value is rejected by systemd's parser — *"Invalid syntax, ignoring"* + # — so the whole line is dropped and the fail-soft above silently does + # not exist. Measured with `systemd-analyze verify`: empty is refused, + # any non-empty value is accepted. This shipped broken and only looked + # fine because the credential file happened to be present. + # + # The word is deliberate rather than arbitrary: it reaches the token + # request as the client secret, so authelia refuses it and the journal + # says so in terms an operator can act on. + # + # Safe in a unit file precisely because it is not a secret: # `SetCredential=` values are readable by unprivileged processes over - # IPC, so a real secret must never appear here. - SetCredential = [ "queue-client.secret:" ]; + # IPC, so real key material must never appear here. + SetCredential = [ "queue-client.secret:placeholder-no-secret-file" ]; User = "swarm-controller"; Group = "swarm-controller"; Restart = "on-failure";