feat: add port 0 (random port) support for gateway security

When --port 0 is passed, the OS assigns a random available ephemeral
port (typically 49152-65535). The actual port is resolved after binding
and used for all log output and tunnel forwarding.

This prevents port-scanning attacks against a known fixed port.

Changes:
  src/gateway/mod.rs — bind first, extract actual_port from listener,
    use actual_port for addr formatting and tunnel.start()
  src/main.rs — update CLI help text, conditional log for port=0

8 new edge case tests:
  - port_zero_binds_to_random_port
  - port_zero_assigns_different_ports
  - port_zero_assigns_high_port
  - specific_port_binds_exactly
  - actual_port_matches_addr_format
  - port_zero_listener_accepts_connections
  - duplicate_specific_port_fails
  - tunnel_gets_actual_port_not_zero

943 tests passing, 0 clippy warnings, cargo fmt clean
This commit is contained in:
argenis de la rosa 2026-02-14 01:21:55 -05:00
parent b2aff60722
commit c8d4ceee71
2 changed files with 115 additions and 7 deletions

View file

@ -69,7 +69,7 @@ enum Commands {
/// Start the gateway server (webhooks, websockets)
Gateway {
/// Port to listen on
/// Port to listen on (use 0 for random available port)
#[arg(short, long, default_value = "8080")]
port: u16,
@ -234,9 +234,11 @@ async fn main() -> Result<()> {
} => agent::run(config, message, provider, model, temperature).await,
Commands::Gateway { port, host } => {
info!("🚀 Starting ZeroClaw Gateway on {host}:{port}");
info!("POST http://{host}:{port}/webhook — send JSON messages");
info!("GET http://{host}:{port}/health — health check");
if port == 0 {
info!("🚀 Starting ZeroClaw Gateway on {host} (random port)");
} else {
info!("🚀 Starting ZeroClaw Gateway on {host}:{port}");
}
gateway::run_gateway(&host, port, config).await
}