[3/3] Self-mint IAP tokens for Oz runners via Workload Identity Federation#13624
[3/3] Self-mint IAP tokens for Oz runners via Workload Identity Federation#13624IsaiahWitzke wants to merge 2 commits into
Conversation
Sandboxed Oz runners authenticate to staging through IAP using a server-injected WARP_IAP_TOKEN minted as the warp-server service account. That identity isn't an authorized IAP member (403s), and the injected token lands in the terminal EnvInjected state which never refreshes (REMOTE-1370). This gives IapManager a runner-context refresh path that self-mints an IAP-valid ID token via WIF, mirroring the REV-1599 GEAP mint chain: 1. Warp OIDC JWT (audience = the oz-agents WIF provider resource name) 2. GCP STS token exchange -> federated access token 3. IAM Credentials generateIdToken impersonating iap-access@ (audience = the IAP OAuth client ID), which IS an authorized IAP member The minted token flows through the existing IapManager lifecycle (proactive refresh before expiry, reactive refresh on 403), closing the no-refresh gap. Native HTTP is used throughout since the runner image ships without gcloud. - IapConfig gains federation_audience (the WIF provider resource name); the private staging channel config must populate it for this to activate. - The mint is gated on runner context (ambient-agent run id), so local staging clients keep using the gcloud impersonation path unchanged. - The env-injected token is kept as an initial seed and served until the first WIF mint lands, then refreshed like any managed token. Co-Authored-By: Oz <oz-agent@warp.dev>
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds a runner-context IAP refresh path that mints tokens via Workload Identity Federation and wires the app-layer managed-secrets minter into IapManager. No approved spec context was provided, so this review focuses on the implementation and security posture of the diff.
Concerns
- The new app-layer minter uses
FutureExt::boxed(), which requires aSendfuture even though this module is compiled for wasm whereManagedSecretsClientmethods areasync_trait(?Send). - The IAP challenge path now suppresses the existing stale env-token warning whenever a managed minter exists, even when the channel config is missing
federation_audienceand the refresh path will still no-op.
Verdict
Found: 0 critical, 2 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| .await?; | ||
| Ok(token.token) | ||
| } | ||
| .boxed() |
There was a problem hiding this comment.
.boxed() requires the async block to be Send; ManagedSecretsClient::issue_task_identity_token is async_trait(?Send) on wasm, so wasm builds can fail here. Use a wasm-local boxed future (or cfg-gate this module/impl to native) so the return matches the target-specific BoxFuture alias.
| if matches!(state.state(), IapCredentialsState::EnvInjected { .. }) { | ||
| // An env-injected token with no managed-mint fallback cannot be | ||
| // refreshed (no gcloud in the runner, no WIF configured). | ||
| if self.managed_mint.is_none() |
There was a problem hiding this comment.
federation_audience is absent; start_refresh then hits the EnvInjected no-op below, so a missing rollout config silently keeps failing IAP challenges. Treat missing federation config as no refresh path here, or emit a specific warning before returning.
The env-injected token is minted as the warp-server service account, which isn't an authorized IAP member, so it 403s today and provides no working fallback. With WIF self-minting in place there's no reason to keep it. Drops the EnvInjected state variant and all its handling (seed, get_cached, previous_token, challenge/refresh special-cases, and the settings-page status arm). Runner IAP now depends entirely on the WIF mint. Co-Authored-By: Oz <oz-agent@warp.dev>
What
Gives
IapManagera runner-context refresh path so sandboxed Oz runners self-mint an IAP-valid ID token via Workload Identity Federation, and removes the old server-injectedWARP_IAP_TOKENpath from the client entirely.Why
After IAP was enabled on staging, sandboxed runners 403 at the proxy:
warp-server minted the injected token as its own service-account identity (
MintIAPTokenForContainer→idtoken.NewTokenSource), which isn't an authorized IAP member — so the injected token 403s and offers no working fallback. It also landed in the terminalEnvInjectedstate, which never refreshed (REMOTE-1370). The runner container has nogcloud/ADC, so the existing gcloud path can't run there either.How
New
fetch_iap_token_via_wifmirrors the REV-1599 GEAP mint chain, but ends in an ID token for IAP:issue_task_identity_token(audience = theoz-agentsWIF provider resource name)generateIdTokenimpersonatingiap-access@(audience = the IAP OAuth client ID) —iap-access@is an authorized IAP memberThe result flows through the existing
IapManagerlifecycle (proactive refresh beforeexp, reactive on 403). All native HTTP — nogcloud.Also removes the
WARP_IAP_TOKEN/EnvInjectedhandling from the client (state variant, seed, challenge/refresh special-cases, settings-page arm), since that token is the brokenwarp-server@identity and provided no working fallback. Runner IAP now depends entirely on the WIF mint.Key details:
IapConfiggainsfederation_audience(the WIF provider resource name). The private staging channel config must populate it for this to activate (same overlay that setsaudiences/service_account_email); OSSproduction()leaves IAPNone.IapIdentityTokenMinter(impl in the app crate over the managed-secrets client) sowarp_server_clientdoesn't depend on the managed-secrets stack.Dependencies / rollout
serviceAccountTokenCreatoroniap-access@(separate PR).federation_audience.WARP_IAP_TOKEN(separate change); the client already ignores it as of this PR.Testing
cargo test -p warp_server_client --lib iap— 18 passing (WIF request/response serde, camelCaseincludeEmail, expiry parsing).cargo check -p warp_server_clientandcargo check -p warppass;cargo clippy -p warp_server_client --all-targetsclean.Conversation
Co-Authored-By: Oz oz-agent@warp.dev