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
|
|
@ -12,7 +12,7 @@ name: flake update
|
||||||
#
|
#
|
||||||
# `PR_BOT_TOKEN` (repo secret) is the `flake-bot` account's write token.
|
# `PR_BOT_TOKEN` (repo secret) is the `flake-bot` account's write token.
|
||||||
# Self-signed forge cert: not worked around with `curl -k` - swarm-ci-runner.nix
|
# Self-signed forge cert: not worked around with `curl -k` - swarm-ci-runner.nix
|
||||||
# already sets SSL_CERT_FILE to a bundle with the hive CA (wiring
|
# already sets SSL_CERT_FILE to a bundle with the store's pki root (wiring
|
||||||
# confirmed; anchor coverage for the swarm forge specifically isn't).
|
# confirmed; anchor coverage for the swarm forge specifically isn't).
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
|
||||||
|
|
@ -397,9 +397,10 @@ hive's domain. `swarm.name` names the group; `hiveName` names this hive.
|
||||||
The env-var chain and `qualify()` / `qualified_label()` semantics:
|
The env-var chain and `qualify()` / `qualified_label()` semantics:
|
||||||
[`conventions.md`](../process/conventions.md) § Hive identity.
|
[`conventions.md`](../process/conventions.md) § Hive identity.
|
||||||
|
|
||||||
Trust inside a swarm comes from the swarm root ([`ca.md`](ca.md)): every hive
|
Trust inside a swarm comes from the secret store's `pki` root
|
||||||
chains to it. Trusting a hive whose root this swarm doesn't own — another
|
([`ca.md`](ca.md)): every hive's gateway leaf chains to it. Trusting a
|
||||||
swarm's — has no mechanism.
|
hive whose root this swarm doesn't own — another swarm's — has no
|
||||||
|
mechanism.
|
||||||
|
|
||||||
### What the directory feeds
|
### What the directory feeds
|
||||||
|
|
||||||
|
|
@ -415,7 +416,7 @@ swarm's — has no mechanism.
|
||||||
|
|
||||||
⚠️ On a hive with self-signed gateway certificates, the container
|
⚠️ On a hive with self-signed gateway certificates, the container
|
||||||
also trusts this hive's `trust-bundle.pem`, bound in at runtime and
|
also trusts this hive's `trust-bundle.pem`, bound in at runtime and
|
||||||
added to the public CAs; it ends at the swarm root, so a peer whose
|
added to the public CAs; it holds the store's `pki` root, so a peer whose
|
||||||
certificate chains to the same root validates. A peer outside this
|
certificate chains to the same root validates. A peer outside this
|
||||||
swarm's root needs a CA-issued certificate (ACME). Federation firewall
|
swarm's root needs a CA-issued certificate (ACME). Federation firewall
|
||||||
and TLS requirements: [`integrations/matrix.md`](../integrations/matrix.md).
|
and TLS requirements: [`integrations/matrix.md`](../integrations/matrix.md).
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ itself, plus a pair for every principal it runs — see
|
||||||
The operator issues those leaves out of band; they're the credentials the
|
The operator issues those leaves out of band; they're the credentials the
|
||||||
store can't hand you, being what opens it.
|
store can't hand you, being what opens it.
|
||||||
|
|
||||||
⚠️ Not the gateway's HTTPS certificates and not the hive CA — this is **mTLS
|
⚠️ Not the gateway's HTTPS certificates and not the store's `pki` root — this is **mTLS
|
||||||
between services and the store**, a separate trust domain, because a store
|
between services and the store**, a separate trust domain, because a store
|
||||||
that took its identity from an authority it itself distributes could never
|
that took its identity from an authority it itself distributes could never
|
||||||
come up before that authority.
|
come up before that authority.
|
||||||
|
|
|
||||||
|
|
@ -273,8 +273,8 @@ client secret to stand in.
|
||||||
⚠️ **Service↔store mTLS is its own trust domain.** You can't fetch a
|
⚠️ **Service↔store mTLS is its own trust domain.** You can't fetch a
|
||||||
credential you must already hold to authenticate from the thing it
|
credential you must already hold to authenticate from the thing it
|
||||||
authenticates you to, so the store's identity can't come from an authority the store
|
authenticates you to, so the store's identity can't come from an authority the store
|
||||||
distributes — which excludes the hive CA and the swarm CA both, and has nothing
|
distributes — which excludes the store's `pki` root, and has nothing to do
|
||||||
to do with the gateway's HTTPS certificates either way. `glue-bao-tls.nix`
|
with the gateway's HTTPS certificates either way. `glue-bao-tls.nix`
|
||||||
mints a CA that signs a fixed, short list of leaves — the store's server
|
mints a CA that signs a fixed, short list of leaves — the store's server
|
||||||
certificate, the hive's own reader certificate, and one per service principal —
|
certificate, the hive's own reader certificate, and one per service principal —
|
||||||
and distributes nothing. A deployment with a real
|
and distributes nothing. A deployment with a real
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,7 @@ async fn connect_as_agent<S: SecretSource>(
|
||||||
secret.prime().await?;
|
secret.prime().await?;
|
||||||
let secret = Arc::new(secret);
|
let secret = Arc::new(secret);
|
||||||
let agent = name.clone();
|
let agent = name.clone();
|
||||||
// No extra trust anchor: the container trusts the swarm root, which
|
// No extra trust anchor: the container trusts the store's root, which
|
||||||
// `hive_c0re::meta` embeds at build time.
|
// `hive_c0re::meta` embeds at build time.
|
||||||
swarm_queue_client::connect_with_token(url, None, &agent, move || {
|
swarm_queue_client::connect_with_token(url, None, &agent, move || {
|
||||||
let secret = Arc::clone(&secret);
|
let secret = Arc::clone(&secret);
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Embedded-CA list: keep the `./hive-ca.pem` file at the meta root in
|
||||||
// lockstep with its host source so the build-time `certificateFiles`
|
// lockstep with its host source so the build-time `certificateFiles`
|
||||||
// list render_flake emits always resolves. Empty when no self-signed
|
// list render_flake emits always resolves. Empty when the hive has no
|
||||||
// hive CA is configured.
|
// trust bundle.
|
||||||
let (ca_files, ca_changed) = ca_embed_state(&dir);
|
let (ca_files, ca_changed) = ca_embed_state(&dir);
|
||||||
|
|
||||||
// Skip only when both the flake AND the embedded CA are unchanged — a
|
// 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
|
// contain '/flake.nix'". Lock then commit once with both
|
||||||
// flake.nix and flake.lock — single commit per change.
|
// flake.nix and flake.lock — single commit per change.
|
||||||
git(&dir, &["add", "flake.nix"]).await?;
|
git(&dir, &["add", "flake.nix"]).await?;
|
||||||
// Stage every embedded CA file we wrote or removed (hive CA + peer
|
// Stage every embedded CA file we wrote or removed (trust bundle +
|
||||||
// CAs). `git add <path>` stages a deletion when the path is tracked
|
// stale peer CAs). `git add <path>` stages a deletion when the path is
|
||||||
// and now gone; best-effort so the never-tracked-and-absent case
|
// tracked and now gone; best-effort so the never-tracked-and-absent case
|
||||||
// (pathspec mismatch) is a harmless no-op.
|
// (pathspec mismatch) is a harmless no-op.
|
||||||
for name in &ca_touched {
|
for name in &ca_touched {
|
||||||
let _ = git(&dir, &["add", "--", name]).await;
|
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
|
/// 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
|
/// 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
|
/// (to `<deploy.hive-controller.tls.stateDir>/trust-bundle.pem`) whenever the
|
||||||
/// gateway serves a self-signed, hive-CA-signed leaf. The file holds one
|
/// gateway serves a leaf issued by the secret store's `pki` mount. The
|
||||||
/// *or more* certs —
|
/// file holds that mount's root, the one anchor every hive's gateway leaf
|
||||||
/// the hive CA plus the swarm root it is issued under — and is copied
|
/// chains to, and is copied verbatim, which `security.pki.certificateFiles`
|
||||||
/// verbatim, which `security.pki.certificateFiles` accepts; nothing here
|
/// accepts; nothing here parses it. Returns `Some(path)` only when the var
|
||||||
/// parses it. Returns `Some(path)` only when the var is set AND
|
/// is set AND the cert exists on disk — so render + write stay consistent
|
||||||
/// the cert exists on disk — so render + write stay consistent (we never
|
/// (we never emit a `certificateFiles` reference to a file we didn't
|
||||||
/// emit a `certificateFiles` reference to a file we didn't embed). Only the
|
/// embed). Only the public cert is ever read here.
|
||||||
/// public cert is ever read here; the CA private key never leaves the host.
|
|
||||||
fn hive_ca_source() -> Option<String> {
|
fn hive_ca_source() -> Option<String> {
|
||||||
let path = std::env::var("HIVE_TLS_CA_PATH").ok()?;
|
let path = std::env::var("HIVE_TLS_CA_PATH").ok()?;
|
||||||
if path.is_empty() || !std::path::Path::new(&path).is_file() {
|
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
|
/// `(filename, host_source_path)` — now just the hive's own trust
|
||||||
/// anchors as `hive-ca.pem`, when self-signed TLS is active.
|
/// anchors as `hive-ca.pem`, when self-signed TLS is active.
|
||||||
///
|
///
|
||||||
/// That single file already carries the swarm root (the hyperhive TLS
|
/// That single file carries the store's `pki` root (the hyperhive TLS
|
||||||
/// module writes the hive CA *and* the root it is issued under into the
|
/// module writes it into the trust bundle), so every hive's gateway leaf
|
||||||
/// trust bundle), so every hive under the swarm root validates from it. Which
|
/// validates from it and nothing is embedded per peer.
|
||||||
/// is why the per-peer CAs this used to append are gone: they said the
|
|
||||||
/// same thing once per peer.
|
|
||||||
///
|
///
|
||||||
/// `render_flake` emits exactly these filenames into
|
/// `render_flake` emits exactly these filenames into
|
||||||
/// `security.pki.certificateFiles` and `sync_agents` materialises them,
|
/// `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
|
/// 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
|
/// 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
|
/// filename written or removed, for the caller to stage. The public CA
|
||||||
/// certs only; no private key is ever embedded.
|
/// 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
|
/// ⚠️ Still matches `peer-ca-<N>.pem`, which `embedded_ca_files` no
|
||||||
/// longer produces — deliberately. Cleanup is driven by
|
/// longer produces — deliberately. Cleanup is driven by
|
||||||
/// "recognised but not desired", so this arm is exactly what removes the
|
/// "recognised but not desired", so this arm is exactly what removes a
|
||||||
/// peer CAs a hive embedded before per-peer pinning was replaced by the
|
/// per-peer CA file still sitting at a meta root. Drop it and those files
|
||||||
/// swarm root. Drop it and those files are orphaned at every meta root
|
/// are orphaned at every meta root forever, referenced by nothing and
|
||||||
/// forever, referenced by nothing and cleaned by nobody.
|
/// cleaned by nobody.
|
||||||
fn is_embedded_ca_name(name: &str) -> bool {
|
fn is_embedded_ca_name(name: &str) -> bool {
|
||||||
name == HIVE_CA_FILE || (name.starts_with("peer-ca-") && has_pem_ext(name))
|
name == HIVE_CA_FILE || (name.starts_with("peer-ca-") && has_pem_ext(name))
|
||||||
}
|
}
|
||||||
|
|
@ -994,9 +991,8 @@ where
|
||||||
" services.hyperhive.agent.claudeCodePath = \"{path}\";"
|
" services.hyperhive.agent.claudeCodePath = \"{path}\";"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// CA trust: embed the hive's trust anchors so each agent validates
|
// CA trust: embed the hive's trust bundle so each agent validates
|
||||||
// them at build time — the hive's own self-signed CA when active,
|
// the store's `pki` root at build time (one file; see
|
||||||
// together with the swarm root it is issued under (one file; see
|
|
||||||
// `hive_ca_source`). `certificateFiles` is build-time, so the certs
|
// `hive_ca_source`). `certificateFiles` is build-time, so the certs
|
||||||
// travel with the flake source: `sync_agents` writes `./hive-ca.pem`
|
// travel with the flake source: `sync_agents` writes `./hive-ca.pem`
|
||||||
// next to flake.nix and stages it. Only public CA certs are embedded;
|
// next to flake.nix and stages it. Only public CA certs are embedded;
|
||||||
|
|
@ -1987,7 +1983,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render_flake_embeds_hive_ca_when_signalled() {
|
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
|
// HIVE_TLS_CA_PATH (and the cert exists), the agent module must
|
||||||
// trust it at build time via security.pki.certificateFiles. Absent
|
// trust it at build time via security.pki.certificateFiles. Absent
|
||||||
// the signal, no reference is emitted (so the flake doesn't point at
|
// the signal, no reference is emitted (so the flake doesn't point at
|
||||||
|
|
@ -2019,9 +2015,9 @@ mod tests {
|
||||||
.expect("render flake")
|
.expect("render flake")
|
||||||
};
|
};
|
||||||
|
|
||||||
// A leftover peer-CA file + the env var that used to name it. Both
|
// A leftover peer-CA file + the env var that names it. Both must be
|
||||||
// must now be inert: the swarm root rides in the hive's own trust
|
// inert: the store's root rides in the hive's own trust bundle, so
|
||||||
// bundle, so nothing per-peer is embedded any more.
|
// nothing per-peer is embedded.
|
||||||
let stale_peer =
|
let stale_peer =
|
||||||
std::env::temp_dir().join(format!("peer-ca0-test-{}.pem", std::process::id()));
|
std::env::temp_dir().join(format!("peer-ca0-test-{}.pem", std::process::id()));
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
|
|
@ -2041,7 +2037,7 @@ mod tests {
|
||||||
std::env::set_var("HIVE_PEER_CA_PATHS", stale_peer.display().to_string());
|
std::env::set_var("HIVE_PEER_CA_PATHS", stale_peer.display().to_string());
|
||||||
}
|
}
|
||||||
let with_stale_peer_env = render();
|
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 {
|
unsafe {
|
||||||
std::env::remove_var("HIVE_TLS_CA_PATH");
|
std::env::remove_var("HIVE_TLS_CA_PATH");
|
||||||
}
|
}
|
||||||
|
|
@ -2065,9 +2061,9 @@ mod tests {
|
||||||
// on changes that have nothing to do with this test.
|
// on changes that have nothing to do with this test.
|
||||||
assert!(
|
assert!(
|
||||||
with_stale_peer_env.contains("security.pki.certificateFiles = [ ./hive-ca.pem ]"),
|
with_stale_peer_env.contains("security.pki.certificateFiles = [ ./hive-ca.pem ]"),
|
||||||
"HIVE_PEER_CA_PATHS must not change the CA list — per-peer CA \
|
"HIVE_PEER_CA_PATHS must not change the CA list — the store's \
|
||||||
embedding was replaced by the swarm root inside the hive's own \
|
root inside the hive's own trust bundle is the only anchor \
|
||||||
trust bundle:\n{with_stale_peer_env}"
|
embedded:\n{with_stale_peer_env}"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!with_stale_peer_env.contains("peer-ca-"),
|
!with_stale_peer_env.contains("peer-ca-"),
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ reqwest = { workspace = true, features = [
|
||||||
"json",
|
"json",
|
||||||
# `rustls` carries the platform verifier, which reads the OS trust store
|
# `rustls` carries the platform verifier, which reads the OS trust store
|
||||||
# (and honours `SSL_CERT_FILE`), so hive-forge validates the hive
|
# (and honours `SSL_CERT_FILE`), so hive-forge validates the hive
|
||||||
# gateway's self-signed leaf once the hive CA is in the agent's trust
|
# gateway's self-signed leaf once the store's root is in the agent's trust
|
||||||
# store — embedded into agent flakes via `security.pki.certificateFiles`
|
# store — embedded into agent flakes via `security.pki.certificateFiles`
|
||||||
# — while public CAs still validate.
|
# — while public CAs still validate.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
# handed. A deployment with a real internal CA drops this file and names its
|
# handed. A deployment with a real internal CA drops this file and names its
|
||||||
# own paths; nothing in the store changes.
|
# own paths; nothing in the store changes.
|
||||||
#
|
#
|
||||||
# ⚠️ Not the hive CA and not the swarm CA. The store will eventually
|
# ⚠️ Not the store's `pki` root, which the store distributes. An authority
|
||||||
# distribute both, and an authority you must already hold a certificate from
|
# you must already hold a certificate from cannot be one the store hands
|
||||||
# cannot be one the store hands out — reach the store to get the CA material,
|
# out — reach the store to get the CA material, need a cert from that CA to
|
||||||
# need a cert from that CA to reach the store. This CA signs a fixed, short
|
# reach the store. This CA signs a fixed, short
|
||||||
# list of leaves and distributes nothing, so it cannot enter that cycle.
|
# list of leaves and distributes nothing, so it cannot enter that cycle.
|
||||||
#
|
#
|
||||||
# ⚠️ Files like this are the only place a `deploy.<foo>` value may derive from
|
# ⚠️ Files like this are the only place a `deploy.<foo>` value may derive from
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,9 @@ in
|
||||||
# ⚠️ `SSL_CERT_FILE` REPLACES the trust store, and unlike the other
|
# ⚠️ `SSL_CERT_FILE` REPLACES the trust store, and unlike the other
|
||||||
# consumers of this helper, hive-c0re already makes a *public*-TLS call:
|
# consumers of this helper, hive-c0re already makes a *public*-TLS call:
|
||||||
# the OTEL exporter reaches whatever `otel.endpoint` names, typically a
|
# the OTEL exporter reaches whatever `otel.endpoint` names, typically a
|
||||||
# normal internet host. The bundle is system CAs + hive CA precisely so
|
# normal internet host. The bundle is system CAs + the store's root
|
||||||
# that path keeps working — narrowing it to the hive CA alone would fix
|
# precisely so that path keeps working — narrowing it to the store's root
|
||||||
# a case nobody hits yet and break one that runs today.
|
# alone would fix a case nobody hits yet and break one that runs today.
|
||||||
(caTrust.trustBundle {
|
(caTrust.trustBundle {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
name = "hive-c0re";
|
name = "hive-c0re";
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,7 @@ in
|
||||||
# --link-journal=try-guest, and EXTRA_NSPAWN_FLAGS expands after it.
|
# --link-journal=try-guest, and EXTRA_NSPAWN_FLAGS expands after it.
|
||||||
extraFlags = [ "--link-journal=host" ];
|
extraFlags = [ "--link-journal=host" ];
|
||||||
inherit privateNetwork;
|
inherit privateNetwork;
|
||||||
# Self-signed mode: bind the public hive CA cert read-only so forgejo
|
# Self-signed mode: bind the hive's trust bundle read-only so forgejo
|
||||||
# can trust the gateway's self-signed leaf for outbound webhook
|
# can trust the gateway's self-signed leaf for outbound webhook
|
||||||
# delivery. The bind-mount, the `container@` ordering and the bundle
|
# delivery. The bind-mount, the `container@` ordering and the bundle
|
||||||
# the container's consumers read all come from the hive-ca-trust
|
# the container's consumers read all come from the hive-ca-trust
|
||||||
|
|
@ -480,12 +480,12 @@ in
|
||||||
|
|
||||||
# Forgejo is Go, and `SSL_CERT_FILE` *replaces* the default store
|
# Forgejo is Go, and `SSL_CERT_FILE` *replaces* the default store
|
||||||
# rather than adding to it — so it needs the system CAs and the
|
# rather than adding to it — so it needs the system CAs and the
|
||||||
# hive CA concatenated, not the CA alone, or public mirror fetches
|
# store's root concatenated, not the root alone, or public mirror
|
||||||
# lose every anchor they had.
|
# fetches lose every anchor they had.
|
||||||
#
|
#
|
||||||
# BOTH units that make an outbound HTTPS call are consumers, not
|
# BOTH units that make an outbound HTTPS call are consumers, not
|
||||||
# just the obvious one: `forgejo-sso-source` fetches the issuer's
|
# just the obvious one: `forgejo-sso-source` fetches the issuer's
|
||||||
# `.well-known/openid-configuration` over the swarm CA, and it
|
# `.well-known/openid-configuration` under the store's root, and it
|
||||||
# once shipped without the trust its sibling had. `optional`
|
# once shipped without the trust its sibling had. `optional`
|
||||||
# rather than a flat list because that unit only exists when SSO
|
# rather than a flat list because that unit only exists when SSO
|
||||||
# is on — naming an absent unit would order nothing and quietly
|
# is on — naming an absent unit would order nothing and quietly
|
||||||
|
|
@ -801,7 +801,8 @@ in
|
||||||
# container's `imports` above, which is where `SSL_CERT_FILE`
|
# container's `imports` above, which is where `SSL_CERT_FILE`
|
||||||
# comes from): registering the login source makes an **outbound
|
# comes from): registering the login source makes an **outbound
|
||||||
# HTTPS call** to `<issuer>/.well-known/openid-configuration`,
|
# HTTPS call** to `<issuer>/.well-known/openid-configuration`,
|
||||||
# served under the swarm CA the default store has never heard of.
|
# served under the store's `pki` root, which the default store has
|
||||||
|
# never heard of.
|
||||||
# It once shipped without the trust `forgejo.service` had, and
|
# It once shipped without the trust `forgejo.service` had, and
|
||||||
# failed every single time with `x509: certificate signed by
|
# failed every single time with `x509: certificate signed by
|
||||||
# unknown authority`. The trust belongs to every process that
|
# unknown authority`. The trust belongs to every process that
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Read-only: `name -> the proxy_ssl_* lines` for dialling
|
Read-only: `name -> the proxy_ssl_* lines` for dialling
|
||||||
another service on this hive **by name over https**, with the
|
another service on this hive **by name over https**, with the
|
||||||
certificate actually verified against the hive CA bundle.
|
certificate actually verified against the hive's trust bundle.
|
||||||
|
|
||||||
Published rather than written per module because nginx
|
Published rather than written per module because nginx
|
||||||
verifies nothing by default: `proxy_ssl_verify` is **off**, so
|
verifies nothing by default: `proxy_ssl_verify` is **off**, so
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Shared hive-CA trust plumbing for containers that must trust the
|
# Shared trust-bundle plumbing for containers that must trust the
|
||||||
# self-signed gateway/forge leaf for *outbound* TLS (webhook delivery,
|
# self-signed gateway/forge leaf for *outbound* TLS (webhook delivery,
|
||||||
# CI artifact upload, …). The anchors exist only at runtime — the host
|
# CI artifact upload, …). The anchors exist only at runtime — the host
|
||||||
# `hive-tls-ca.service` (see `hive-tls.nix`) assembles them into
|
# `hive-tls-ca.service` (see `hive-tls.nix`) assembles them into
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
# *consumption* differs per runtime: an additive variable (Node's
|
# *consumption* differs per runtime: an additive variable (Node's
|
||||||
# `NODE_EXTRA_CA_CERTS`, swarm-ci-runner) points straight at `caContainerPath` from
|
# `NODE_EXTRA_CA_CERTS`, swarm-ci-runner) points straight at `caContainerPath` from
|
||||||
# the call site, while a *replacing* one (Go's `SSL_CERT_FILE`, rustls) needs
|
# the call site, while a *replacing* one (Go's `SSL_CERT_FILE`, rustls) needs
|
||||||
# the system-CAs+hive-CA concat that `trustBundle` below does for it.
|
# the system-CAs+bundle concat that `trustBundle` below does for it.
|
||||||
#
|
#
|
||||||
# Pure function — NOT a NixOS module (don't add it to the host-modules
|
# Pure function — NOT a NixOS module (don't add it to the host-modules
|
||||||
# aggregator). Call it from a module's `let`:
|
# aggregator). Call it from a module's `let`:
|
||||||
|
|
@ -93,7 +93,7 @@ in
|
||||||
requires = [ "hive-tls-ca.service" ];
|
requires = [ "hive-tls-ca.service" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# System CAs + hive CA in one bundle, with `SSL_CERT_FILE` set on each
|
# System CAs + the trust bundle in one file, with `SSL_CERT_FILE` set on each
|
||||||
# consumer — for runtimes whose trust variable *replaces* the store (Go,
|
# consumer — for runtimes whose trust variable *replaces* the store (Go,
|
||||||
# rustls-native-certs). An additive one (Node's `NODE_EXTRA_CA_CERTS`,
|
# rustls-native-certs). An additive one (Node's `NODE_EXTRA_CA_CERTS`,
|
||||||
# swarm-ci-runner) needs no bundle and should not use this.
|
# swarm-ci-runner) needs no bundle and should not use this.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
let
|
let
|
||||||
# This tier now reaches the swarm's collector by name through the
|
# This tier now reaches the swarm's collector by name through the
|
||||||
# gateway (`swarm-otel-service.nix`'s `domain`) instead of a loopback URL, so it
|
# gateway (`swarm-otel-service.nix`'s `domain`) instead of a loopback URL, so it
|
||||||
# needs the same hive-CA trust every other host consumer of an `https://`
|
# needs the same trust-bundle wiring every other host consumer of an `https://`
|
||||||
# swarm-service name needs — see `swarm-controller.nix` for the sibling
|
# swarm-service name needs — see `swarm-controller.nix` for the sibling
|
||||||
# wiring this copies.
|
# wiring this copies.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -97,9 +97,8 @@ let
|
||||||
tlsCfg = deployCfg.hive-controller.tls;
|
tlsCfg = deployCfg.hive-controller.tls;
|
||||||
};
|
};
|
||||||
# `swarm-authelia-bridge` verifies the gateway when it introspects by name.
|
# `swarm-authelia-bridge` verifies the gateway when it introspects by name.
|
||||||
# Nothing in this container trusted the swarm CA, which is a runtime file no
|
# The store's `pki` root is a runtime file no build-time option can name, so
|
||||||
# build-time option can name — so an https call out of here could only ever
|
# without this bundle an https call out of here fails `UnknownIssuer`.
|
||||||
# fail `UnknownIssuer`. Same defect the queue's responder hit.
|
|
||||||
caBundleModule = caTrust.trustBundle {
|
caBundleModule = caTrust.trustBundle {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
name = cfg.machine;
|
name = cfg.machine;
|
||||||
|
|
|
||||||
|
|
@ -1180,8 +1180,8 @@ let
|
||||||
autheliaCfg = hyperhiveCfg.swarm.authelia;
|
autheliaCfg = hyperhiveCfg.swarm.authelia;
|
||||||
|
|
||||||
# Outbound TLS for that collector: it dials the swarm collector's gateway
|
# Outbound TLS for that collector: it dials the swarm collector's gateway
|
||||||
# name and authelia's token endpoint, both leaves of the runtime-generated
|
# name and authelia's token endpoint, both leaves under the store's runtime
|
||||||
# hive CA, which cannot be baked into a derivation. Same helper, same two
|
# pki root, which cannot be baked into a derivation. Same helper, same two
|
||||||
# halves and the same `SSL_CERT_FILE` consumption as ./swarm-otel.nix's own
|
# halves and the same `SSL_CERT_FILE` consumption as ./swarm-otel.nix's own
|
||||||
# container — a Go process whose trust variable REPLACES the store, which is
|
# container — a Go process whose trust variable REPLACES the store, which is
|
||||||
# what the bundle (system CAs ++ our anchors) is for.
|
# what the bundle (system CAs ++ our anchors) is for.
|
||||||
|
|
@ -1374,10 +1374,9 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Authority the store validates hive **client** certificates against.
|
Authority the store validates hive **client** certificates against.
|
||||||
|
|
||||||
This module declares no default and does not reach for the hive
|
This module declares no default, and the store's own `pki` root
|
||||||
CA: the hive CA is a future *consumer* of the store, so a store
|
can't be this authority: a store that authenticated against a root
|
||||||
that authenticated against it could not come up before the thing
|
it issues could not come up before the thing it issues.
|
||||||
it issues.
|
|
||||||
|
|
||||||
On a hive that runs the store, a glue module supplies the CA it
|
On a hive that runs the store, a glue module supplies the CA it
|
||||||
minted for exactly this, as a `mkDefault`. Point this at something
|
minted for exactly this, as a `mkDefault`. Point this at something
|
||||||
|
|
@ -3430,7 +3429,7 @@ in
|
||||||
isReadOnly = true;
|
isReadOnly = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# The hive CA, for the forwarder's two outbound calls: the swarm
|
# The trust bundle, for the forwarder's two outbound calls: the swarm
|
||||||
# collector's gateway name and authelia's token endpoint. Empty when
|
# collector's gateway name and authelia's token endpoint. Empty when
|
||||||
# the gateway is not self-signed, so the whole path drops out.
|
# the gateway is not self-signed, so the whole path drops out.
|
||||||
// caTrust.bindMount;
|
// caTrust.bindMount;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,8 @@ let
|
||||||
# below compares the two, so both come from here.
|
# below compares the two, so both come from here.
|
||||||
forgeUrl = "https://${forgeCfg.domain}";
|
forgeUrl = "https://${forgeCfg.domain}";
|
||||||
|
|
||||||
# The forge's gateway serves a leaf from the swarm CA in self-signed mode.
|
# The forge's gateway serves a leaf from the store's `pki` mount in
|
||||||
|
# self-signed mode.
|
||||||
# The runner daemon is Go, whose `SSL_CERT_FILE` replaces the system store,
|
# The runner daemon is Go, whose `SSL_CERT_FILE` replaces the system store,
|
||||||
# so it gets the assembled bundle; the Node actions it runs get
|
# so it gets the assembled bundle; the Node actions it runs get
|
||||||
# `NODE_EXTRA_CA_CERTS`, which is additive, further down.
|
# `NODE_EXTRA_CA_CERTS`, which is additive, further down.
|
||||||
|
|
|
||||||
|
|
@ -686,9 +686,9 @@ in
|
||||||
})
|
})
|
||||||
# Grafana is Go, and `SSL_CERT_FILE` *replaces* the default store
|
# Grafana is Go, and `SSL_CERT_FILE` *replaces* the default store
|
||||||
# rather than adding to it — so it needs the system CAs and the
|
# rather than adding to it — so it needs the system CAs and the
|
||||||
# hive CA concatenated, not the CA alone. Without it the browser
|
# store's root concatenated, not the root alone. Without it the
|
||||||
# half of the login succeeds and the server-to-server token
|
# browser half of the login succeeds and the server-to-server
|
||||||
# exchange fails `x509: unknown authority`.
|
# token exchange fails `x509: unknown authority`.
|
||||||
(caTrust.trustBundle {
|
(caTrust.trustBundle {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
name = cfg.machine;
|
name = cfg.machine;
|
||||||
|
|
|
||||||
|
|
@ -796,7 +796,7 @@ in
|
||||||
isReadOnly = true;
|
isReadOnly = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# The public hive CA, read-only — only when something in here
|
# The public trust bundle, read-only — only when something in here
|
||||||
# actually verifies a swarm-service name.
|
# actually verifies a swarm-service name.
|
||||||
// caTrust.bindMount
|
// caTrust.bindMount
|
||||||
# The host's journal, read-only, and only when something will read it.
|
# The host's journal, read-only, and only when something will read it.
|
||||||
|
|
|
||||||
|
|
@ -155,11 +155,11 @@ fn build_provider(interval: Duration) -> Result<SdkMeterProvider> {
|
||||||
/// No CA handling here unlike [`swarm_queue_client::QueueConfig::ca_file`]
|
/// No CA handling here unlike [`swarm_queue_client::QueueConfig::ca_file`]
|
||||||
/// (which `mint_token_for_blocking` already applies to the TOKEN endpoint
|
/// (which `mint_token_for_blocking` already applies to the TOKEN endpoint
|
||||||
/// internally): the metrics endpoint is reached through the gateway with a
|
/// internally): the metrics endpoint is reached through the gateway with a
|
||||||
/// certificate the swarm CA issued, and this whole process already trusts
|
/// certificate the store's `pki` mount issued, and this whole process
|
||||||
/// that CA via `SSL_CERT_FILE` (`hive-ca-trust.nix`, applied host-wide to
|
/// already trusts that mount's root via `SSL_CERT_FILE` (`hive-ca-trust.nix`,
|
||||||
/// this unit for the same reason the forge client needs no special
|
/// applied host-wide to this unit for the same reason the forge client needs
|
||||||
/// handling either) — a second, narrower trust bundle here would just be
|
/// no special handling either) — a second, narrower trust bundle here would
|
||||||
/// the same fact stated twice.
|
/// just be the same fact stated twice.
|
||||||
pub(crate) fn authenticated_http_client() -> Result<crate::otel_http_client::AuthenticatedHttpClient>
|
pub(crate) fn authenticated_http_client() -> Result<crate::otel_http_client::AuthenticatedHttpClient>
|
||||||
{
|
{
|
||||||
let cfg = crate::queue_identity::get().cloned().ok_or_else(|| {
|
let cfg = crate::queue_identity::get().cloned().ok_or_else(|| {
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl Config {
|
||||||
// `QueueConfig::from_env`: a swarm behind a publicly-trusted
|
// `QueueConfig::from_env`: a swarm behind a publicly-trusted
|
||||||
// certificate needs no extra anchor, so a CA path with no endpoint is
|
// certificate needs no extra anchor, so a CA path with no endpoint is
|
||||||
// meaningless rather than half-configured. Nothing in this tree sets
|
// meaningless rather than half-configured. Nothing in this tree sets
|
||||||
// it for an agent — a container already trusts the swarm root, which
|
// it for an agent — a container already trusts the store's root, which
|
||||||
// `hive_c0re::meta` embeds at build time.
|
// `hive_c0re::meta` embeds at build time.
|
||||||
let ca_file = var("OIDC_CA_FILE").map(PathBuf::from);
|
let ca_file = var("OIDC_CA_FILE").map(PathBuf::from);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue