Watch
0
0
Fork
You've already forked hyperhive
0

swarm-nats-auth: read its own OIDC client secret from the store

The responder reads its OIDC client secret at
`swarm/services/<client-id>/oidc/client` with the store login it already
opens for the callout keys, and holds it in memory. `--client-secret-file`
is removed.

When introspection answers non-2xx, the responder re-reads the secret from
the store once and, if it changed, retries the introspection once. Both fit
inside the request's 1.5s introspection budget, under the server's 2s
callout timeout: the budget is now one deadline for the whole request. A
re-read that does not finish by then keeps running in the background, the
request is denied, and the next request introspects with what it read. One
re-read runs at a time; a refusal while one runs waits on it.

In swarm-nats.nix the `swarm-bao-nats-auth-oidc` fetch unit, the flag and
the `oidc-client.secret` LoadCredential entry are gone, along with the
unit's policy ordering in glue-bao-readers-policy-order.nix. A tmpfiles `r`
rule removes the copy a container still holds at
/var/lib/nixos-containers/swarm-nats/var/lib/swarm-nats-auth/oidc-client.secret;
nothing reads it.

No policy change: the `swarm-nats-auth` role already reads this path
(`natsAuthReaders` in swarm-bao.nix), which is the role the removed fetch
unit logged in as.

Deploy order: none, and no operator step. The store's grant already exists,
so the queue host can be deployed before, after or without the store's host
and authelia's. Rolling back re-creates the fetch unit and the file; the
store leaf is untouched.
This commit is contained in:
atlas 2026-10-06 13:43:14 +02:00
commit 6646329da5
12 changed files with 557 additions and 253 deletions

View file

@ -823,7 +823,7 @@ let
&& lib.elem "swarm-bao-matrix-ctl-policy.service" u.wants
&& !(lib.elem "swarm-bao-matrix-ctl-policy.service" u.requires)
&& !(noReaders ? swarm-bao-matrix-oidc)
&& !(noReaders ? swarm-bao-nats-auth-oidc);
&& !(noReaders ? swarm-bao-nats-callout-xkey);
}
{
# The ordering is set apart from each reader's own definition, so it can

View file

@ -347,8 +347,8 @@ let
}
{
# The responder names no seed file, by flag or by credential, and the
# copies a container may still hold are removed. The OIDC secret is the
# control that the credential list is read at all.
# copies a container may still hold are removed. The store identity is
# the control that the credential list is read at all.
name = "the responder takes no seed from a file, and the container's seed copies are removed";
ok =
let
@ -357,7 +357,7 @@ let
dir = "/var/lib/nixos-containers/swarm-nats/var/lib/swarm-nats-auth";
rules = natsWithStoreIdentity.systemd.tmpfiles.rules;
in
lib.elem "oidc-client.secret:/var/lib/swarm-nats-auth/oidc-client.secret" creds
lib.elem "bao-client.pem:/var/lib/swarm-nats-auth/bao-client.pem" creds
&& !(lib.any (lib.hasInfix ".seed") creds)
&& !(lib.hasInfix "seed-file" u.serviceConfig.ExecStart)
&& lib.elem "r ${dir}/issuer.seed" rules
@ -404,41 +404,31 @@ let
&& !(lib.any (path: refused natsWithStoreIdentity path) (lib.attrNames setting));
}
{
# The responder's OIDC secret has one route, the store, under the
# responder's own leaf. The copy unit's arms are the control: on a queue
# host without authelia nothing reads authelia's tree, and nothing
# copies the secret from any host file.
name = "a queue host without authelia reads the responder's OIDC secret from the store, and copies none from a host file";
# The responder reads its OIDC secret from the store itself, so no host
# unit fetches it, no credential or flag carries it, and a copy a
# container still holds is removed. On or off the store's host alike;
# the callout key fetch beside it is the control that the unit set is
# read at all, and the store cert role the control on the flags.
name = "the responder's OIDC secret is fetched by no unit and carried by no credential or flag, and the container's copy is removed";
ok =
let
services = natsWithStoreIdentity.systemd.services;
fetch = services.swarm-bao-nats-auth-oidc;
copy = services.swarm-nats-auth-secrets.script;
fetchedNowhere =
h:
!(h.systemd.services ? swarm-bao-nats-auth-oidc)
&& h.systemd.services ? swarm-bao-nats-callout-xkey;
u = responderOf natsWithStoreIdentity;
copy = natsWithStoreIdentity.systemd.services.swarm-nats-auth-secrets.script;
rules = natsWithStoreIdentity.systemd.tmpfiles.rules;
in
!natsWithStoreIdentity.services.hyperhive.deploy.authelia.enable
&& fetch.environment.BAO_CLIENT_CERT == "/etc/pki/nats-auth.pem"
&& fetch.environment.BAO_CLIENT_KEY == "/etc/pki/nats-auth-key.pem"
&& lib.hasInfix "bao kv get -field=value secret/swarm/services/swarm-nats/oidc/client" fetch.script
&& lib.hasInfix "/var/lib/nixos-containers/swarm-nats/var/lib/swarm-nats-auth/oidc-client.secret" fetch.script
&& lib.hasInfix "refresh_consumer swarm-nats swarm-nats-auth.service" fetch.script
# An unpublished or empty secret fails the unit, so `storeRetry` retries it.
&& !(lib.hasInfix "exit 0" fetch.script)
&& lib.elem "container@swarm-nats.service" fetch.before
fetchedNowhere natsWithStoreIdentity
&& fetchedNowhere natsOnStoreHost
&& !(lib.any (lib.hasInfix "oidc-client") u.serviceConfig.LoadCredential)
&& !(lib.hasInfix "client-secret" u.serviceConfig.ExecStart)
&& lib.hasInfix "--store-cert-role swarm-nats-auth" u.serviceConfig.ExecStart
&& lib.elem "r /var/lib/nixos-containers/swarm-nats/var/lib/swarm-nats-auth/oidc-client.secret" rules
&& !(lib.hasInfix "oidc-client" copy)
&& !(lib.hasInfix "authelia" copy);
}
{
# On the store's host the fetch logs in under the responder's leaf, so it
# waits for the unit writing that role.
name = "on the store's host the responder's OIDC secret fetch uses its leaf and waits for its role";
ok =
let
u = natsOnStoreHost.systemd.services.swarm-bao-nats-auth-oidc;
in
u.environment.BAO_CLIENT_CERT == "/var/lib/swarm-bao-pki/nats-auth.pem"
&& lib.elem "swarm-bao-nats-auth-policy.service" u.after
&& lib.elem "swarm-bao-nats-auth-policy.service" u.wants;
}
{
# On the store's host the glue pairs the responder with a leaf of its
# own, never the queue's TLS-issuing one or the hive's.