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 ¶
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 ¶
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 ¶
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.
type ErrInvalidRule ¶
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.
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 ¶
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.
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 ¶
New constructs a Proxy. The proxy is not yet listening — call Proxy.Start when ready to accept clients.
func (*Proxy) Endpoint ¶
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.
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).