swarm-queue-client: one agent-token spelling, and no hive in AgentCredential
`swarm_queue_client::agent_token::format_agent_token` / `parse_agent_token` are the spelling an agent presents its own queue secret in, `swarm-agent.<agent>.<secret>`, and the one the auth-callout responder reads back. The prefix is what separates it from an OIDC access token, which may itself contain `.`. Parsing distinguishes "not an agent token" (no prefix) from "a malformed one"; the error names the problem and never the value. The module is store-free, so the agent formats its token without linking the secret-store client. `swarm_secret_client::queue::AgentCredential` loses `hive`: an agent's identity is not tied to a hive, and nothing reads the field. Objects already in the store carry it and still decode, since unknown fields are ignored; a test parses one. The controller stops writing it. With the credential no longer naming a hive, and the agent's policy naming none since #4762, nothing in the mint consumes one. `hive` goes from `mint_and_verify`, from the `MintAgentIdentity` node, and from `POST /api/agents/{name}/identity`, which now takes no body and no longer checks a hive against the roster; a caller that still sends one is not refused, the body is ignored. `swarmctl agent mint-identity` loses `--hive`, so passing it is now a usage error.
This commit is contained in:
parent
6170e74a31
commit
fc97c237dc
10 changed files with 234 additions and 201 deletions
|
|
@ -136,7 +136,7 @@ fn generate_queue_secret() -> Result<String> {
|
|||
/// Anything that stops one of those five steps, with the step named. A
|
||||
/// failure here fails the job node and nothing else — the agent is still
|
||||
/// created, without a store identity.
|
||||
pub async fn mint_and_verify(agent: &str, hive: &str) -> Result<()> {
|
||||
pub async fn mint_and_verify(agent: &str) -> Result<()> {
|
||||
let (mount, pki_role) = agent_pki(|k| std::env::var(k).ok())?;
|
||||
let name = policy::agent_object_name(agent)?;
|
||||
let path = mtls::identity_path(agent)?;
|
||||
|
|
@ -161,18 +161,15 @@ pub async fn mint_and_verify(agent: &str, hive: &str) -> Result<()> {
|
|||
.read_optional(&queue_path)
|
||||
.await
|
||||
.with_context(|| format!("checking whether {queue_path} already holds a credential"))?;
|
||||
// The secret survives a re-run; the principal it names does not get to.
|
||||
// An object whose `hive` disagrees with the hive this node was invoked
|
||||
// with would grant its holder subjects on the wrong hive, so it is
|
||||
// corrected — but by rewriting the two name fields around the *same*
|
||||
// `value`, which is a correction no live connection notices.
|
||||
// The secret survives a re-run. An object naming a different agent is
|
||||
// corrected by rewriting the name around the *same* `value`, which no live
|
||||
// connection notices.
|
||||
let wanted = queue::AgentCredential {
|
||||
value: match &existing {
|
||||
Some(existing) => existing.value.clone(),
|
||||
None => generate_queue_secret()?,
|
||||
},
|
||||
agent: agent.to_owned(),
|
||||
hive: hive.to_owned(),
|
||||
};
|
||||
if existing.as_ref() == Some(&wanted) {
|
||||
tracing::info!(
|
||||
|
|
@ -187,9 +184,8 @@ pub async fn mint_and_verify(agent: &str, hive: &str) -> Result<()> {
|
|||
.with_context(|| format!("publishing the agent queue credential at {queue_path}"))?;
|
||||
tracing::info!(
|
||||
agent,
|
||||
hive,
|
||||
%queue_path,
|
||||
// Never "rotated": the secret is the same one, only the names
|
||||
// Never "rotated": the secret is the same one, only the name
|
||||
// around it moved.
|
||||
corrected = existing.is_some(),
|
||||
"agent queue credential published"
|
||||
|
|
@ -280,9 +276,9 @@ async fn read_back_as_agent(
|
|||
.read(queue_path)
|
||||
.await
|
||||
.with_context(|| format!("reading {queue_path} back under {role}'s own token"))?;
|
||||
// The two name fields are compared as well as the secret: they are what
|
||||
// the verifying end will grant subjects from, so a mismatch here is the
|
||||
// same class of fault as an unreadable path.
|
||||
// The agent is compared as well as the secret: the verifying end refuses
|
||||
// an object naming a different agent than the path, so a mismatch here is
|
||||
// the same class of fault as an unreadable path.
|
||||
if read_back != *queue_credential {
|
||||
bail!("the store returned a different object at {queue_path} than the one just published");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue