netproxy

package
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package netproxy implements iterion's HTTP CONNECT proxy that enforces the workflow's sandbox network policy.

The proxy runs as a goroutine on the host (engine side) and is joined by every sandboxed container via HTTPS_PROXY/HTTP_PROXY env vars. By default it does NOT terminate TLS — only the CONNECT host:port is inspected — so host filtering works without minting a CA or injecting trust into the container. This is a cost/simplicity choice, not a cert-pinning constraint: the clients iterion runs (Claude Code, the Anthropic/OpenAI SDKs) are standard trust-store clients with no pinning and work behind a TLS-inspecting proxy once its CA is trusted — which is how the opt-in inspection mode (Layer 2 secret egress substitution) operates.

Pattern semantics, copied from the design plan (.plans/...,§5):

*.example.com    — exactly one DNS label
**.example.com   — one or more labels (greedy, dots allowed)
**               — any host (the "open" sentinel)
literal          — exact case-insensitive host match
!pattern         — exclusion (negation)
1.2.3.4          — IPv4 literal exact match
10.0.0.0/8       — CIDR range (only for IP literal rules)

Evaluation: rules walk top-to-bottom, last-match-wins. A host that matches no rule falls back to the configured Mode default (allowlist denies, denylist allows, open accepts everything).

IP-literal hosts (4-tuple) and bare IPs are compared after a failed DNS-label match. They are refused by default unless a rule explicitly lists them — that closes the cloud-metadata exfiltration vector (169.254.169.254 etc.).

Index

Constants

View Source
const (
	// PresetIterionDefault covers the LLM endpoints all claw-supported
	// providers use, the package registries every common runtime
	// relies on (npm, pypi, golang proxy), and the canonical code
	// hosts (github, gitlab, bitbucket). Suitable for the typical
	// "agent installs deps, edits code, opens a PR" workflow.
	PresetIterionDefault = "iterion-default"
)

Presets are named rule lists shipped with iterion so workflow authors don't have to repeat the LLM-endpoints + package-registries + code-hosts boilerplate in every sandbox: block.

Apply a preset by setting `network.preset:` in the .bot file (or by passing PresetIterionDefault as a Compile() prefix).

The list is intentionally curated rather than exhaustive — adding a new endpoint here is a deliberate API decision. Workflow authors who need more can append to `network.rules`.

Variables

This section is empty.

Functions

func NewToken

func NewToken() (string, error)

NewToken generates a random opaque token suitable for Options.Token. 32 bytes of entropy hex-encoded — collision-resistant under the number of concurrent runs iterion can plausibly host.

func PresetRules

func PresetRules(name string) ([]string, bool)

PresetRules returns the rule list for a named preset, or (nil, false) when the preset is unknown. Callers prepend these rules to the workflow's own list so per-workflow `!exclusion` entries can override the preset on a host-by-host basis.

Types

type EphemeralCA added in v0.39.0

type EphemeralCA struct {
	// contains filtered or unexported fields
}

EphemeralCA is a per-run certificate authority used by the proxy's TLS-inspection mode (Layer 2 secret egress substitution). It is generated fresh per run, lives only in memory, and is never persisted — its sole job is to mint short-lived leaf certificates for the hosts a sandboxed agent connects to, so the proxy can terminate TLS, rewrite the plaintext request (placeholder→secret, DLP), and re-encrypt to the real upstream.

The CA's public certificate (CertPEM) is injected into the sandbox's trust stores (system + NODE_EXTRA_CA_CERTS) so in-container clients accept the minted leaves. The private key NEVER leaves the host process.

func NewEphemeralCA added in v0.39.0

func NewEphemeralCA() (*EphemeralCA, error)

NewEphemeralCA generates a fresh in-memory CA.

func (*EphemeralCA) CertPEM added in v0.39.0

func (ca *EphemeralCA) CertPEM() []byte

CertPEM returns the PEM-encoded CA certificate to inject into the sandbox trust stores.

func (*EphemeralCA) GetCertificate added in v0.39.0

func (ca *EphemeralCA) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error)

GetCertificate is the tls.Config.GetCertificate callback: it mints (or returns a cached) leaf for the SNI host the client requested.

type ErrInvalidMode

type ErrInvalidMode struct {
	Mode Mode
}

ErrInvalidMode is returned by Compile when the policy mode is not one of allowlist, denylist, or open.

func (*ErrInvalidMode) Error

func (e *ErrInvalidMode) Error() string

Error implements error.

type ErrInvalidRule

type ErrInvalidRule struct {
	Raw    string
	Reason string
}

ErrInvalidRule is returned by Compile when a rule string fails to parse. The Raw field carries the user-supplied source so error messages let the user fix the right line.

func (*ErrInvalidRule) Error

func (e *ErrInvalidRule) Error() string

Error implements error.

type Mode

type Mode string

Mode is the egress default for unmatched hosts.

const (
	ModeAllowlist Mode = "allowlist"
	ModeDenylist  Mode = "denylist"
	ModeOpen      Mode = "open"
)

