swarm CA: describe the store's pki root as the only anchor in docs and comments
The docs, option descriptions and comments that named the swarm root, the swarm CA or the hive CA as an existing trust anchor now name the secret store's pki root or the trust bundle that holds it: - docs/swarm/README.md, secrets.md, bao.md - deploy.bao.clientCaFile's and gateway.verifiedProxyTo's descriptions - comments in hive-c0re/src/meta.rs, hive-agent/src/swarm_queue.rs, swarm-logs/src/auth.rs, swarm-controller/src/vcs_metrics.rs, hive-forge, swarm-authelia, swarm-ci-runner, glue-bao-tls, swarm-bao, swarm-grafana, swarm-otel, otel, hive-c0re, lib/hive-ca-trust.nix, hive-forge/Cargo.toml and the flake-update workflow Nothing behaves differently: every edit is a nix, Rust or TOML comment, an option description, or a test assertion message. The Rust edits change the workspace source, which this PR already rebuilds for hivectl. Refs #4588
This commit is contained in:
parent
15f7d4d0cf
commit
cf7d357f17
20 changed files with 79 additions and 82 deletions
|
|
@ -105,8 +105,8 @@ pub async fn sync_agents(hive: &HiveEnv, agents: &[AgentSpec]) -> Result<()> {
|
|||
|
||||
// Embedded-CA list: keep the `./hive-ca.pem` file at the meta root in
|
||||
// lockstep with its host source so the build-time `certificateFiles`
|
||||
// list render_flake emits always resolves. Empty when no self-signed
|
||||
// hive CA is configured.
|
||||
// list render_flake emits always resolves. Empty when the hive has no
|
||||
// trust bundle.
|
||||
let (ca_files, ca_changed) = ca_embed_state(&dir);
|
||||
|
||||
// Skip only when both the flake AND the embedded CA are unchanged — a
|
||||
|
|
@ -178,9 +178,9 @@ pub async fn sync_agents(hive: &HiveEnv, agents: &[AgentSpec]) -> Result<()> {
|
|||
// contain '/flake.nix'". Lock then commit once with both
|
||||
// flake.nix and flake.lock — single commit per change.
|
||||
git(&dir, &["add", "flake.nix"]).await?;
|
||||
// Stage every embedded CA file we wrote or removed (hive CA + peer
|
||||
// CAs). `git add <path>` stages a deletion when the path is tracked
|
||||
// and now gone; best-effort so the never-tracked-and-absent case
|
||||
// Stage every embedded CA file we wrote or removed (trust bundle +
|
||||
// stale peer CAs). `git add <path>` stages a deletion when the path is
|
||||
// tracked and now gone; best-effort so the never-tracked-and-absent case
|
||||
// (pathspec mismatch) is a harmless no-op.
|
||||
for name in &ca_touched {
|
||||
let _ = git(&dir, &["add", "--", name]).await;
|
||||
|
|
@ -653,14 +653,13 @@ const HIVE_CA_FILE: &str = "hive-ca.pem";
|
|||
/// Host path of the hive's TLS trust anchors, when self-signed TLS is
|
||||
/// active. The hyperhive TLS module sets `HIVE_TLS_CA_PATH` in hive-c0re's env
|
||||
/// (to `<deploy.hive-controller.tls.stateDir>/trust-bundle.pem`) whenever the
|
||||
/// gateway serves a self-signed, hive-CA-signed leaf. The file holds one
|
||||
/// *or more* certs —
|
||||
/// the hive CA plus the swarm root it is issued under — and is copied
|
||||
/// verbatim, which `security.pki.certificateFiles` accepts; nothing here
|
||||
/// parses it. Returns `Some(path)` only when the var is set AND
|
||||
/// the cert exists on disk — so render + write stay consistent (we never
|
||||
/// emit a `certificateFiles` reference to a file we didn't embed). Only the
|
||||
/// public cert is ever read here; the CA private key never leaves the host.
|
||||
/// gateway serves a leaf issued by the secret store's `pki` mount. The
|
||||
/// file holds that mount's root, the one anchor every hive's gateway leaf
|
||||
/// chains to, and is copied verbatim, which `security.pki.certificateFiles`
|
||||
/// accepts; nothing here parses it. Returns `Some(path)` only when the var
|
||||
/// is set AND the cert exists on disk — so render + write stay consistent
|
||||
/// (we never emit a `certificateFiles` reference to a file we didn't
|
||||
/// embed). Only the public cert is ever read here.
|
||||
fn hive_ca_source() -> Option<String> {
|
||||
let path = std::env::var("HIVE_TLS_CA_PATH").ok()?;
|
||||
if path.is_empty() || !std::path::Path::new(&path).is_file() {
|
||||
|
|
@ -723,11 +722,9 @@ fn otel_config() -> Option<OtelConfig> {
|
|||
/// `(filename, host_source_path)` — now just the hive's own trust
|
||||
/// anchors as `hive-ca.pem`, when self-signed TLS is active.
|
||||
///
|
||||
/// That single file already carries the swarm root (the hyperhive TLS
|
||||
/// module writes the hive CA *and* the root it is issued under into the
|
||||
/// trust bundle), so every hive under the swarm root validates from it. Which
|
||||
/// is why the per-peer CAs this used to append are gone: they said the
|
||||
/// same thing once per peer.
|
||||
/// That single file carries the store's `pki` root (the hyperhive TLS
|
||||
/// module writes it into the trust bundle), so every hive's gateway leaf
|
||||
/// validates from it and nothing is embedded per peer.
|
||||
///
|
||||
/// `render_flake` emits exactly these filenames into
|
||||
/// `security.pki.certificateFiles` and `sync_agents` materialises them,
|
||||
|
|
@ -741,7 +738,7 @@ fn embedded_ca_files() -> Vec<(String, String)> {
|
|||
}
|
||||
|
||||
/// Write each desired embedded CA file next to `flake.nix` and remove
|
||||
/// any stale one (a hive CA turned off, or a peer dropped from config),
|
||||
/// any stale one (self-signed TLS turned off, or a leftover peer CA),
|
||||
/// so the flake never references a file we didn't write. Returns every
|
||||
/// filename written or removed, for the caller to stage. The public CA
|
||||
/// certs only; no private key is ever embedded.
|
||||
|
|
@ -773,10 +770,10 @@ fn materialise_ca_files(dir: &Path, ca_files: &[(String, String)]) -> Result<Vec
|
|||
///
|
||||
/// ⚠️ Still matches `peer-ca-<N>.pem`, which `embedded_ca_files` no
|
||||
/// longer produces — deliberately. Cleanup is driven by
|
||||
/// "recognised but not desired", so this arm is exactly what removes the
|
||||
/// peer CAs a hive embedded before per-peer pinning was replaced by the
|
||||
/// swarm root. Drop it and those files are orphaned at every meta root
|
||||
/// forever, referenced by nothing and cleaned by nobody.
|
||||
/// "recognised but not desired", so this arm is exactly what removes a
|
||||
/// per-peer CA file still sitting at a meta root. Drop it and those files
|
||||
/// are orphaned at every meta root forever, referenced by nothing and
|
||||
/// cleaned by nobody.
|
||||
fn is_embedded_ca_name(name: &str) -> bool {
|
||||
name == HIVE_CA_FILE || (name.starts_with("peer-ca-") && has_pem_ext(name))
|
||||
}
|
||||
|
|
@ -994,9 +991,8 @@ where
|
|||
" services.hyperhive.agent.claudeCodePath = \"{path}\";"
|
||||
);
|
||||
}
|
||||
// CA trust: embed the hive's trust anchors so each agent validates
|
||||
// them at build time — the hive's own self-signed CA when active,
|
||||
// together with the swarm root it is issued under (one file; see
|
||||
// CA trust: embed the hive's trust bundle so each agent validates
|
||||
// the store's `pki` root at build time (one file; see
|
||||
// `hive_ca_source`). `certificateFiles` is build-time, so the certs
|
||||
// travel with the flake source: `sync_agents` writes `./hive-ca.pem`
|
||||
// next to flake.nix and stages it. Only public CA certs are embedded;
|
||||
|
|
@ -1987,7 +1983,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn render_flake_embeds_hive_ca_when_signalled() {
|
||||
// When the hyperhive TLS module signals a self-signed hive CA via
|
||||
// When the hyperhive TLS module signals a trust bundle via
|
||||
// HIVE_TLS_CA_PATH (and the cert exists), the agent module must
|
||||
// trust it at build time via security.pki.certificateFiles. Absent
|
||||
// the signal, no reference is emitted (so the flake doesn't point at
|
||||
|
|
@ -2019,9 +2015,9 @@ mod tests {
|
|||
.expect("render flake")
|
||||
};
|
||||
|
||||
// A leftover peer-CA file + the env var that used to name it. Both
|
||||
// must now be inert: the swarm root rides in the hive's own trust
|
||||
// bundle, so nothing per-peer is embedded any more.
|
||||
// A leftover peer-CA file + the env var that names it. Both must be
|
||||
// inert: the store's root rides in the hive's own trust bundle, so
|
||||
// nothing per-peer is embedded.
|
||||
let stale_peer =
|
||||
std::env::temp_dir().join(format!("peer-ca0-test-{}.pem", std::process::id()));
|
||||
std::fs::write(
|
||||
|
|
@ -2041,7 +2037,7 @@ mod tests {
|
|||
std::env::set_var("HIVE_PEER_CA_PATHS", stale_peer.display().to_string());
|
||||
}
|
||||
let with_stale_peer_env = render();
|
||||
// The stale var alone, with no hive CA: must produce nothing.
|
||||
// The stale var alone, with no trust bundle: must produce nothing.
|
||||
unsafe {
|
||||
std::env::remove_var("HIVE_TLS_CA_PATH");
|
||||
}
|
||||
|
|
@ -2065,9 +2061,9 @@ mod tests {
|
|||
// on changes that have nothing to do with this test.
|
||||
assert!(
|
||||
with_stale_peer_env.contains("security.pki.certificateFiles = [ ./hive-ca.pem ]"),
|
||||
"HIVE_PEER_CA_PATHS must not change the CA list — per-peer CA \
|
||||
embedding was replaced by the swarm root inside the hive's own \
|
||||
trust bundle:\n{with_stale_peer_env}"
|
||||
"HIVE_PEER_CA_PATHS must not change the CA list — the store's \
|
||||
root inside the hive's own trust bundle is the only anchor \
|
||||
embedded:\n{with_stale_peer_env}"
|
||||
);
|
||||
assert!(
|
||||
!with_stale_peer_env.contains("peer-ca-"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue