Sandbox
Core reference

Proxy Server

The Proxy Server is the network security gatekeeper of the jazzmine framework. It is a lightweight, asynchronous TCP proxy designed to run as a "sidecar" container within the same isolated Docker network as the tool sandboxes. It enforces a strict egress allowlist, ensuring that scripts running in the sandbox can only communicate with authorized external domains or IP addresses.

Tool System: Proxy Server

1. Behavior and Context

In the jazzmine execution environment, the Proxy Server acts as a transparent intermediary for all outbound traffic.

  • Transparent Redirection: When a sandbox container is spawned by the SandboxPool, it is automatically configured with HTTP_PROXY and HTTPS_PROXY environment variables pointing to this sidecar.
  • Sidecar Pattern: Every sandbox that requires network access is connected to a bridge network where the proxy is the only reachable "gateway" to the public internet.
  • Protocol Support: It handles both plain HTTP requests and HTTPS tunneling via the HTTP CONNECT method.
  • DNS Resolution: Critically, the proxy performs DNS resolution on behalf of the sandbox. Since the sandbox container itself has no direct DNS access, this prevents "DNS Rebinding" attacks or bypasses via direct IP manipulation.

2. Purpose

  • Egress Lockdown: Implementing a "Default Deny" network posture for untrusted code execution.
  • Allowlist Enforcement: Restricting the agent's tools to specific trusted APIs (e.g., Stripe, Slack, or internal company endpoints).
  • IP-Literal Protection: Preventing scripts from bypassing hostname-based filters by using raw IP addresses of unauthorized servers.
  • Observability: Providing a centralized point for logging network activity and data transfer volumes from the sandboxes.

3. High-Level API (Configuration)

The Proxy Server is a standalone script that is configured exclusively via environment variables. These are typically managed automatically by the SandboxBuilder.

Configuration Variables

VariableDescriptionExample
PROXY_PORTThe port the proxy listens on.8888
PROXY_ALLOWLISTComma-separated list of host:port strings.api.stripe.com:443,1.2.3.4:8080

Example: Running the Proxy Manually

bash
# Define the authorized destinations
export PROXY_ALLOWLIST="api.openai.com:443,github.com:443,192.168.1.50:80"

# Start the server
python proxy_server.py

5. Detailed Functionality

_load_allowlist()

Functionality: Parses the PROXY_ALLOWLIST environment variable into a Python set for O(1) lookups.

How it works:

  • Splits the input string by commas.
  • If an entry contains a colon (e.g., host:port), it parses both.
  • If an entry lacks a port, it automatically authorizes both port 80 (HTTP) and 443 (HTTPS) for that host.

_is_allowed(host, port)

Functionality: The core decision engine that determines if a specific connection request should be granted.

Parameters:

  • host (str): The destination hostname or IP address.
  • port (int): The destination port.

How it works:

  • Hostname Logic: If the host is a domain name, it checks if the (host, port) pair exists in the allowlist.
  • IP Logic: If the host is a raw IP address, it is only allowed if that specific IP and Port were explicitly added to the allowlist. Any IP not explicitly mentioned is blocked, even if it belongs to an authorized domain. This prevents allowlist bypass via IP literals.

_handle_connect(client_reader, client_writer, host, port)

Functionality: Handles HTTPS (SSL/TLS) traffic via "Blind Tunneling."

How it works:

  1. Receives the CONNECT request from the sandbox.
  2. Calls _is_allowed. If denied, returns 403 Forbidden.
  3. If allowed, opens a raw TCP connection to the target server.
  4. Sends 200 Connection Established back to the sandbox.
  5. Spawns two _pipe tasks to shuttle encrypted bytes bidirectionally.

Note: The proxy never decrypts this traffic; it simply acts as a conduit for the TLS handshake.


_handle_http(client_reader, client_writer, method, url, headers, body)

Functionality: Forwards unencrypted HTTP requests.

How it works:

  1. Extracts the Host header to perform the allowlist check.
  2. If allowed, connects to the target.
  3. Reconstructs the raw HTTP request (proxy format to origin format).
  4. Forwards the payload and streams the response back to the sandbox.

_pipe(reader, writer, label, byte_counter)

Functionality: An efficient, asynchronous bidirectional data transfer loop.

How it works: Reads chunks of up to 64KB from one socket and writes them to the other. It updates a byte_counter to track how much data is leaving/entering the sandbox for auditing purposes.


6. Error Handling

  • 403 Forbidden: Returned to the sandbox tool if it attempts to connect to a destination not present in the allowlist.
  • 502 Bad Gateway: Returned if the proxy is authorized to connect but the target server is unreachable or refused the connection.
  • ConnectionResetError: Handled gracefully during the piping phase to ensure that one hanging connection does not crash the entire proxy service.

7. Remarks

  • Performance: Built on asyncio, the proxy can handle hundreds of concurrent connections with minimal CPU and RAM overhead.
  • DNS Resolution: Because the host parameter in the _handle functions is often a domain name, the proxy's internal asyncio.open_connection triggers a DNS lookup on the proxy's network interface (not the sandbox's). This is a vital security feature.
  • IP Support: While raw IPs are supported for internal testing or legacy systems, developers are encouraged to use hostnames (FQDNs) to take full advantage of the proxy's filtering logic.