Watch
0
0
Fork
You've already forked hyperhive
0

drop the one-time migration: seed the store from a leftover file once, leave the file

A leftover plaintext client secret is stored once with -cas=0 when the store holds none, so nothing rotates. A store value wins over a different file. The file is no longer read back, compared or removed, and the migration unit is gone.
This commit is contained in:
atlas 2026-10-30 16:12:37 +01:00 • committed by mara
commit fcc9c8d0ba

View file

@ -323,7 +323,6 @@ let
onStdin = s: puts s != [ ] && lib.all (l: lib.hasInfix "value=-" l) (puts s);
in
onStdin (inAuthelia secretPublisherHere).systemd.services.authelia-swarm-secrets.script
&& onStdin (inAuthelia secretPublisherHere).systemd.services.authelia-swarm-client-secrets.script
&& !(onStdin "printf '%s' \"$secret\" | bao kv put -cas=0 x value=\"$secret\"")
&& !(onStdin "# kv put x value=-\nbao kv put x value=@/tmp/f")
&& onStdin "printf '%s' \"$secret\" | bao kv put -cas=0 x value=-";
@ -369,8 +368,6 @@ let
&& !(lib.hasInfix "bao kv" (lib.head (lib.splitString "bao login" c)));
in
holdsTokenFirst (inAuthelia secretPublisherHere).systemd.services.authelia-swarm-secrets.script
&& holdsTokenFirst (inAuthelia secretPublisherHere)
.systemd.services.authelia-swarm-client-secrets.script
&& holdsTokenFirst (inAuthelia secretPublisherHere).systemd.services.swarm-authelia-bridge.preStart
&& holdsTokenFirst baoWithMatrix.systemd.services.swarm-bao-matrix-oidc.script
# Controls, so a clean verdict above means something. In order: a bare
@ -389,24 +386,24 @@ let
# would have left the unit rendering only on the one deployment that has
# no use for it. It runs beside the mint, in authelia's container, and
# never on the host.
name = "the client secret migration holding an identity runs in authelia's container on a host with no store";
name = "the mint holding an identity runs in authelia's container on a host with no store";
ok =
let
m = secretPublisherRemote;
c = inAuthelia m;
in
!m.services.hyperhive.deploy.bao.enable
&& (c.systemd.services ? authelia-swarm-client-secrets)
&& !(m.systemd.services ? authelia-swarm-client-secrets)
&& lib.hasInfix "mint_client_secret" c.systemd.services.authelia-swarm-secrets.script
&& !(m.systemd.services ? authelia-swarm-secrets)
# Control: the host's own unit set is the one read above.
&& (m.systemd.services ? "container@swarm-authelia");
}
{
# What makes the arm above able to fail. Without a certificate the
# plaintext has no store to go to, so the mint keeps it in a file beside
# the digest as before, the migration does not exist, the bridge reads
# the digest as before, the bridge reads
# that file, and nspawn gets no credential to load.
name = "an IdP host with no store identity keeps the client secrets in files and renders no migration";
name = "an IdP host with no store identity keeps the client secrets in files";
ok =
let
m = secretPublisherNoIdentity;
@ -414,7 +411,6 @@ let
mint = c.systemd.services.authelia-swarm-secrets.script;
in
m.services.hyperhive.deploy.authelia.enable
&& !(c.systemd.services ? authelia-swarm-client-secrets)
&& lib.hasInfix "mint() {" mint
&& !(lib.hasInfix "mint_client_secret" mint)
&&
@ -423,7 +419,6 @@ let
&& !(c.systemd.services.swarm-authelia-bridge.serviceConfig ? RuntimeDirectory)
&& !(lib.any (lib.hasPrefix "--load-credential=") m.containers.swarm-authelia.extraFlags)
# Controls: with an identity, the same lookups find the other shape.
&& ((inAuthelia secretPublisherHere).systemd.services ? authelia-swarm-client-secrets)
&& lib.hasInfix "mint_client_secret" (
(inAuthelia secretPublisherHere).systemd.services.authelia-swarm-secrets.script
)
@ -456,20 +451,16 @@ let
&& lib.hasInfix "client_secret_read ${leaf}" bridge.preStart
&& lib.hasInfix "mint_client_secret swarm-authelia-bridge ${leaf}" here.systemd.services.authelia-swarm-secrets.script
&& lib.elem "publisher-bao-cert" bridge.serviceConfig.LoadCredential
&& bridge.serviceConfig.Restart == "on-failure"
# The file it reads from is the one the earlier release left, until the
# migration moves it into the store.
&& lib.hasInfix "legacy=/var/lib/authelia-swarm/oidc-clients/swarm-authelia-bridge.secret" bridge.preStart;
&& bridge.serviceConfig.Restart == "on-failure";
}
{
# Every non-native client is migrated and minted at the leaf its reader
# Every non-native client is minted at the leaf its reader
# fetches, not only the ones with a reader in the publisher's list: the
# bridge's own, which only the bridge reads, is the one that list lacks.
name = "the migration visits every client at the leaf the mint creates, and a failure that no retry changes is not retried";
name = "the mint stores every client at the leaf its reader fetches";
ok =
let
c = inAuthelia secretPublisherHere;
migrate = c.systemd.services.authelia-swarm-client-secrets;
mint = c.systemd.services.authelia-swarm-secrets;
pairs = [
"hive-h1 secret/swarm/hives/h1/oidc/client"
@ -479,19 +470,11 @@ let
"swarm-authelia-bridge secret/swarm/services/swarm-authelia-bridge/oidc/client"
];
in
lib.all (
p:
lib.hasInfix "migrate_client_secret ${p}" migrate.script
&& lib.hasInfix "mint_client_secret ${p}" mint.script
) pairs
# A native client is public: it has no secret to mint or move.
&& !(lib.hasInfix "swarmctl" migrate.script)
lib.all (p: lib.hasInfix "mint_client_secret ${p}" mint.script) pairs
# A native client is public: it has no secret to mint.
&& !(lib.hasInfix "mint_client_secret swarmctl" mint.script)
&& migrate.serviceConfig.RestartPreventExitStatus == 3
# The mint answers authelia's password prompt through a pty.
&& lib.any (p: lib.hasPrefix "util-linux" (p.name or "")) mint.path
&& migrate.serviceConfig.Restart == "on-failure"
&& migrate.serviceConfig.Type == "oneshot";
&& lib.any (p: lib.hasPrefix "util-linux" (p.name or "")) mint.path;
}
{
# Every key authelia signs or encrypts with comes from the store, seeded
@ -532,8 +515,8 @@ let
name = "the store identity reaches authelia's container as system credentials";
ok =
let
dir = "/run/credentials/authelia-swarm-client-secrets.service";
unit = m: (inAuthelia m).systemd.services.authelia-swarm-client-secrets;
dir = "/run/credentials/authelia-swarm-secrets.service";
unit = m: (inAuthelia m).systemd.services.authelia-swarm-secrets;
flags = m: m.containers.swarm-authelia.extraFlags;
here = unit secretPublisherHere;
in
@ -566,33 +549,6 @@ let
)
&& (unit secretPublisherRemote).environment.BAO_CLIENT_CERT == "${dir}/publisher-bao-cert";
}
{
# Authelia is the swarm's IdP and has to come up while the store is down.
# Nothing it starts after or needs may be the migration, and the
# migration may not order itself in front of authelia.
name = "authelia does not wait for the client secret migration";
ok =
let
c = inAuthelia secretPublisherHere;
authelia = c.systemd.services.authelia-swarm;
publisher = c.systemd.services.authelia-swarm-client-secrets;
unit = "authelia-swarm-client-secrets.service";
in
!(lib.any (l: lib.elem unit (authelia.${l} or [ ])) [
"after"
"requires"
"wants"
"bindsTo"
"requisite"
])
&& !(lib.any (l: lib.elem "authelia-swarm.service" (publisher.${l} or [ ])) [
"before"
"requiredBy"
])
# Controls: both lookups find the real units.
&& authelia.serviceConfig ? ExecStart
&& lib.elem "authelia-swarm-secrets.service" publisher.after;
}
];
in
runGroup "secret-publisher" cases