From 099af86cd2855423a4c47e3441848a248c601466 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 28 Oct 2026 20:18:18 +0100 Subject: [PATCH] raise the cert role token ttl to 1h --- docs/swarm/credentials.md | 12 ++++++++---- nix/host-modules/swarm-bao.nix | 2 +- nix/module-eval/bao-grants.nix | 16 +++++++++++----- swarm-secret-client/src/client.rs | 6 +++--- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/swarm/credentials.md b/docs/swarm/credentials.md index 997e0fad..35bd3544 100644 --- a/docs/swarm/credentials.md +++ b/docs/swarm/credentials.md @@ -456,8 +456,8 @@ responders log in on every check, and the store keeps no record of a batch token so unauthenticated requests don't grow its token storage. Nothing can renew or revoke a batch token. -The other roles issue service tokens that live 15 minutes (`token_ttl=15m` and -`token_max_ttl=15m`). That covers the roles `swarm-bao.nix` writes, and the +The other roles issue service tokens that live an hour (`token_ttl=1h` and +`token_max_ttl=1h`). That covers the roles `swarm-bao.nix` writes, and the agent and hive roles the controller writes through `SecretStore::write_cert_role`, which takes the same value from `CERT_ROLE_TOKEN_TTL`. The store keeps a lease for every token until it @@ -465,10 +465,14 @@ expires, and the mount default of 32 days leaves one lease per login for a month; at the 17 thousand logins a day this swarm made, that came to half a million leases. A reader logs in per operation and holds the token for seconds. The longest hold is the controller's forge work, which stops after two minutes. -A client that keeps a `SecretStore` handle longer than 15 minutes must log in +A client that keeps a `SecretStore` handle longer than an hour must log in again, because nothing renews the token. A role the controller writes takes the TTL at its next write: a hive's at the controller's start, an agent's at its -next certificate renewal. +next certificate renewal. At that rate an hour keeps about 700 tokens live. + +The granter's role keeps 15 minutes. Only the one-time bootstrap token writes +it, and a deployed store never rewrites it, so the longer TTL would not reach +it; the granter logs in and finishes within seconds. ### What a missing leaf costs diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index 02f82ec2..7a79b2e4 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -995,7 +995,7 @@ let # within two minutes (the controller's forge work is cut off at # `FORGE_DEADLINE`). Rust-written roles take `CERT_ROLE_TOKEN_TTL`, which # is the same value. - certRoleTokenTtl = "15m"; + certRoleTokenTtl = "1h"; # The agent CA's certificate, cached on this host by `swarm-bao-agent-pki`, # so `swarm-bao-certs` can compose the listener's bundle before the store diff --git a/nix/module-eval/bao-grants.nix b/nix/module-eval/bao-grants.nix index 0aff4049..9a5f25d3 100644 --- a/nix/module-eval/bao-grants.nix +++ b/nix/module-eval/bao-grants.nix @@ -907,8 +907,8 @@ let && batch "swarm-bao-metrics-auth-policy" "swarm-metrics-auth" && lib.hasInfix "token_policies=swarm-ci-runner" ci && !(lib.hasInfix "token_type" ci) - && lib.hasInfix "token_ttl=15m" ci - && lib.hasInfix "token_max_ttl=15m" ci; + && lib.hasInfix "token_ttl=1h" ci + && lib.hasInfix "token_max_ttl=1h" ci; } { name = "the PKI unit signs the metrics store responder's leaf under its own subject"; @@ -1476,8 +1476,9 @@ let # A cert login leaves a lease until its token expires, and a role without # `token_ttl` gets the mount's default of 32 days. Every role write in # every unit is read, so the TTL must be on the command itself, not - # somewhere else in the script. A role is a service token at `15m`, or a - # batch token at `5m`. + # somewhere else in the script. A role is a service token at `1h`, or a + # batch token at `5m`. The granter keeps `15m`: only the bootstrap token + # writes its role, so a deployed store does not rewrite it. # # The presence control: the roles found include the controller's, the # granter's, a batch responder's, the CI runner's and a per-hive leaf @@ -1508,7 +1509,12 @@ let writes = roleWrites baoGrantHere ++ roleWrites baoGrantTwoHives; short = w: - (lib.hasInfix "token_ttl=15m" w.command && lib.hasInfix "token_max_ttl=15m" w.command) + (lib.hasInfix "token_ttl=1h" w.command && lib.hasInfix "token_max_ttl=1h" w.command) + || ( + w.role == "bao-granter" + && lib.hasInfix "token_ttl=15m" w.command + && lib.hasInfix "token_max_ttl=15m" w.command + ) || ( lib.hasInfix "token_type=batch" w.command && lib.hasInfix "token_ttl=5m" w.command diff --git a/swarm-secret-client/src/client.rs b/swarm-secret-client/src/client.rs index 778ece34..17a236e3 100644 --- a/swarm-secret-client/src/client.rs +++ b/swarm-secret-client/src/client.rs @@ -35,7 +35,7 @@ pub const DEFAULT_CERT_MOUNT: &str = "cert"; /// it leaves one lease per login for the mount's default of 32 days. /// [`SecretStore`] holds the token it logs in with and renews nothing: log in /// again rather than keep a handle past this. -pub const CERT_ROLE_TOKEN_TTL: &str = "15m"; +pub const CERT_ROLE_TOKEN_TTL: &str = "1h"; /// Upper bound on one request to the store, login included. /// @@ -738,8 +738,8 @@ mod tests { #[test] fn a_cert_role_is_written_with_short_lived_tokens() { let r = cert_role_request("cert", "hive-agent-a", "PEM", "hive-agent-a", &["shared"]); - assert_eq!(r.token_ttl.as_deref(), Some("15m")); - assert_eq!(r.token_max_ttl.as_deref(), Some("15m")); + assert_eq!(r.token_ttl.as_deref(), Some("1h")); + assert_eq!(r.token_max_ttl.as_deref(), Some("1h")); // The control: the rest of the role is still written. assert_eq!( r.allowed_common_names,