mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
Add a hidden HTTP/3 TCP tunnel command (#45900)
## What changed Add `codex tcp-tunnel` and the `codex-tcp-tunnel` crate to forward loopback TCP connections to an explicit target through a TLS-verified HTTP/3 CONNECT proxy. - Require the proxy origin to match an approved HTTPS origin in a supplied policy file. - Read bearer tokens and optional bounded, non-forwarding `x-` headers from stdin. Support token updates for new connections, `LISTENING` and `AUTH_UPDATED` notifications, and shutdown when the control pipe closes in token-update mode. - Preserve the listener across proxy reconnects without replaying TCP streams, and let accepted streams continue while a proxy drains. ## Testing Add tests for hidden CLI parsing, proxy and target validation, credential renewal, control-pipe closure, and invalid input without secret disclosure. A local HTTP/3 proxy test covers token replacement, transport recovery without stream replay, and graceful draining. GitOrigin-RevId: c6af3025301c61e9fe90940cf5a6df0039465e69
This commit is contained in:
@@ -150,6 +150,9 @@ enum Subcommand {
|
||||
/// Browse all agent sessions on the shared local app-server daemon.
|
||||
Agents(AgentsCommand),
|
||||
|
||||
/// Internal: forward a local TCP socket through an HTTP/3 CONNECT proxy.
|
||||
#[clap(hide = true)]
|
||||
TcpTunnel(codex_tcp_tunnel::Args),
|
||||
/// Run Codex non-interactively.
|
||||
#[clap(visible_alias = "e")]
|
||||
Exec(ExecCli),
|
||||
@@ -1276,6 +1279,9 @@ async fn cli_main(
|
||||
.await?;
|
||||
handle_app_exit(exit_info, daemon_cli_executable.as_deref())?;
|
||||
}
|
||||
Some(Subcommand::TcpTunnel(args)) => {
|
||||
return codex_tcp_tunnel::run(args).await;
|
||||
}
|
||||
Some(Subcommand::Exec(mut exec_cli)) => {
|
||||
reject_remote_mode_for_subcommand(
|
||||
root_remote.as_deref(),
|
||||
@@ -2674,6 +2680,7 @@ fn unsupported_subcommand_name_for_strict_config(
|
||||
Some(Subcommand::ResponsesApiProxy(_)) => Some("responses-api-proxy"),
|
||||
Some(Subcommand::StdioToUds(_)) => Some("stdio-to-uds"),
|
||||
Some(Subcommand::Features(_)) => Some("features"),
|
||||
Some(Subcommand::TcpTunnel(_)) => Some("tcp-tunnel"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3780,6 +3787,36 @@ mod tests {
|
||||
err.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tcp_tunnel_is_hidden_and_accepts_its_control_input_contract() {
|
||||
let root_help = help_from_args(&["codex", "--help"]);
|
||||
assert!(!root_help.contains("tcp-tunnel"), "{root_help}");
|
||||
let help = help_from_args(&["codex", "tcp-tunnel", "--help"]);
|
||||
for option in [
|
||||
"--target",
|
||||
"--auth-token-stdin",
|
||||
"--auth-token-updates-stdin",
|
||||
"--connect-headers-stdin",
|
||||
] {
|
||||
assert!(help.contains(option), "{help}");
|
||||
}
|
||||
let cli = MultitoolCli::try_parse_from([
|
||||
"codex",
|
||||
"tcp-tunnel",
|
||||
"--proxy-url",
|
||||
"https://proxy.example.org",
|
||||
"--proxy-origins-file",
|
||||
"/tmp/approved-origins",
|
||||
"--target",
|
||||
"[::1]:22",
|
||||
"--auth-token-stdin",
|
||||
"--auth-token-updates-stdin",
|
||||
"--connect-headers-stdin",
|
||||
])
|
||||
.expect("generic tunnel should parse");
|
||||
assert!(matches!(cli.subcommand, Some(Subcommand::TcpTunnel(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_marketplace_help_uses_plugin_namespace() {
|
||||
let help = help_from_args(&["codex", "plugin", "marketplace", "--help"]);
|
||||
|
||||
Reference in New Issue
Block a user