swarm-controller: create each hive's cert-auth role at startup
A hive holds an mTLS pair and a policy naming what it may read, and still cannot log in: nothing creates the role that maps its certificate to that policy. The one pre-shared credential in the system therefore buys no access. Minting happens here rather than in nix, which was the first plan. Nix mints from the store's own container, and that path is gated on the bootstrap token -- so onboarding a hive later would mean placing the one genuinely pre-shared secret again. Doing it from the controller costs a public certificate authority as an input and makes the bootstrap token one-time. A startup pass, not a hook: the hive list is loaded once and a config change means a redeploy, so the roles are as static as the list. Only the policy is derived from something that moves. The subject is the hive's name because glue-bao-tls.nix mints a hive's client leaf with its name as the CN, and cert auth matches on that. Per-hive failures are logged and skipped, matching the queue, bridge and forge connects above it: a controller whose store is unreachable still serves everything else, and the next start retries. Not covered by a test: ensure_hive_roles is IO from end to end, and the seam that would make it assertable is the one the read-grant sink already has. Said here rather than implied by a green suite.
This commit is contained in:
parent
7f9e65e923
commit
4007fc965d
3 changed files with 104 additions and 16 deletions
|
|
@ -167,6 +167,39 @@ impl SecretStore {
|
|||
vaultrs::sys::policy::set(&self.inner, name, policy).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create or replace the cert-auth role `name`, so that a certificate
|
||||
/// issued by `certificate` and carrying `common_name` logs in with
|
||||
/// `policy`.
|
||||
///
|
||||
/// `certificate` is the authority by value, not a path: the store keeps
|
||||
/// its own copy inside the role and never reads a file of ours.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Vault`] when the token's policy does not cover
|
||||
/// `auth/<mount>/certs/<name>`, or the store rejects the authority.
|
||||
pub async fn write_cert_role(
|
||||
&self,
|
||||
mount: &str,
|
||||
name: &str,
|
||||
certificate: &str,
|
||||
common_name: &str,
|
||||
policy: &str,
|
||||
) -> Result<(), Error> {
|
||||
let mut opts =
|
||||
vaultrs::api::auth::cert::requests::CreateCaCertificateRoleRequest::builder();
|
||||
opts.allowed_common_names(vec![common_name.to_owned()])
|
||||
.token_policies(vec![policy.to_owned()]);
|
||||
vaultrs::auth::cert::ca_cert_role::set(
|
||||
&self.inner,
|
||||
mount,
|
||||
name,
|
||||
certificate,
|
||||
Some(&mut opts),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue