Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2162,7 +2162,21 @@ pub(crate) fn initialize_app(
ctx.add_singleton_model(system::SystemInfo::new);
}

// `IapManager` drives gcloud-based IAP token refresh for staging builds.
// In a sandboxed Oz runner, mint IAP tokens by self-minting via Workload
// Identity Federation (impersonating the IAP access service account). Gated
// on runner context (ambient-agent run id) so local staging clients keep
// using the gcloud path.
#[cfg(not(target_family = "wasm"))]
let managed_iap_mint = std::env::var(warp_cli::OZ_RUN_ID_ENV).is_ok().then(|| {
let client = server_api_provider.as_ref(ctx).get_managed_secrets_client();
warp_server_client::iap::ManagedIapMint::new(Arc::new(
crate::server::iap_identity_minter::ManagedSecretsIapMinter::new(client),
))
});
#[cfg(target_family = "wasm")]
let managed_iap_mint: Option<warp_server_client::iap::ManagedIapMint> = None;

// `IapManager` drives IAP token refresh for staging builds.
// Register it after `LocalShellState`: the Manager needs to know where the gcloud
// cli lives & thus needs PATH config set by ~/.zshrc et al.
//
Expand All @@ -2184,7 +2198,7 @@ pub(crate) fn initialize_app(

path_future
});
IapManager::new(iap_state, path_resolver, ctx)
IapManager::new(iap_state, path_resolver, managed_iap_mint, ctx)
});
// Subscribe to IAP manager events to show toasts when refresh fails.
ctx.subscribe_to_model(&IapManager::handle(ctx), |_, e, ctx| {
Expand Down
1 change: 1 addition & 0 deletions app/src/pane_group/mod_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ fn initialize_app(app: &mut App) {
IapManager::new(
None,
Box::new(|_| futures::FutureExt::boxed(futures::future::ready(None::<String>))),
None,
ctx,
)
});
Expand Down
43 changes: 43 additions & 0 deletions app/src/server/iap_identity_minter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::sync::Arc;
use std::time::Duration;

use futures::FutureExt as _;
use vec1::vec1;
use warp_managed_secrets::client::{IdentityTokenOptions, ManagedSecretsClient};
use warp_server_client::iap::IapIdentityTokenMinter;
use warpui::r#async::BoxFuture;

/// Mints Warp-signed OIDC identity tokens for the runner-context IAP Workload
/// Identity Federation flow, backed by the managed-secrets client. Lives in the
/// app crate so `warp_server_client` need not depend on the managed-secrets
/// stack.
pub struct ManagedSecretsIapMinter {
client: Arc<dyn ManagedSecretsClient>,
}

impl ManagedSecretsIapMinter {
pub fn new(client: Arc<dyn ManagedSecretsClient>) -> Self {
Self { client }
}
}

impl IapIdentityTokenMinter for ManagedSecretsIapMinter {
fn mint_identity_token(
&self,
audience: String,
requested_duration: Duration,
) -> BoxFuture<'static, anyhow::Result<String>> {
let client = self.client.clone();
async move {
let token = client
.issue_task_identity_token(IdentityTokenOptions {
audience,
requested_duration,
subject_template: vec1!["principal".to_string()],
})
.await?;
Ok(token.token)
}
.boxed()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This module is compiled on wasm, but .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.

}
}
1 change: 1 addition & 0 deletions app/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod block;
pub mod cloud_objects;
pub mod experiments;
pub mod graphql;
pub mod iap_identity_minter;
pub mod ids;
pub mod network_log_pane_manager;
pub mod network_log_view;
Expand Down
3 changes: 0 additions & 3 deletions app/src/settings_view/main_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,9 +1114,6 @@ impl SettingsWidget for IapCredentialsWidget {
(format!("Loaded (refreshes in ~{mins}m)"), active)
}
IapCredentialsState::Failed { message, .. } => (format!("Failed: {message}"), ansi_red),
IapCredentialsState::EnvInjected { .. } => {
("Using injected token (WARP_IAP_TOKEN)".to_string(), active)
}
};

let is_refreshing = matches!(state, IapCredentialsState::Refreshing { .. });
Expand Down
1 change: 1 addition & 0 deletions app/src/terminal/shared_session/sharer/network_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ fn test_messages_are_buffered_while_reconnecting() {
IapManager::new(
None,
Box::new(|_| futures::FutureExt::boxed(futures::future::ready(None::<String>))),
None,
ctx,
)
});
Expand Down
1 change: 1 addition & 0 deletions app/src/test_util/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub fn initialize_app_for_terminal_view(app: &mut App) {
IapManager::new(
None,
Box::new(|_| futures::FutureExt::boxed(futures::future::ready(None::<String>))),
None,
ctx,
)
});
Expand Down
1 change: 1 addition & 0 deletions app/src/workspace/view_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub(crate) fn initialize_app(app: &mut App) {
warp_server_client::iap::IapManager::new(
None,
Box::new(|_| futures::FutureExt::boxed(futures::future::ready(None::<String>))),
None,
ctx,
)
});
Expand Down
6 changes: 6 additions & 0 deletions crates/warp_core/src/channel/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pub struct IapConfig {
pub audiences: Cow<'static, str>,
/// The service account email to impersonate when acquiring IAP credentials.
pub service_account_email: Cow<'static, str>,
/// The Workload Identity Federation provider resource name for the `oz-agents`
/// pool (`//iam.googleapis.com/projects/{num}/locations/global/workloadIdentityPools/{pool}/providers/{provider}`).
/// When set, sandboxed Oz runners self-mint IAP tokens by impersonating
/// [`Self::service_account_email`] via this provider.
#[serde(default)]
pub federation_audience: Option<Cow<'static, str>>,
}

#[derive(Debug, Deserialize, Serialize)]
Expand Down
Loading