swarm: courier an agent's store identity into its container, and log in with it
`swarm-controller` mints an agent's mTLS leaf at creation and publishes it at `swarm/agents/<agent>/bao-mtls`. Nothing read it back. This adds the hop that carries it the rest of the way, and the in-container consumer that proves the hop works. Host side, `lifecycle::agent_identity` reads the row under *this hive's* own certificate — the hive is a principal the store already knows — and stages the leaf and its key `0600` under a new `agent-identity/<name>` state dir, deliberately outside every bind-mounted tree. Both files go in as systemd credentials rather than binds, the same answer and the same mode reason as the queue secret beside it: the staged key is unreadable to the unprivileged agent user, and the container manager reads a `--load-credential` source as root before re-exposing it under the consuming unit's own `User=`. The agent is never asked to authenticate in order to obtain the thing it authenticates with. Container side, `hive-agent-bao-identity.service` logs in with that certificate and reads the agent's own path back, failing the unit when either step does not succeed. It fails loudly where the hive-side readers degrade quietly, because a refused certificate means an agent that believes it reaches the store and never does — a cause only the login itself can name. The address is the whole switch, no separate `enable`, matching how `queue.nix` and `logs.nix` already gate themselves. A hive with a store forwards `HIVE_AGENT_BAO_ADDR` and every agent on it gets the check; a hive without one forwards nothing and no agent does. That is what keeps the delivery from landing in a container with nothing to read it. The hive can now reach an agent's identity, so hive privilege covers agent privilege. Accepted, not mitigated: the alternative is an agent fetching its own credential with a credential it does not yet have. Refs #4137
This commit is contained in:
parent
8aafe4eaee
commit
837e658d4a
10 changed files with 716 additions and 7 deletions
|
|
@ -102,6 +102,32 @@ pub fn forge_repo_creation_disabled_marker(name: &str) -> PathBuf {
|
|||
forge_dir().join(format!("repo-creation-disabled-{name}"))
|
||||
}
|
||||
|
||||
/// `agent-identity/` — one subdir per agent holding the store identity this
|
||||
/// hive collected for it. Staging only: the files exist so `systemd-nspawn`
|
||||
/// has something to `--load-credential` from, and nothing on the host ever
|
||||
/// reads them back.
|
||||
///
|
||||
/// Under the state root rather than `/run` deliberately. The forward happens
|
||||
/// when a container's nspawn config is (re)written, and a container can be
|
||||
/// restarted long after that; material that vanished with `/run` would leave
|
||||
/// a boot where the agent's identity is simply absent and the only symptom is
|
||||
/// a unit inside the container refusing to start.
|
||||
#[must_use]
|
||||
pub fn agent_identity_root() -> PathBuf {
|
||||
state_root().join("agent-identity")
|
||||
}
|
||||
|
||||
/// `agent-identity/<name>` — one agent's staged store identity.
|
||||
///
|
||||
/// ⚠️ Deliberately **not** under `agents/<name>`: that tree is bind-mounted
|
||||
/// into the container (and a parent's, for a child), so a private key placed
|
||||
/// there would be readable by the agent as a plain file, bypassing the
|
||||
/// credential mechanism that exists to control exactly that.
|
||||
#[must_use]
|
||||
pub fn agent_identity_dir(name: &str) -> PathBuf {
|
||||
agent_identity_root().join(name)
|
||||
}
|
||||
|
||||
/// `matrix/` — host-side matrix provisioning state (admin token, hive
|
||||
/// Space room id, per-agent password creds). The shared registration
|
||||
/// token is bind-mounted into the tuwunel container via nix and stays
|
||||
|
|
|
|||
Loading…
Reference in a new issue