Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/agent-modules/queue-identity.nix
atlas c6717bccb0 agents: re-pull the agent's own bao mTLS leaf on a timer
swarm-controller re-issues an agent's store leaf at half its validity, but
the running container kept presenting the copy its hive delivered at
start, so a re-issued leaf reached the agent only on its next restart.

hive-agent-bao-identity now seeds a live copy of the pair in its own
runtime directory from the delivered credentials, and re-runs every 10
minutes: it logs in with the live leaf, reads its own
swarm/agents/<agent>/bao-mtls (a read render_agent already grants), and
when the leaf changed, logs in with the new pair and swaps it in only if
the store accepts it.

Every unit in the container that logs in to the store presents the live
pair: hive-agent (queue-identity.nix), hive-matrix-daemon,
hive-subagent-daemon, hive-agent-forge-token, hive-agent-forge-accounts
and hive-agent-github-token. They keep loading the server CA as a
credential. The long-running daemons are ordered after the refresher so
the live pair exists when they start.

It renews only while the held leaf is still valid: the login that reads
the new leaf is made with the current one.

Refs #5017
2026-10-11 10:00:43 +02:00

74 lines
3.3 KiB
Nix

# This agent's own credential for the swarm queue, read from the store by the
# harness itself.
#
# The credential beside it in ./queue.nix is keyed per **hive**: one OIDC
# client minted at deploy time and handed to every agent container on the
# hive, so at the queue's auth callout one agent is indistinguishable from its
# co-hived neighbours. This one is keyed per **agent** — `swarm-controller`
# mints it at agent creation into `swarm/agents/<agent>/queue`
# (`swarm_secret_client::queue::agent_queue_path`), at swarm level, with no
# hive anywhere in the chain.
#
# The agent authenticates to the store as itself, with the certificate
# ./bao.nix already proves it can log in with, and reads its own path. A hive
# courier in the path would be the hive vouching for which agent this is,
# which is the property the per-agent credential exists to remove.
#
# Nothing here writes the secret anywhere. The harness reads it into memory
# before its first queue connection and again on every reconnect attempt
# (`hive-agent`'s `swarm_queue`), so a re-minted secret is picked up without a
# restart. What this module hands the harness is the store's address and the
# paths of the live certificate pair ./bao.nix keeps.
#
# ⛔ The store certificate is for reaching the store and nothing else. It is
# never presented to the queue: what goes to the queue is the secret read
# back from this path.
{
lib,
config,
...
}:
let
cfg = config.services.hyperhive.agent.bao;
# This container's agent name — the same string `swarm-controller` minted
# the credential under, because the agent's unix user is named for the
# agent (see ./user.nix). The harness builds the path and the cert-auth
# role from it.
agentName = config.services.hyperhive.agent.user.name;
# The server CA id ./bao.nix loads; the id is
# `hive_c0re::lifecycle::agent_identity`'s and a rename is a rename there
# too.
serverCaCredential = "hive-agent-bao-server-ca";
# The store's address is the whole switch, exactly as in ./bao.nix — and
# deliberately *not* the queue coordinates in ./queue.nix. Those are the
# hive's, and gating a swarm-minted per-agent credential on a hive-level
# option would put the hive back in a path whose entire purpose is not
# having one.
configured = cfg.addr != null;
in
{
config = lib.mkIf configured {
systemd.services.hive-agent = {
# A bare id, no path: the terse `LoadCredential=` form that inherits a
# credential the service *manager* received, which is what the container
# manager passed in. ./bao.nix and ./queue.nix state the same shape.
serviceConfig.LoadCredential = [ serverCaCredential ];
# The live certificate pair exists once ./bao.nix's unit has run.
after = [ "hive-agent-bao-identity.service" ];
# Paths and a name only. `%d` is this unit's own credentials directory.
environment = {
HIVE_AGENT_NAME = agentName;
BAO_ADDR = cfg.addr;
BAO_CLIENT_CERT = cfg.clientCertFile;
BAO_CLIENT_KEY = cfg.clientKeyFile;
# Named even when no CA was delivered: a bare `LoadCredential=` is
# non-fatal when absent, and the harness treats a missing or empty file
# as "use the container's own trust store".
BAO_CACERT = "%d/${serverCaCredential}";
};
};
};
}