swarm-queue-client: re-read a held OIDC client secret when the IdP refuses it
hive-c0re (`hive-<name>`), swarm-controller and swarm-review-classifier read their queue client secret from the store once and held it as `ClientSecret::Value` for the life of the process. After authelia re-minted a secret and swarm-secret-publish stored the new one, every later token mint presented the old value and was refused until the daemon restarted. `ClientSecret::Stored` replaces `Value`: a `StoredSecret` holds the secret in memory with a caller-supplied read callback, so the crate takes no store dependency. When the token endpoint answers 400 or 401, `mint_token` (and its blocking sibling, used by the controller's OTLP exporters) re-reads it and, if the store holds a different secret, mints once more with it. One re-read runs at a time and is spawned so it outlives a caller that gives up. A re-read that fails or finds the refused value delays the next one, 5 s doubling to 5 min, so a secret that stays wrong costs one store read per delay. Logs name the client id and never the secret. swarm-nats-auth's `oidc_secret` was the template; its re-read machinery now sits on `StoredSecret`, so the tree has one copy. The responder gains the same re-read backoff. The controller keeps its `IDENTITY` OnceLock: it now holds a `QueueConfig` whose secret is re-read, and the lazily built vcs-metrics exporter reads the identity from it. agent_renewal.rs's module doc said a re-minted agent queue secret only reaches the agent at its next start; hive-agent reads the store on every connection attempt after its first, so the doc now says that. Part of #5017 (slice Q).
This commit is contained in:
parent
be9b2475a9
commit
a6935e57c3
11 changed files with 819 additions and 376 deletions
|
|
@ -7,8 +7,8 @@
|
|||
//! the controller reads the PR and sends it along.
|
||||
//!
|
||||
//! The store identity comes from the `BAO_*` environment. The queue client
|
||||
//! secret is read once at start and the API key once per run, both from the
|
||||
//! store under that identity.
|
||||
//! secret is read at start and again when authelia refuses it, the API key
|
||||
//! once per run, both from the store under that identity.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
|
@ -16,6 +16,7 @@ use std::time::Duration;
|
|||
use anyhow::{Context, Result, bail};
|
||||
use futures_util::StreamExt as _;
|
||||
use swarm_queue_client::review_classifier::{QUEUE_GROUP, Reply, SUBJECT, TryOut};
|
||||
use swarm_queue_client::stored_secret::StoredSecret;
|
||||
use swarm_queue_client::{ClientSecret, QueueConfig};
|
||||
|
||||
mod aggregate;
|
||||
|
|
@ -59,11 +60,16 @@ async fn serve() -> Result<()> {
|
|||
.context("finding this binary for the claude trampoline")?,
|
||||
};
|
||||
let secret = client_secret_with_retry(&client_id).await?;
|
||||
let reread_id = client_id.clone();
|
||||
let secret = StoredSecret::new(client_id.clone(), secret, move || {
|
||||
let client_id = reread_id.clone();
|
||||
async move { client_secret(&client_id).await.map_err(Into::into) }
|
||||
});
|
||||
let queue = swarm_queue_client::connect(QueueConfig {
|
||||
url: var("NATS_URL")?,
|
||||
token_endpoint: var("OIDC_TOKEN_ENDPOINT")?,
|
||||
client_id,
|
||||
client_secret: ClientSecret::Value(secret),
|
||||
client_secret: ClientSecret::Stored(secret),
|
||||
ca_file: std::env::var(format!("{PREFIX}_OIDC_CA_FILE"))
|
||||
.ok()
|
||||
.map(PathBuf::from),
|
||||
|
|
|
|||
Loading…
Reference in a new issue