Watch
0
0
Fork
You've already forked hyperhive
0

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:
atlas 2026-10-13 09:49:32 +02:00 • committed by mara
commit 868c6cf96c
41 changed files with 286 additions and 1410 deletions

View file

@ -106,14 +106,11 @@ let
deploy.matrix.enable = true;
};
# The store with none of the readers beside it. Grafana and the collector are
# simply off; the queue reader renders on any host holding
# its leaf, which ./glue-bao-tls.nix mints here, so that leaf is taken away.
# The store with none of the readers beside it: Grafana and the collector are
# off.
baoGrantNoReaders = hiveWithController {
deploy.bao.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
deploy.bao.queueAgentClientCertFile = lib.mkForce null;
deploy.bao.queueAgentClientKeyFile = lib.mkForce null;
};
# The store with the forwarder's own pair taken away. The forwarder renders
@ -131,8 +128,6 @@ let
deploy.swarm-otel.enable = true;
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
deploy.bao.queueAgentClientCertFile = "/etc/pki/bao-queue-agent.pem";
deploy.bao.queueAgentClientKeyFile = "/etc/pki/bao-queue-agent-key.pem";
deploy.bao.grafanaOidcClientCertFile = "/etc/pki/bao-grafana-oidc.pem";
deploy.bao.grafanaOidcClientKeyFile = "/etc/pki/bao-grafana-oidc-key.pem";
deploy.bao.otelOidcClientCertFile = "/etc/pki/bao-otel-oidc.pem";
@ -142,7 +137,6 @@ let
# The readers ./glue-bao-readers-policy-order.nix orders after their policy
# units.
policyReaders = [
"swarm-bao-queue-agent"
"swarm-bao-grafana-oidc"
"swarm-bao-otel-oidc"
];
@ -175,7 +169,6 @@ let
"swarm-bao-controller-policy"
"swarm-bao-secret-publisher-policy"
"swarm-bao-matrix-ctl-policy"
"swarm-bao-queue-agent-policy"
"swarm-bao-grafana-oidc-policy"
"swarm-bao-otel-oidc-policy"
"swarm-bao-forwarder-oidc-policy"
@ -599,19 +592,18 @@ let
# arms pin what each principal must NOT reach, so a later widening fails
# here instead of being noticed in a store.
{
name = "the queue-credential reader's grant is one hive's queue credential and nothing else";
# A hive holds no shared queue credential for its agents: no unit writes
# a role or policy for one, none reads one, and no granting unit names
# its path. The control: the same scan finds a reader's grant.
name = "no unit grants, reads or writes a hive-shared agent queue credential";
ok =
let
s = baoGrantHere.systemd.services.swarm-bao-queue-agent-policy.script;
scripts = lib.mapAttrsToList (_: u: u.script or "") baoGrantWithConsumers.systemd.services;
in
lib.hasInfix "path \"secret/data/swarm/hives/h1/queue/agent\" {" s
&& lib.hasInfix "capabilities = [\"read\"]" s
&& !(lib.hasInfix "secret/data/swarm/agents" s)
&& !(lib.hasInfix "secret/data/swarm/services" s)
&& !(lib.hasInfix "secret/data/swarm/hives/h1/*" s)
&& !(lib.hasInfix "secret/data/swarm/hives/h1/matrix" s)
&& !(lib.hasInfix "secret/data/swarm/hives/*" s)
&& !(lib.hasInfix "sys/policies/acl" s);
!(baoGrantWithConsumers.systemd.services ? swarm-bao-queue-agent-policy)
&& !(baoGrantWithConsumers.systemd.services ? swarm-bao-queue-agent)
&& !(lib.any (lib.hasInfix "/queue/agent") scripts)
&& lib.any (lib.hasInfix "/oidc/client") scripts;
}
{
# ⚠️ The client id is the path segment, so the negative arm that matters
@ -856,11 +848,7 @@ let
# through a token that carries it, and a token is minted by a cert-auth
# role matching a CN. Distinct subjects is the whole mechanism — one
# subject for every reader is one principal however the policies read.
#
# The per-hive subject carries the hive name because its path does; the
# service subjects do not, because an OIDC client is registered once
# per swarm. Pinned so neither shape is tidied into the other.
name = "each of the four readers logs in under a subject of its own";
name = "each of the three readers logs in under a subject of its own";
ok =
let
subjectOf =
@ -875,8 +863,7 @@ let
# the reason the three service principals above state.
&& !(lib.hasInfix "auth/cert/certs/hive-" s);
in
subjectOf "swarm-bao-queue-agent-policy" "swarm-queue-agent-h1" "swarm-bao-queue-agent-h1"
&& subjectOf "swarm-bao-grafana-oidc-policy" "swarm-grafana-oidc" "swarm-bao-grafana-oidc"
subjectOf "swarm-bao-grafana-oidc-policy" "swarm-grafana-oidc" "swarm-bao-grafana-oidc"
&& subjectOf "swarm-bao-otel-oidc-policy" "swarm-otel-oidc" "swarm-bao-otel-oidc"
&& subjectOf "swarm-bao-forwarder-oidc-policy" "swarm-forwarder-oidc" "swarm-bao-forwarder-oidc";
}
@ -889,7 +876,7 @@ let
#
# Each pair is asserted whole: a certificate with no key authenticates
# nothing, so a half-set pair is a reader that does not render.
name = "each of the four readers presents its own leaf, never the hive's";
name = "each of the three readers presents its own leaf, never the hive's";
ok =
let
b = baoGrantWithConsumers.services.hyperhive.deploy.bao;
@ -898,8 +885,6 @@ let
b.clientKeyFile
];
own = [
b.queueAgentClientCertFile
b.queueAgentClientKeyFile
b.grafanaOidcClientCertFile
b.grafanaOidcClientKeyFile
b.otelOidcClientCertFile
@ -915,7 +900,6 @@ let
lib.all (p: p != null) own
&& !(lib.any (p: lib.elem p hiveLeaf) own)
&& lib.length (lib.unique own) == lib.length own
&& presents "swarm-bao-queue-agent" b.queueAgentClientCertFile b.queueAgentClientKeyFile
&& presents "swarm-bao-grafana-oidc" b.grafanaOidcClientCertFile b.grafanaOidcClientKeyFile
&& presents "swarm-bao-otel-oidc" b.otelOidcClientCertFile b.otelOidcClientKeyFile
&& presents "swarm-bao-forwarder-oidc" b.forwarderOidcClientCertFile b.forwarderOidcClientKeyFile;
@ -923,9 +907,7 @@ let
{
# The minting side of the same claim. A role matching a subject nothing
# signs is a reader that cannot log in, so the leaves and the roles have
# to be asserted against each other — and the per-hive leaf carries
# THIS host's hive name, which is what makes one hive's leaf useless
# against another hive's role.
# to be asserted against each other.
name = "the PKI unit signs a leaf per reader, each under that reader's own subject";
ok =
let
@ -936,8 +918,6 @@ let
# literal spanning both would pin this file's line wrapping rather than
# the pairing it means to.
lib.all (lib.flip lib.hasInfix s) [
"/queue-agent.pem ]"
"swarm-bao-queue-agent-h1 \"\" clientAuth"
"/grafana-oidc.pem ]"
"swarm-bao-grafana-oidc \"\" clientAuth"
"/otel-oidc.pem ]"
@ -965,11 +945,10 @@ let
# client certificate and the host is the side that has one, so a unit
# rendered inside the store's container would have neither an identity
# nor a route. Plus the ordering that makes the mounts exist first.
name = "the four readers' granting units are ordered after the mounts and rendered on the host";
name = "the three readers' granting units are ordered after the mounts and rendered on the host";
ok =
let
units = [
"swarm-bao-queue-agent-policy"
"swarm-bao-grafana-oidc-policy"
"swarm-bao-otel-oidc-policy"
"swarm-bao-forwarder-oidc-policy"
@ -990,7 +969,7 @@ let
# The forwarder is listed apart from `policyReaders`: it renders wherever
# the store does, so it is never absent on a store host and never present
# on a remote one, and the two cases below would fail on it for that.
name = "each of the four readers is ordered after the unit writing its role";
name = "each of the three readers is ordered after the unit writing its role";
ok =
let
s = baoGrantWithConsumers.systemd.services;
@ -1483,9 +1462,9 @@ let
}
{
# The other hives' side: every hive in the directory but this one gets
# the same three leaves, under its own name, in a directory of its own
# the same two leaves, under its own name, in a directory of its own
# that the operator copies to its host.
name = "the PKI unit signs every other hive's reader, queue-agent and gateway-leaf identities";
name = "the PKI unit signs every other hive's reader and gateway-leaf identities";
ok =
let
s = baoGrantTwoHives.systemd.services.swarm-bao-pki.script;
@ -1494,7 +1473,6 @@ let
file: cn: lib.hasInfix "/hives/h2/${file}.pem ]" s && lib.hasInfix " ${cn} \"\" clientAuth" s;
in
signs "client" "h2"
&& signs "queue-agent" "${b.queueAgentCommonNamePrefix}-h2"
&& signs "hive-leaf" "${b.hiveLeafCommonNamePrefix}-h2"
# This hive's own leaves stay at the top of the directory.
&& !(lib.hasInfix "/hives/h1/" s);