drop the shared hive agent login
Every agent now connects to the queue and reads logs with its own secret from the store (#5181), so the per-hive `hive-<h>-agent` OIDC client has no reader left. Remove it end to end: - hive-c0re no longer forwards `hive-queue-agent-{secret,client-id}` into containers, and no longer forwards HIVE_AGENT_OIDC_TOKEN_ENDPOINT; the agent module that loaded them (nix/agent-modules/queue.nix) is gone, its options refused with a removal message. - The host reader (glue-queue-agent-credential.nix, swarm-bao-queue-agent), its store role and policy unit (swarm-bao-queue-agent-policy), its PKI leaf and the deploy.bao.queueAgent* / queue.agentCredentialDir options are gone; the options are refused with a removal message. - authelia mints no `hive-<h>-agent` client, and swarm-secret-publish no longer writes swarm/hives/<h>/queue/agent. - swarm-secret-client drops queue::agent_client_path / queue::Credential. - HIVE_AGENT_NATS_URL is now gated on the agents' queue address alone. The log store's authelia check stays for operators. swarm-nats-auth still refuses a `-agent` client id; that refusal and `agentClientSuffix` are left for a follow-up. Refs #4459 Refs #4765
This commit is contained in:
parent
d19bd83aa5
commit
868c6cf96c
41 changed files with 286 additions and 1410 deletions
|
|
@ -230,8 +230,8 @@ let
|
|||
# The secret publisher runs in this container, beside the mint whose secrets
|
||||
# it copies. It needs a store identity, and a reader is defined by holding a
|
||||
# certificate the store accepts, never by standing next to it — the rule
|
||||
# ./glue-queue-agent-credential.nix states in full. `deploy.bao.enable` here
|
||||
# would be the co-location assumption itself.
|
||||
# ./swarm-bao.nix's `deploy.bao` options block states in full.
|
||||
# `deploy.bao.enable` here would be the co-location assumption itself.
|
||||
publisherCfg = deployCfg.swarm-secret-publisher;
|
||||
publishes =
|
||||
publisherCfg.enable
|
||||
|
|
@ -300,71 +300,6 @@ let
|
|||
accessTokenSignedResponseAlg = "RS256";
|
||||
}) hyperhiveCfg.swarm.hives;
|
||||
|
||||
# The one URL an agent's token has to be authorised at beyond the queue:
|
||||
# the log store's machine query route. Read off `swarm.victorialogs.domain`
|
||||
# — an unconditional option, declared whether or not this host runs the
|
||||
# store — rather than restated, because it is also the string `swarm-logs`
|
||||
# requests as its audience and the string `hive-c0re/environment.nix` hands
|
||||
# an agent as its endpoint. Three readers, one formula; two spellings
|
||||
# present as a valid token refused at the store.
|
||||
logsQueryUrl = "https://${hyperhiveCfg.swarm.victorialogs.domain}/select/logsql/query";
|
||||
|
||||
# The identity an agent container presents to the swarm's queue. One per
|
||||
# HIVE, not per agent, and that is the load-bearing choice rather than a
|
||||
# shortcut: agents are created at **runtime**, so anything minted per agent
|
||||
# would make creating one a config change plus an authelia reload. Keyed on
|
||||
# the hive, this list's length tracks the roster above it — deploy-time, like
|
||||
# every other entry here.
|
||||
#
|
||||
# ⚠️ The cost, stated rather than discovered later: every agent on a hive
|
||||
# presents the SAME client id, so the broker can tell *hives* apart and not
|
||||
# *agents*. Deliberate and deferred — closing it requires the client list to
|
||||
# stop being static, which is the same problem the users-database writer
|
||||
# already solves for identities.
|
||||
#
|
||||
# 🏷️ The id EXTENDS the hive's own (`hive-<name>-agent`) rather than taking a
|
||||
# prefix of its own (`agent-<name>`), because that reads as *the agent called
|
||||
# `<name>`* — which is the one thing this identity does not carry. The cost is
|
||||
# that the two ids are not distinguishable by prefix, so the responder's
|
||||
# agent rule must be tried BEFORE its hive rule; `policy.rs` says so at the
|
||||
# match site, and the assertion below covers the case that ordering cannot.
|
||||
#
|
||||
# Mirrors `hiveClients` field for field so the two stay comparable. The
|
||||
# signing algorithm is not load-bearing here — this client's consumers are
|
||||
# the queue's auth-callout responder, which *introspects* rather than
|
||||
# verifying offline, and authelia's own authz endpoint — but it matches its
|
||||
# sibling rather than inventing a second answer to a question nobody asked.
|
||||
agentClients = lib.mapAttrsToList (name: _: {
|
||||
id = "${cfg.hiveClientPrefix}${name}${cfg.agentClientSuffix}";
|
||||
description = "HyperHive agents on hive ${name}";
|
||||
kind = "machine";
|
||||
redirectUris = [ ];
|
||||
# ⚠️ **Two** audiences, where `hiveClients` has one, because an agent
|
||||
# presents this token at two different kinds of consumer. The queue
|
||||
# introspects it and reads the client id; the log store's gateway
|
||||
# location runs `auth_request` against authelia, which authorises a
|
||||
# bearer token **by the URL being requested** and refuses one whose
|
||||
# audience does not name it. Measured, not assumed: without the URL
|
||||
# registered here the token endpoint answers `invalid_target` for it,
|
||||
# and a token minted with no audience at all is answered by the gateway
|
||||
# with a bare 401 that names no cause.
|
||||
audience = [
|
||||
"${cfg.hiveClientPrefix}${name}${cfg.agentClientSuffix}"
|
||||
logsQueryUrl
|
||||
];
|
||||
# Without it the authz endpoint refuses an otherwise valid token and
|
||||
# blames the token rather than the missing grant — the same note
|
||||
# `glue-swarm-otel-oidc-client.nix` carries over the collector's client.
|
||||
bearerAuthz = true;
|
||||
# Stated rather than left on authelia's default, for
|
||||
# `glue-swarm-otel-oidc-client.nix`'s reason: authelia permits only
|
||||
# basic / JWT methods for a confidential client holding this scope, and
|
||||
# enforces it in the startup validator. `swarm-queue-client` sends the
|
||||
# credential as basic auth, so this is also what the minter already does.
|
||||
tokenEndpointAuthMethod = "client_secret_basic";
|
||||
accessTokenSignedResponseAlg = "RS256";
|
||||
}) hyperhiveCfg.swarm.hives;
|
||||
|
||||
# `swarm-authelia-bridge`'s own identity — distinct from
|
||||
# `swarm-controller`'s (`swarm-controller.nix`'s `queueClientId`). A
|
||||
# resource server introspecting a token proves its OWN identity to the
|
||||
|
|
@ -706,7 +641,7 @@ in
|
|||
# wherever this module is, so `oidc.clients` is never actually empty
|
||||
# — see `oidcEnabled`'s comment above.
|
||||
services.hyperhive.swarm.authelia.oidc.clients =
|
||||
lib.optionals cfg.oidc.hiveIdentities (hiveClients ++ agentClients)
|
||||
lib.optionals cfg.oidc.hiveIdentities hiveClients
|
||||
++ [
|
||||
bridgeClient
|
||||
forwarderClient
|
||||
|
|
@ -749,18 +684,11 @@ in
|
|||
}
|
||||
{
|
||||
# A hive whose name ENDS with the agent suffix mints an id the
|
||||
# queue's responder reads as somebody else's agents:
|
||||
# queue's responder reads as a hive's agent client, which it refuses:
|
||||
# `hive-foo-agent` parses as *the agents of hive foo* before it
|
||||
# parses as *the hive foo-agent*, because the agent rule is the
|
||||
# more specific one and therefore runs first.
|
||||
#
|
||||
# ⚠️ NOT covered by the duplicate-id assertion above, and the gap is
|
||||
# the interesting half. That one fires only when BOTH `foo` and
|
||||
# `foo-agent` are on the roster, because only then are two clients
|
||||
# actually named the same string. With `foo-agent` alone there is no
|
||||
# duplicate and nothing to see — the hive simply receives its
|
||||
# agents' grant instead of its own, and a NATS denial arrives as a
|
||||
# timeout, so the symptom names nothing.
|
||||
# more specific one and therefore runs first. A NATS denial arrives
|
||||
# as a timeout, so the symptom names nothing.
|
||||
#
|
||||
# Checkable here and nowhere downstream: the responder runs in a
|
||||
# container with no view of the roster (`policy.rs`'s `hive_name`
|
||||
|
|
@ -774,10 +702,9 @@ in
|
|||
+ lib.concatMapStringsSep ", " (h: "'${h}'") (
|
||||
lib.filter (h: lib.hasSuffix cfg.agentClientSuffix h) (lib.attrNames hyperhiveCfg.swarm.hives)
|
||||
)
|
||||
+ " — names ending with '${cfg.agentClientSuffix}', the suffix that "
|
||||
+ "marks a hive's agent containers. Such a hive's own client id is "
|
||||
+ "indistinguishable from another hive's agent client, and the "
|
||||
+ "queue resolves it as the agents. Rename the hive.";
|
||||
+ " — names ending with '${cfg.agentClientSuffix}', the suffix the "
|
||||
+ "queue refuses as a hive's agent client. Such a hive's own client "
|
||||
+ "id reads as one, and the queue refuses it. Rename the hive.";
|
||||
}
|
||||
# The two obligations `authelia.bearer.authz` carries. Authelia
|
||||
# enforces both itself — but in its `preStart` validator, so a
|
||||
|
|
|
|||
Loading…
Reference in a new issue