swarm-controller: re-issue agent certificates and re-mint queue secrets at half-life
A five-minute pass over every agent some hive's wanted state declares as anything but destroyed queues, per agent: - `MintAgentIdentity` (the node agent creation uses) when the stored certificate at swarm/agents/<agent>/bao-mtls is past half its validity, read from its own notBefore/notAfter: day 45 of the role's 90; - the new `RenewAgentQueueCredential` node when the queue secret at swarm/agents/<agent>/queue is 45 days old or has no mint time. The node re-decides, writes a fresh value with `minted_at`, reads it back, and logs the agent and the old age. When both are due the secret node runs after_any the certificate node, because mint_and_verify compares the queue secret it read with the one it reads back. A credential that is not stored is never created here. `queue::AgentCredential` gains an optional `minted_at` (unix seconds); agent creation now sets it. Stored objects without it decode unchanged and count as due, so every existing queue secret is re-minted on the first pass. Both replacements reach the agent at its next start. The old certificate stays valid until it expires; the old queue secret does not, so a queue reconnect before that restart is denied. Adds x509-cert 0.2 (with der_derive and flagset) to read the validity. docs/swarm/credentials.md: the renewal column splits into automatic re-mint and automatic re-pull, filled from the code as it stands.
This commit is contained in:
parent
b14ff2796c
commit
2115ec2bb3
10 changed files with 763 additions and 29 deletions
|
|
@ -101,6 +101,15 @@ pub struct AgentCredential {
|
|||
|
||||
/// The agent this secret authenticates.
|
||||
pub agent: String,
|
||||
|
||||
/// When `value` was minted, in unix seconds. `swarm-controller` re-mints
|
||||
/// the secret once this is old enough, and treats `None` as due.
|
||||
///
|
||||
/// `Option` because objects written before this field existed lack it and
|
||||
/// must still decode; skipped when `None` so such an object re-serialises
|
||||
/// unchanged.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub minted_at: Option<i64>,
|
||||
}
|
||||
|
||||
/// What the path holds: the client secret, plus the client id it belongs to.
|
||||
|
|
@ -255,6 +264,7 @@ mod tests {
|
|||
let c = AgentCredential {
|
||||
value: "s3cr3t".to_owned(),
|
||||
agent: "atlas".to_owned(),
|
||||
minted_at: Some(1_790_000_000),
|
||||
};
|
||||
let json = serde_json::to_string(&c).expect("serialises");
|
||||
assert_eq!(
|
||||
|
|
@ -268,10 +278,12 @@ mod tests {
|
|||
let json = serde_json::to_value(AgentCredential {
|
||||
value: "s3cr3t".to_owned(),
|
||||
agent: "atlas".to_owned(),
|
||||
minted_at: Some(1_790_000_000),
|
||||
})
|
||||
.expect("serialises");
|
||||
assert_eq!(json["value"], "s3cr3t");
|
||||
assert_eq!(json["agent"], "atlas");
|
||||
assert_eq!(json["minted_at"], 1_790_000_000);
|
||||
assert!(json.get("hive").is_none(), "{json}");
|
||||
}
|
||||
|
||||
|
|
@ -287,10 +299,21 @@ mod tests {
|
|||
AgentCredential {
|
||||
value: "s3cr3t".to_owned(),
|
||||
agent: "atlas".to_owned(),
|
||||
minted_at: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// An object with no mint time decodes, and re-serialises without a
|
||||
/// `null` in its place.
|
||||
#[test]
|
||||
fn an_agent_object_without_a_mint_time_decodes_and_round_trips_unchanged() {
|
||||
let raw = r#"{"value":"s3cr3t","agent":"atlas"}"#;
|
||||
let c: AgentCredential = serde_json::from_str(raw).expect("decodes");
|
||||
assert_eq!(c.minted_at, None);
|
||||
assert_eq!(serde_json::to_string(&c).expect("serialises"), raw);
|
||||
}
|
||||
|
||||
/// The agent is required: it is what the verifier checks the presented
|
||||
/// name against.
|
||||
#[test]
|
||||
|
|
@ -315,6 +338,7 @@ mod tests {
|
|||
let agent_json = serde_json::to_string(&AgentCredential {
|
||||
value: "s".to_owned(),
|
||||
agent: "atlas".to_owned(),
|
||||
minted_at: None,
|
||||
})
|
||||
.expect("serialises");
|
||||
assert!(serde_json::from_str::<Credential>(&agent_json).is_err());
|
||||
|
|
|
|||
Loading…
Reference in a new issue