type Options

type Options struct {
	// Policy is the compiled rule set the proxy enforces. Required.
	Policy *Policy

	// Token, if non-empty, requires every client to present it via
	// Proxy-Authorization: Bearer <token>. Empty disables auth (useful
	// for tests).
	Token string

	// OnBlocked, when non-nil, is called for every request the proxy
	// rejects. Engine integration uses this to emit the
	// `network_blocked` event into events.jsonl.
	OnBlocked func(host, reason string)

	// Dial overrides the upstream dialer. Tests use this to redirect
	// CONNECTs to a loopback echo server.
	Dial func(ctx context.Context, network, addr string) (net.Conn, error)

	// InspectCA, when non-nil, enables TLS-inspection mode (Layer 2):
	// CONNECT tunnels are terminated with leaves minted by this CA so the
	// proxy can rewrite the plaintext request. The CA's public cert must
	// be trusted inside the sandbox.
	InspectCA *EphemeralCA

	// Rewriter is consulted in inspection mode to substitute secret
	// placeholders and to block exfiltration. Nil leaves inspection a
	// pure decrypt/re-encrypt passthrough.
	Rewriter SecretRewriter

	// InspectUpstreamTLS overrides the TLS config used for the proxy's
	// connection to the REAL upstream in inspection mode (tests inject a
	// RootCAs trusting a local httptest server). Nil uses the system
	// trust store with proper verification.
	InspectUpstreamTLS *tls.Config
}

Options configures a Proxy.

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

Policy is a compiled set of rules with a fallback Mode.

Construct via Compile. Reuse the same Policy across many host checks — the rule list is read-only and cheap to evaluate.

func Compile

func Compile(mode Mode, rules []string) (*Policy, error)

Compile parses the rule list into a Policy. An empty Mode defaults to ModeAllowlist (the safer choice). Invalid rules are reported as errors so callers can surface them at compile time rather than at proxy connect time.

func (*Policy) Allow

func (p *Policy) Allow(host string) bool

Allow reports whether the host is permitted by the policy.

host is the raw value from a CONNECT line ("api.anthropic.com:443") or HTTP request — port suffix and surrounding whitespace are stripped before matching. Empty input is denied by all modes except open.

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

Proxy is the iterion HTTP / HTTPS CONNECT proxy. It enforces a Policy on the host portion of every CONNECT and plain-HTTP request, then tunnels accepted traffic transparently.

By default the proxy does NOT terminate TLS — it inspects only the CONNECT host:port (and the plain-HTTP Host header) and shuttles the encrypted bytes through untouched. The reason is cost and simplicity, NOT client cert pinning: the clients iterion actually runs (the Claude Code CLI, the Anthropic/OpenAI SDKs, npm/pip/go/git) are standard trust-store clients with no certificate pinning — they work behind TLS-inspecting proxies (Zscaler, CrowdStrike, mitmproxy) once the proxy CA is trusted, which is exactly how the opt-in TLS-inspection mode (secret egress substitution, Layer 2) works. Transparent tunnelling is the default because it needs no CA minted, no CA private key to custody, and no per-runtime trust-store injection (NODE_EXTRA_CA_CERTS, certifi, the system store, …).

Proxy authentication is via Proxy-Authorization: Bearer <token>. Each run gets a fresh token (so a leaked token from one run cannot be replayed on another container against the same host port).

func New

func New(opts Options) (*Proxy, error)

New constructs a Proxy. The proxy is not yet listening — call Proxy.Start when ready to accept clients.

func (*Proxy) Addr

func (p *Proxy) Addr() net.Addr

Addr returns the bound network address (or nil before Start).

func (*Proxy) Endpoint

func (p *Proxy) Endpoint(hostName string) string

Endpoint returns the URL clients should set in HTTPS_PROXY/HTTP_PROXY. Includes the auth token when one is configured. Returns "" before Proxy.Start has bound a port.

func (*Proxy) Shutdown

func (p *Proxy) Shutdown(ctx context.Context) error

Shutdown stops the proxy server and closes the listener. Idempotent.

func (*Proxy) Start

func (p *Proxy) Start(addr string) error

Start binds the proxy to the given address ("127.0.0.1:0" for an ephemeral port) and begins serving in a background goroutine. Stop gracefully via Proxy.Shutdown.

type SecretRewriter added in v0.39.0

type SecretRewriter interface {
	// MaterializeForHost swaps secret placeholders for real values, but
	// only for secrets scoped to (or unrestricted toward) host.
	MaterializeForHost(s, host string) string
	// ExfiltratesTo reports whether s carries a real secret value bound
	// for a host that secret is NOT scoped to (a blockable exfiltration).
	ExfiltratesTo(s, host string) bool
}

SecretRewriter is the proxy's view of the secret guard, used by the TLS-inspection mode to rewrite plaintext requests (Layer 2). It is a structural interface so netproxy stays decoupled from pkg/backend/secretguard (which implements it).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL