swarm-authelia: run the secret publisher and a journal forwarder inside the container
swarm-secret-publish and its path unit now run inside authelia's container, beside the mint, and read each client secret at the path authelia writes it. The host unit and host path unit, which read the secrets through the container's root on the host, are gone, and so is deploy.authelia.hostClientSecretDir, which only they read. The publisher keeps its store identity, the secret-publisher leaf. The host hands the leaf and the store's CA into the container as nspawn system credentials, and the unit loads them; where this host mints the leaf, the container waits for swarm-bao-pki. Authelia does not wait for the publisher, which retries every 30 seconds while the store is down. The container also runs the journal forwarder the other service containers run, pushing to a receiver of its own on the swarm collector (serviceProducers swarm-authelia) as a new machine OIDC client (swarm.authelia.otel.clientId) that authelia registers for itself and the publisher carries to the store. The fetch unit reads it back under the publisher's leaf, also handed to it as a system credential, so the swarm-secret-publisher policy gains read on that one leaf; its stanza keeps create and update, which the exact path would otherwise take from the services/* prefix. Without the leaf the forwarder is off. The container drops --link-journal=host, and the host collector stops reading it. The services' "nothing at <path>" errors name the publisher in authelia's container.
This commit is contained in:
parent
a2dca32bb8
commit
b68a8ef290
36 changed files with 753 additions and 363 deletions
|
|
@ -481,7 +481,7 @@ let
|
|||
secretPublisherPolicyName = "swarm-secret-publisher";
|
||||
secretPublisherCn = baoDeploy.secretPublisherCommonName;
|
||||
|
||||
# Three grants and one denial, and every narrowing in each is load-bearing.
|
||||
# Four grants and six denials, and every narrowing in each is load-bearing.
|
||||
#
|
||||
# `secret/data/` is KV v2's ACL prefix, inserted by the engine rather than
|
||||
# written by the caller — same trap as the controller's grant above.
|
||||
|
|
@ -494,8 +494,9 @@ let
|
|||
# here too — a grant widens when a path gains a WRITER, not when a kind is
|
||||
# declared.
|
||||
#
|
||||
# Write-only. It copies secrets in and never reads one back; a read
|
||||
# capability would let a file-copier recover every hive's credentials.
|
||||
# Write-only but for one leaf, the client secret of its container's forwarder:
|
||||
# a read on a prefix would let a file-copier recover every hive's credentials.
|
||||
# That stanza repeats `create`/`update`, which its exact path would drop.
|
||||
#
|
||||
# The queue callout's three seeds sit under `swarm/services/` and are no
|
||||
# secret this principal copies, so their exact paths, which outrank the
|
||||
|
|
@ -550,6 +551,10 @@ let
|
|||
path "${credentialMountPath}/data/${controllerClientLeaf}" {
|
||||
capabilities = ["create", "update"]
|
||||
}
|
||||
|
||||
path "${credentialMountPath}/data/swarm/services/${hyperhiveCfg.swarm.authelia.otel.clientId}/oidc/client" {
|
||||
capabilities = ["create", "update", "read"]
|
||||
}
|
||||
'';
|
||||
|
||||
# The identity the matrix container's `swarm-matrix-ctl` presents. Named outside `hive-*`
|
||||
|
|
@ -1448,10 +1453,10 @@ let
|
|||
# own and lands it in a collector's container — because that route is
|
||||
# proven and there is no second one worth inventing.
|
||||
#
|
||||
# Where the publisher on authelia's host leaves it. A swarm service's client
|
||||
# is registered once for the whole swarm, so its secret is one value. The
|
||||
# `swarm-forwarder-oidc` role's grant is this one path, so any other answers
|
||||
# 403 however correct it looks.
|
||||
# Where the publisher in authelia's container leaves it. A swarm service's
|
||||
# client is registered once for the whole swarm, so its secret is one value.
|
||||
# The `swarm-forwarder-oidc` role's grant is this one path, so any other
|
||||
# answers 403 however correct it looks.
|
||||
forwarderStoreSecretPath = "secret/swarm/services/${cfg.otel.clientId}/oidc/client";
|
||||
# On the host's /run (tmpfs), so the secret never reaches persistent disk:
|
||||
# after a reboot `swarm-bao-forwarder-oidc` fetches it again. The container
|
||||
|
|
@ -1770,7 +1775,9 @@ in
|
|||
controller's grant includes rewriting every hive's policy and login
|
||||
role, which is not something a unit whose whole job is copying one file
|
||||
should be able to do. This one's grant is `create`/`update` under the
|
||||
hive prefix and nothing else.
|
||||
hive and service prefixes and on the controller's client, and `read` on
|
||||
one leaf: the client secret of the journal forwarder in authelia's
|
||||
container, which presents this identity too.
|
||||
|
||||
⚠️ Same collision as its sibling above, and the same answer: ./swarm.nix
|
||||
feeds this value into the guard on
|
||||
|
|
@ -2967,9 +2974,8 @@ in
|
|||
# matters most and is the least obvious:
|
||||
# 1. `nixos-container` refuses to start when a bind source is missing,
|
||||
# and a certificate minted on this same boot does not exist yet when
|
||||
# the container is ordered. Same trap `hostClientSecretDir`
|
||||
# documents in ./swarm-authelia.nix. Ordering against whatever
|
||||
# mints it belongs with whatever named the path, not here.
|
||||
# the container is ordered. Ordering against whatever mints it
|
||||
# belongs with whatever named the path, not here.
|
||||
# 2. `tlsDir` is already mounted at the same path inside, so a copy
|
||||
# needs no second mount.
|
||||
# 3. A directory holding a leaf usually holds the CA's private key
|
||||
|
|
@ -3797,8 +3803,8 @@ in
|
|||
;;
|
||||
esac
|
||||
|
||||
# Published by ./swarm-secret-publisher.nix on authelia's host,
|
||||
# which this boot does not wait on, so absence is retried.
|
||||
# Published by the secret publisher in authelia's container, which
|
||||
# this boot does not wait on, so absence is retried.
|
||||
if ! secret="$(bao kv get -field=value ${lib.escapeShellArg "${credentialMountPath}/${baoUiClientLeaf}"} 2>"$err")"; then
|
||||
echo ${lib.escapeShellArg "the store did not return ${credentialMountPath}/${baoUiClientLeaf}: the UI's OIDC client secret is not published yet."} >&2
|
||||
cat "$err" >&2
|
||||
|
|
|
|||
Loading…
Reference in a new issue