swarm-logs: authenticate as the agent with its own queue secret
swarm-logs reads `swarm/agents/<agent>/queue` from the swarm secret store with the agent's own store certificate (the read hive-agent makes for its queue connection) and presents it as `Bearer swarm-agent.<agent>.<secret>`, which the gateway's swarm-logs-auth checks. On a 401 it reads the secret again and retries once, so a secret re-minted between the read and the check does not fail the query. The OIDC mint against the hive's shared `hive-<h>-agent` client is gone from swarm-logs, along with the unused `HIVE_AGENT_OIDC_CA_FILE` anchor. logs.nix now gates on the store address instead of the queue's token endpoint and wraps the binary with the paths queue-identity.nix gives the harness; the host forwards the query URL wherever it forwards a store identity. The shared client is still delivered into containers; removing that is Part of #4459.
This commit is contained in:
parent
1f4736e79b
commit
0f53e9be6c
12 changed files with 260 additions and 331 deletions
|
|
@ -20,40 +20,19 @@ attach at the gateway location, not in this binary.
|
|||
|
||||
## How it authenticates
|
||||
|
||||
The agent container already holds one identity, the per-hive machine client
|
||||
`hive-<name>-agent`, delivered as a systemd credential pair. This binary
|
||||
presents a `client_credentials` access token minted from it as
|
||||
`Authorization: Bearer`.
|
||||
As the agent itself. On each query it reads the agent's own queue secret,
|
||||
`swarm/agents/<agent>/queue`, from the swarm secret store with the agent's
|
||||
store certificate (`nix/agent-modules/bao.nix`), the same read `hive-agent`
|
||||
makes for its queue connection. It sends the secret as
|
||||
`Authorization: Bearer swarm-agent.<agent>.<secret>`, spelled by
|
||||
`swarm_queue_client::agent_token`.
|
||||
|
||||
Minting is **not** implemented here — `swarm_queue_client::mint_token_for_blocking`
|
||||
already owns the request, its error type and its CA handling, and a
|
||||
token-endpoint fix has to be findable in one place. What this crate adds is
|
||||
the agent-shaped half: the client id arrives as a _file_ beside the secret, so
|
||||
nothing outside `nix/agent-modules/queue.nix` spells `hive-<name>-agent`
|
||||
twice. That is the same problem `hive-agent/src/swarm_queue.rs` solves, and
|
||||
`src/auth.rs` is its `decide` restated over this binary's inputs.
|
||||
At the gateway, nginx hands a bearer of that shape to `swarm-logs-auth`, which
|
||||
compares it with the stored secret (`swarm-agent-verify`, the check the queue's
|
||||
auth callout also uses). A 401 makes `swarm-logs` read the secret again and
|
||||
retry once, which covers a secret re-minted between the read and the check.
|
||||
|
||||
🩸 Every credential here is a **path**. The secret's contents are read at the
|
||||
moment of the request, inside `swarm-queue-client`, and are never bound to a
|
||||
name in this crate, logged, or rendered into an error.
|
||||
|
||||
### The audience is the URL
|
||||
|
||||
`swarm-logs` mints its token with the query URL as the requested audience,
|
||||
because that is what authelia checks at the authz endpoint the gateway's
|
||||
`auth_request` calls. The same rule `swarm-otel.nix` states over its own push
|
||||
targets: one binding for the address and the audience, since two spellings of
|
||||
one address present as a valid token refused at the store.
|
||||
|
||||
⚠️ That requires the agent client to be _registered_ for that audience and to
|
||||
hold `authelia.bearer.authz`. Both are set in `swarm-authelia.nix`'s
|
||||
`agentClients`; without them authelia answers `invalid_target` at the token
|
||||
endpoint, or the gateway answers 401 with no explanation.
|
||||
|
||||
⚠️ Registration is not issuance, so the token request _asks_ for both: the
|
||||
audience and the scope are named in the `client_credentials` form, because a
|
||||
client that is registered for a scope it does not request is handed a token
|
||||
carrying none, and the gateway refuses that with the same bare 401.
|
||||
🩸 The secret is never logged or rendered into an error.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
|
@ -62,19 +41,17 @@ shape `swarmctl` is configured in, and for the same reason: every value is
|
|||
derived from an option that module owns, so a default here would be an address
|
||||
we _hope_ points at something.
|
||||
|
||||
| variable | what |
|
||||
| ------------------------------------ | ---------------------------------------------------- |
|
||||
| `HIVE_AGENT_LOGS_QUERY_URL` | the LogsQL query endpoint; also the token's audience |
|
||||
| `HIVE_AGENT_OIDC_TOKEN_ENDPOINT` | authelia's token endpoint |
|
||||
| `HIVE_AGENT_OIDC_CLIENT_ID_FILE` | path of the delivered client-id credential |
|
||||
| `HIVE_AGENT_OIDC_CLIENT_SECRET_FILE` | path of the delivered secret credential |
|
||||
| `HIVE_AGENT_OIDC_CA_FILE` | optional extra trust anchor; unset in this tree |
|
||||
| variable | what |
|
||||
| --------------------------- | ------------------------------------------------ |
|
||||
| `HIVE_AGENT_LOGS_QUERY_URL` | the LogsQL query endpoint |
|
||||
| `HIVE_AGENT_NAME` | this agent's name; the store path and role |
|
||||
| `BAO_ADDR` | the secret store's address |
|
||||
| `BAO_CLIENT_CERT` | path of the agent's live store certificate |
|
||||
| `BAO_CLIENT_KEY` | path of its key |
|
||||
| `BAO_CACERT` | path of the store's CA; missing or empty is fine |
|
||||
|
||||
`HIVE_AGENT`, the same prefix the harness reads, because it is the same
|
||||
identity — an agent authenticates as its hive's agent client whether the
|
||||
caller is the harness or a CLI the agent typed. The first four are all-or-none:
|
||||
a half-set environment is a deployment bug, and this binary says so by name
|
||||
rather than behaving like an unconfigured one.
|
||||
With no `HIVE_AGENT_LOGS_QUERY_URL` the binary says there is no log store;
|
||||
with the URL and no store address it reports a deployment bug by name.
|
||||
|
||||
## Output
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue