Tidies up the host resolution code a bit and closes some small gaps --- Release Notes: - N/A or Added/Fixed/Improved ...
58 lines
2.9 KiB
Rust
58 lines
2.9 KiB
Rust
//! In-process HTTP/HTTPS proxy that enforces a hostname allowlist.
|
|
//!
|
|
//! Spawned per terminal command from the parent process. The sandbox is
|
|
//! configured to permit network only to this proxy's port; everything the
|
|
//! sandboxed command tries to reach the network for has to come through here.
|
|
//!
|
|
//! The proxy:
|
|
//!
|
|
//! - Speaks HTTP CONNECT for HTTPS tunnels and HTTP forward proxying for
|
|
//! plain HTTP. Other protocols cannot reach it (the seatbelt rule limits
|
|
//! the sandboxed process to this one TCP destination, and this proxy only
|
|
//! speaks HTTP).
|
|
//! - Checks the destination hostname against an allowlist of exact hostnames
|
|
//! and leading-`*.` subdomain wildcards. Unless the allowlist allows any
|
|
//! host, IP-literal targets are denied, and hostnames whose DNS resolves
|
|
//! only into loopback / private / link-local space are denied too
|
|
//! (DNS-rebinding protection — the proxy runs outside the sandbox, so it
|
|
//! must not reopen the local network the Seatbelt rule closed off).
|
|
//! - Pins each TCP connection to the destination approved for its first
|
|
//! request: directly (to the vetted resolved addresses) or via a CONNECT
|
|
//! tunnel through an optional upstream HTTP proxy from the parent's
|
|
//! environment (`HTTPS_PROXY` / `HTTP_PROXY`), honoring `NO_PROXY`. Plain
|
|
//! HTTP is also tunneled when chaining, so keep-alive requests after the
|
|
//! first can never be routed to a different host by the upstream.
|
|
//! - Reports per-connection events (allowed, denied, completed) over an
|
|
//! mpsc supplied by the caller.
|
|
//!
|
|
//! ## Trust assumptions
|
|
//!
|
|
//! The proxy's sole client is model-driven code running inside the sandbox —
|
|
//! exactly the party the sandbox distrusts — and the proxy itself runs inside
|
|
//! the editor process. It therefore caps request header sizes and concurrent
|
|
//! connections, and bounds connect/handshake waits with timeouts, so a
|
|
//! malicious command can't exhaust the editor's memory, threads, or file
|
|
//! descriptors through it. Bandwidth is deliberately not capped; the
|
|
//! command's lifetime bounds it.
|
|
//!
|
|
//! ## "No proxy here" principle
|
|
//!
|
|
//! The agent and tools running inside the sandbox should not need to know
|
|
//! that a proxy is in front of them. The only response code the proxy
|
|
//! synthesizes itself is `511 Network Authentication Required`, used solely
|
|
//! for policy denials (with `Via:` and `Proxy-Status:` headers and a
|
|
//! plain-text body explaining the policy decision). Other failure modes
|
|
//! (upstream connection failure, malformed input from the client, etc.) are
|
|
//! handled by silently closing the connection — same behavior the client
|
|
//! would see from a direct network failure, no proxy fingerprint.
|
|
|
|
mod allowlist;
|
|
mod pinned_host;
|
|
mod proxy;
|
|
|
|
pub use allowlist::{Allowlist, HostPattern, HostPatternError};
|
|
pub use pinned_host::{PinnedHost, PinnedHostError, is_forbidden_ip};
|
|
pub use proxy::{
|
|
DenyReason, ProxyConfig, ProxyEvent, ProxyHandle, RequestMethod, RequestOutcome, UpstreamProxy,
|
|
};
|