Repository navigation
atenet: inject RFC 8693 exchanged tokens on egress - #3345
Open
Max Thompson (thompsonmax) wants to merge 8 commits into
Open
Max Thompson (thompsonmax) wants to merge 8 commits into
Max Thompson (thompsonmax) wants to merge 8 commits into
Conversation
Max Thompson (thompsonmax)
force-pushed
the
token-exchange-wiring
branch
2 times, most recently
from
October 27, 2026 21:39
f203453 to
38cde20
Compare
1 of 2 tasks
2 tasks done
Max Thompson (thompsonmax)
marked this pull request as ready for review
November 13, 2026 19:02
A replace_headers entry can now set token_exchange, naming an RFC 8693 token endpoint, an audience, and scopes. The egress gateway will exchange the actor's JWT there and inject the returned access token. ateapi requires token_url to be https with a DNS hostname and no userinfo, query, or fragment. It also rejects a policy unless one of its https rules allows the token_url's host and port, so the endpoint stays within what the policy already allows. EgressPolicyTemplate gets the same check. The gateway does not exchange tokens yet and denies a request that carries a token_exchange header with a 500. Without that case the entry would reach the credential provider with an empty URI. Signed-off-by: Max Thompson <maxthomp@google.com>
Egress policies will let the gateway exchange an actor JWT for an access token at an RFC 8693 endpoint. Add the client for that. It posts the token exchange form, caps the response at 1 MiB, follows no redirects, ignores proxy environment variables, and maps endpoint errors to gRPC codes that the gateway can turn into a 403, 500, or 503. Add --token-exchange-ca-file for endpoints with private certificates. The router builds the client at startup so a bad file fails startup. Nothing calls the client yet. Signed-off-by: Max Thompson <maxthomp@google.com>
Add tokenexchange.Broker, which mints a 300-second actor JWT bound to the exchange's audience, exchanges it, and caches the access token per actor, token URL, audience, and scopes. A token is reused until a third of its lifetime or 30 seconds remains, whichever comes first, and for 60 seconds when the endpoint returns no expires_in. Concurrent requests for one token share a single exchange. The exchange outlives a caller that gives up, so a slow endpoint still fills the cache for the next request, and it has its own 10-second deadline so an endpoint that never answers cannot hold the exchange forever. Mint failures wrap ErrMint so the gateway can map them like other actor JWT mint failures. Signed-off-by: Max Thompson <maxthomp@google.com>
TestSecretLikeFieldsAreLabeled flags fields whose names look like credentials. token_exchange holds an endpoint, an audience, and scopes, and token_url can carry no userinfo or query, so neither holds a secret. The token URL check now skips headers that use another source, and the token_url doc names the DNS hostname rule. The http-rule test case now fails on the protocol alone, and the scopes limits have test cases. Signed-off-by: Max Thompson <maxthomp@google.com>
A malformed token response's JSON error can quote the endpoint's own text, such as an expires_in number a megabyte long. Clip it like the other endpoint-supplied error fields. Exchange's doc now names each error code by outcome. New tests cover DEL and a space in access_token and a truncated response body, and the deadline test uses an expired context instead of waiting. Signed-off-by: Max Thompson <maxthomp@google.com>
Each token_exchange entry costs a pass over every rule when ateapi checks that an https rule allows its token URL. A policy with thousands of entries could cost seconds of CPU. Reject more than 16, the same bound as one rule's replace_headers, before running the check. Signed-off-by: Max Thompson <maxthomp@google.com>
A replace_headers entry with token_exchange now gets the access token the broker exchanges for the actor, where the gateway used to deny it with a 500. The gateway first checks that an https rule in the actor's policy allows the token URL's host and port, as ateapi does at admission, and denies with a 500 if none does. An endpoint that refuses the actor denies the request with a 403, a transient endpoint failure with a 503, and a misconfigured endpoint with a 500. A failed mint of the subject JWT maps the same way as for actor JWT injection. Signed-off-by: Max Thompson <maxthomp@google.com>
The egress gateway's --token-exchange-ca-file adds PEM certificates it trusts for token exchange endpoints with private certificates. ate-setup server-side applies the egress Deployment on every install, so a manual patch of the ext-proc args would not survive the next one. When the setting names a ConfigMap, the installer mounts its ca.crt into the ext-proc container and sets the flag. The admin owns the ConfigMap: deploy ate-system and deploy atenet fail before changing the cluster if it or its ca.crt key is missing. The setting requires the Envoy dataplane. Signed-off-by: Max Thompson <maxthomp@google.com>
Max Thompson (thompsonmax)
force-pushed
the
token-exchange-wiring
branch
from
November 13, 2026 19:32
38cde20 to
6fcd8b8
Compare
This branch has not been deployed
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.
Part of #1661. Stacked on #3344, which is stacked on #3334 and #3335. The last two commits are new.
applyEffectsinjects the access token the broker returns for atoken_exchangeentry, replacing the 500 from egress policy: add an RFC 8693 token exchange source to CredentialHeader #3334. It first checks that anhttpsrule in the actor's policy allows thetoken_url's host and port, as ateapi does at admission.mapActorJWTErrordoes.--token-exchange-ca-fileclient toegress.New, replacing theTODO(#1661)from atenet: add an RFC 8693 token exchange client to the egress gateway #3335.The request-time check repeats the admission check, so the gateway doesn't post an actor JWT to an endpoint the policy it enforces doesn't allow. The gateway reads the CA file at startup, so a ConfigMap change needs an
atenet-egressrestart.Testing: egress unit tests exchange against a TLS endpoint at
localhoston both legs, covering injection, a request without the header, a token URL no rule allows, and each status mapping. ate-setup tests cover rendering, a missing ConfigMap or key, and the setting checks. Each new test fails without its change. A module-widego test -race ./...passes exceptcmd/ateworker-microvm/internal/child, which fails on main on macOS too, and an atelet registry test that timed out under load and passes alone.hack/verify-all.shpasses.AI use: AI-assisted (Claude Code) for code, tests, and docs. I reviewed and tested it.