control-plane-api, agent: capability-token validity is configurable - #3433
Closed
bbartman wants to merge 3 commits into
Closed
control-plane-api, agent: capability-token validity is configurable#3433bbartman wants to merge 3 commits into
bbartman wants to merge 3 commits into
Conversation
Task 6 of #3376: close the remaining surfaces a masked bearer must not reach. Forbidden::require_unmasked is the one definition of the masked-bearer refusal - keyed on the capability_mask claim's presence, never its value - consumed by requirement evaluation at extraction, by the capability_token mint, and directly by GraphQL resolvers, which an axum extractor cannot reach. createRefreshToken refuses masked bearers ahead of its service-account lookup: a refresh token exchanges for a full-authority access token, which would escape the mask. Revocation never widens authority and stays open to masked bearers, with a test pinning both. /admin/create-data-plane and /admin/update-l2-reporting take Authority<RequireUnmasked>: their SQL internal.user_roles authorization cannot bind the capability ceiling, so they fail closed for masked bearers, byte-identical for unmasked callers. Each gains its first tests: the structured 403 for a masked bearer, and the handler's own ops/-admin refusal for an unmasked one. The RequireUnmasked doc carries the audited inventory of unmasked-only surfaces and why everything else deliberately stays open; the SQL functions reachable through PostgREST remain the documented mask-bypass boundary tracked under #3376 task 8. The GraphQL schema regen also picks up the userCapability description from the legacy-decision migration, which had not been regenerated.
revokeApiKey authorizes RevokeApiKey through the mask-filtered grant walk; it is not an identity-gated revocation, and grouping it with revokeRefreshToken misstated why it needs no unmasked guard.
The validity window of capability_token mints is an App field fed by the agent's --capability-token-validity / CAPABILITY_TOKEN_VALIDITY setting (humantime syntax), following the pattern of the controller duration settings. Its 1h default matches the SQL generate_access_token mint, so an unconfigured deployment mints exactly the window it always has; an override diverges from the SQL mint's fixed hour deliberately, and a longer window widens the exposure of a leaked masked token — the flag docs say so. A unit test pins a non-default window at the signing seam; the HTTP test exercises the harness-configured default.
bbartman
force-pushed
the
bmb/3376-stack-8-unmasked-guards
branch
from
August 28, 2026 12:05
062c784 to
a9c1cb1
Compare
Contributor
Author
|
Folded into #3424 (task 5's PR) rather than landing as a separate stacked PR: the configurable validity is a refinement of the mint itself, so it lives with it. The relocated commit also consolidates the default into a shared DEFAULT_CAPABILITY_TOKEN_VALIDITY constant with a weld test pinning the clap default string to it. #3427 is rebased on the updated base. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3424 (stacked on #3427): the validity window of
capability_tokenmints becomes a deploy-time setting instead of a hardcoded constant.What this does
Appgains acapability_token_validityfield, fed by a new agent setting--capability-token-validity/CAPABILITY_TOKEN_VALIDITY(humantime syntax, e.g.1h,30m), following the existing pattern of the controller duration settings (CONTROLLER_DEQUEUE_INTERVAL,CONTROLLER_HEARTBEAT_TIMEOUT).sign_capability_tokentakes the validity as a parameter — it stays pure; no env reads at request time.1h, matching the SQLgenerate_access_tokenmint, so an unconfigured deployment (production today, local stacks, test harnesses) mints exactly the window it always has. No deploy changes are required anywhere; overriding is one line indeploy-agent-api.yaml'senv_varsor a local stack's agent env file.Deliberate trade-offs (noted on #3376)
interval '1 hour'— claim-set parity between the two mints is unaffected, but the windows differ when configured. The flag docs say so.Tests
test_validity_is_configurablepins a non-default (90s) window at the signing seam; the end-to-end mint test continues to exercise the harness-configured one-hour default.