mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
## Why Allow remote exec servers to connect directly to AWS-hosted registries that authenticate registry requests and WebSocket handshakes with AWS SigV4. ## What changed - Add `--remote-transport direct` with SigV4 profile, region, and service options while keeping Noise as the default transport. - Register the `direct_jsonrpc_v1` transport and carry plain exec-server JSON-RPC messages over the authenticated WebSocket. - Reuse direct registrations across transient disconnects, refresh them after a `409 Conflict`, and require TLS for non-loopback endpoints. ## Testing - Cover CLI validation and SigV4 request signing. - Exercise direct registration, handshake retry behavior, JSON-RPC interoperability, and process recovery after reconnecting. GitOrigin-RevId: 0755df330ba3abe5db0a516fdaa49338d9bbe2d2
78 lines
2.5 KiB
Rust
78 lines
2.5 KiB
Rust
//! CLI-only glue between Codex authentication and generic AWS signing.
|
|
//!
|
|
//! Keeping this adapter here leaves `codex-api` and `codex-aws-auth` independent.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use codex_api::AuthError;
|
|
use codex_api::AuthProvider;
|
|
use codex_api::SharedAuthProvider;
|
|
use codex_aws_auth::AwsAuthConfig;
|
|
use codex_aws_auth::AwsAuthContext;
|
|
use codex_aws_auth::AwsAuthError;
|
|
use codex_aws_auth::AwsRequestToSign;
|
|
use codex_http_client::Request;
|
|
use codex_http_client::RequestBody;
|
|
use codex_http_client::RequestCompression;
|
|
use http::HeaderMap;
|
|
|
|
/// Creates a SigV4 provider, preferring an explicit profile over the default credential chain.
|
|
pub(super) async fn aws_sigv4_auth_provider(
|
|
mut config: AwsAuthConfig,
|
|
) -> Result<SharedAuthProvider, AwsAuthError> {
|
|
config.profile = config
|
|
.profile
|
|
.map(|profile| profile.trim().to_string())
|
|
.filter(|profile| !profile.is_empty());
|
|
config.region = config
|
|
.region
|
|
.map(|region| region.trim().to_string())
|
|
.filter(|region| !region.is_empty());
|
|
let context = if config.profile.is_some() {
|
|
AwsAuthContext::load_profile(config).await
|
|
} else {
|
|
AwsAuthContext::load(config).await
|
|
}?;
|
|
Ok(Arc::new(AwsSigV4AuthProvider { context }))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct AwsSigV4AuthProvider {
|
|
context: AwsAuthContext,
|
|
}
|
|
|
|
impl AuthProvider for AwsSigV4AuthProvider {
|
|
fn add_auth_headers(&self, _headers: &mut HeaderMap) {}
|
|
|
|
fn apply_auth(&self, mut request: Request) -> codex_api::AuthProviderFuture<'_> {
|
|
Box::pin(async move {
|
|
let prepared = request.prepare_body_for_send().map_err(AuthError::Build)?;
|
|
let signed = self
|
|
.context
|
|
.sign(AwsRequestToSign {
|
|
method: request.method.clone(),
|
|
url: request.url.clone(),
|
|
headers: prepared.headers.clone(),
|
|
body: prepared.body_bytes(),
|
|
})
|
|
.await
|
|
.map_err(|error| {
|
|
if error.is_retryable() {
|
|
AuthError::Transient(error.to_string())
|
|
} else {
|
|
AuthError::Build(error.to_string())
|
|
}
|
|
})?;
|
|
request.url = signed.url;
|
|
request.headers = signed.headers;
|
|
request.body = prepared.body.map(RequestBody::Raw);
|
|
request.compression = RequestCompression::None;
|
|
Ok(request)
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "exec_server_auth_tests.rs"]
|
|
mod tests;
|