go-egress-proxy
A MITM HTTP/HTTPS proxy implementing split-brain DNS for egress traffic control in containerized environments.
Features
Section titled “Features”- Split-brain DNS via TCP dial interception (not DNS-level), with
target_iportarget_hostrouting - Path-based routing - route different URL paths on the same domain to different backends (
path_pattern) - Scheme rewriting - change the request scheme before forwarding (
target_scheme) - ACL with whitelist/blacklist/passthrough support (exact match, wildcards, regex)
- Header injection on rewritten requests, plus automatic
X-Request-IDfor tracing - Header stripping - remove sensitive headers before forwarding (
drop_headers) - Per-rewrite TLS bypass - skip upstream TLS verification for specific targets (
insecure) - Outgoing TLS trust - custom CA bundle (PEM) and/or PKCS#12 truststore for upstream verification
- Hot reload via SIGHUP signal (no restart required)
- Blocked request log - optional JSON log file for auditing blocked requests
- Selective request tracing - opt-in, full-detail aggregated trace records for a configurable subset of requests (by host/URL), spanning the TCP/TLS, request, and response layers (plus optional bodies), with secure-by-default redaction
- Environment variable overrides for 12-factor app compatibility
- Outbound HTTP/2 - negotiates HTTP/2 with upstream servers via ALPN
- Prometheus metrics - request counts, latency histograms, active connections, upstream errors
- Graceful shutdown - on SIGTERM: fail
/readyz, keep serving forPROXY_PRESTOP_GRACE(default 10s) so load balancers route away, then drain in-flight requests and CONNECT tunnels within a 30s budget - Health endpoints -
/healthzand/readyzfor Kubernetes probes
Quick Start
Section titled “Quick Start”# 1. Generate internal CA certificatesmitm-proxy gencert --type root --cn "My MITM CA" --out-cert certs/ca.crt --out-key certs/ca.key
# 2. Create config.yaml (copy from example)cp doc/examples/configuration.yaml config.yaml
# 3. Edit config.yaml with your rules
# 4. Run the proxymake run
# 5. Test (in another terminal)curl -v -x http://localhost:8080 --cacert certs/ca.crt https://example.comCLI Usage
Section titled “CLI Usage”# Print versionmitm-proxy --version
# Show helpmitm-proxy --help
# Run with default verbosity (warn — errors and warnings only)mitm-proxy
# Run with info logs (access logs, startup info)mitm-proxy -v
# Run with debug outputmitm-proxy -vv
# Validate configurationmitm-proxy validate --config config.yaml
# Generate certificates (see Certificate Generation section below)mitm-proxy gencert --help| Flag | Description |
|---|---|
--version |
Print version and exit |
-h, --help |
Show help message |
| (none) | Warn (default) — errors and warnings only. Highest verbosity wins when multiple flags are given. |
-v |
Info — enables info logs including ACCESS log per request |
-vv |
Debug — adds REQUEST_DETAIL per request (scheme, full URL, proto, remote addr, content-length, user-agent, content-type, rewrite target) |
-vvv |
Trace — adds full request headers to REQUEST_DETAIL |
Subcommands (validate, gencert) default to info level for user-friendly output.
| Subcommand | Description |
|---|---|
validate |
Validate configuration file and exit |
gencert |
Generate CA certificates (root or intermediate) |
The version is injected at build time. Use VERSION=1.0.0 make build to set a specific version, otherwise it defaults to the git describe output or dev.
Configuration
Section titled “Configuration”Create a config.yaml file (or set CONFIG_PATH environment variable):
proxy: port: "8080" # Proxy listen port metrics_port: "9090" # Metrics/health endpoint port default_policy: "BLOCK" # ALLOW or BLOCK unmatched hosts outgoing_ca_bundle: "" # Optional: PEM CA bundle for upstream TLS outgoing_ca: # Optional: list of individual CA cert files - "certs/internal-ca.crt" - "certs/partner-ca.crt" blocked_log_path: "" # Optional: JSON log file for blocked requests
# Optional: ceiling on concurrent client connections (0/unset = unlimited). # A hijacked CONNECT tunnel has no deadline at any layer, so this is what # bounds a client that connects and then goes silent. At the cap the proxy # pauses accepting until one closes and increments # proxy_listener_saturated_total. Overridable with PROXY_MAX_CONNECTIONS. # max_connections: 2048
# Optional: PKCS#12 truststore for upstream TLS (additive with outgoing_ca_bundle) # outgoing_truststore_path: "certs/upstream-cas.p12" # outgoing_truststore_password: "changeit"
# Disable upstream TLS certificate verification globally (for dev/test only!) # insecure_skip_verify: false
# MITM CA certificate - provide either PEM cert+key or a PKCS#12 keystore (not both)
# Option A: PEM cert + key (default) mitm_cert_path: "certs/ca.crt" mitm_key_path: "certs/ca.key"
# Option B: PKCS#12 keystore (mutually exclusive with cert+key) # mitm_keystore_path: "certs/ca.p12" # mitm_keystore_password: "changeit"
rewrites: # Route by IP address - domain: "api.production.com" target_ip: "10.20.30.40" headers: X-Proxy-Source: "egress-gateway"
# Route by hostname (DNS-resolved at dial time) - domain: "external-api.partner.com" target_host: "internal-gateway.corp.example.com"
# Wildcard match (any subdomain depth) - domain: "*.internal.example.com" target_ip: "10.20.30.50"
# Regex match (prefix with ~) - domain: "~^api[0-9]+\\.example\\.com$" target_ip: "10.20.30.60"
# Per-rewrite TLS bypass for self-signed internal services - domain: "self-signed.internal.com" target_ip: "10.20.30.70" insecure: true
# Path-based routing (first match wins, evaluated in YAML order) - domain: "api.example.com" path_pattern: "^/v1/" target_ip: "10.20.30.80" headers: X-Backend: "v1" - domain: "api.example.com" path_pattern: "^/v2/" target_ip: "10.20.30.81" - domain: "api.example.com" # Catch-all (no path_pattern) target_ip: "10.20.30.82"
# Scheme rewriting: forward HTTPS client requests as HTTP to backend - domain: "legacy-backend.internal.com" target_ip: "10.20.30.90" target_scheme: "http"
# Strip sensitive headers before forwarding - domain: "sanitized-api.internal.com" target_ip: "10.20.30.91" drop_headers: - "Authorization" - "Cookie"
acl: whitelist: - "*.google.com" - "github.com" blacklist: - "*.tiktok.com" passthrough: # Tunnel without MITM (for services with their own PKI) - "kubernetes.default.svc" - "*.vault.internal"See doc/examples/configuration.yaml for a complete example.
Rewrite Rule Reference
Section titled “Rewrite Rule Reference”Each rewrite rule supports these fields:
| Field | Required | Description |
|---|---|---|
domain |
yes | Domain pattern: exact, wildcard (*.example.com), or regex (~<pattern>) |
target_ip |
one of | IP address to route to (mutually exclusive with target_host) |
target_host |
one of | Hostname to route to, resolved via DNS at dial time |
path_pattern |
no | Regex matched against r.URL.Path for path-based routing |
target_scheme |
no | "http" or "https" to change the request scheme before forwarding. The port moves with the scheme when the client used the old scheme’s default (an intercepted HTTPS request carries :443, so a downgrade to http dials :80). A port the client chose explicitly is kept. |
target_port |
no | TCP port to connect to, overriding both the client’s port and the scheme default. Needed when the backend listens somewhere other than 80/443 (e.g. a legacy service on 8080). |
headers |
no | Map of headers to inject into the request |
drop_headers |
no | List of header names to remove before forwarding |
insecure |
no | Skip upstream TLS verification for this target only |
Domain Pattern Semantics
Section titled “Domain Pattern Semantics”ACL entries, rewrite domain values and trace host values share one syntax.
Two properties matter for denylists, because getting them wrong fails open
silently — the config loads, validate passes, and the entry enforces nothing.
| Pattern | Matches | Does not match |
|---|---|---|
example.com |
example.com, EXAMPLE.com, example.com. |
api.example.com |
*.example.com |
api.example.com, a.b.example.com |
example.com (the apex) |
* |
every host | — |
~^(.+\.)?example\.com$ |
the apex and any subdomain | example.com.evil.net |
- A
*.wildcard does not cover the apex. To deny a domain and everything under it, list bothexample.comand*.example.com, or use one~pattern. ~patterns are anchored, wrapped as(?i)^(?:...)$. They are not substring matches:~api\.corp\.comdoes not matchapi.corp.com.attacker.net. Patterns that already carry^/$are unaffected. (Traceurlpatterns are the deliberate exception and remain substring matches.)- Matching is case-insensitive, and a trailing dot is stripped before matching,
so
EXAMPLE.com.cannot slip past an entry forexample.com. - A
*anywhere other than a leading*.label is a load error rather than a pattern that matches nothing.
Response Status Codes
Section titled “Response Status Codes”| Code | Meaning | When |
|---|---|---|
| 200 | OK | Request succeeded through to upstream |
| 403 | Forbidden | Request blocked by ACL (blacklisted or default BLOCK policy) |
| 502 | Bad Gateway | Upstream unreachable: DNS lookup failed, connection refused, or connection reset |
| 504 | Gateway Timeout | Upstream accepted the connection but did not respond in time |
Configuration Validation
Section titled “Configuration Validation”Validate your configuration file without starting the proxy:
# Use --config flagmitm-proxy validate --config config.yaml
# Or use CONFIG_PATH environment variableCONFIG_PATH=config.yaml mitm-proxy validate
# Default: reads config.yaml from current directorymitm-proxy validateThe validate subcommand checks:
- YAML syntax and structure
- Required fields and valid values (including
target_scheme,path_patternregex) - Mutual exclusivity of
target_ip/target_hostand cert+key/keystore - ACL and rewrite pattern compilation
- Referenced files exist and are readable (certificates, CA bundles, truststores)
- Parent directory exists for
blocked_log_path
Exits with code 0 on success, 1 on failure.
Certificate Generation
Section titled “Certificate Generation”The gencert subcommand generates root and intermediate CA certificates with optional client trust bundles. It replaces the need for OpenSSL and the make certs script.
# Generate a root CA (self-signed, ECDSA P-256, 10-year validity)mitm-proxy gencert --type root \ --cn "My Root CA" --org "ACME Corp" --country CH \ --out-cert certs/root-ca.crt --out-key certs/root-ca.key
# Generate an intermediate CA signed by the root (leaf-signing only)mitm-proxy gencert --type intermediate \ --signing-cert certs/root-ca.crt --signing-key certs/root-ca.key \ --cn "MITM Proxy CA" --org "ACME Corp" \ --max-path-len 0 --validity 365 \ --out-cert certs/mitm-ca.crt --out-key certs/mitm-ca.key \ --out-chain certs/mitm-chain.crt
# Generate a root CA with client trust bundlesmitm-proxy gencert --type root --cn "My Root CA" \ --out-client-bundle certs/trust.pem \ --out-client-p12 certs/truststore.p12 --client-p12-password changeit| Flag | Default | Description |
|---|---|---|
--type |
root |
root (self-signed) or intermediate (signed by parent) |
--key-algo |
ecdsa-p256 |
rsa-2048, rsa-4096, ecdsa-p256, ecdsa-p384, ed25519 |
--cn |
MITM Proxy CA |
Certificate CommonName |
--org |
MITM Proxy |
Certificate Organization |
--country |
(empty) | Country code (e.g. CH) |
--validity |
3650 |
Validity in days |
--max-path-len |
-1 |
BasicConstraints PathLen (-1 unlimited, 0 leaf-signing only) |
--signing-cert |
Parent CA certificate (required for intermediate) |
|
--signing-key |
Parent CA private key (required for intermediate) |
|
--out-cert |
ca.crt |
Output certificate (PEM) |
--out-key |
ca.key |
Output private key (PEM, 0600 permissions) |
--out-chain |
Output full chain: cert + parent certs (PEM) | |
--out-p12 |
Output PKCS#12 keystore with cert+key (for mitm_keystore_path) |
|
--p12-password |
Password for --out-p12 |
|
--out-client-bundle |
Output client trust bundle (PEM, for distribution to clients) | |
--out-client-p12 |
Output client PKCS#12 truststore (for Java keystore import) | |
--client-p12-password |
changeit |
Password for --out-client-p12 |
Production workflow with intermediate CA:
# 1. Generate root CA (store offline / in vault)mitm-proxy gencert --type root --cn "Corp Root CA" --org "Corp" \ --out-cert root-ca.crt --out-key root-ca.key \ --out-client-p12 client-truststore.p12 --client-p12-password changeit
# 2. Generate intermediate CA for the proxymitm-proxy gencert --type intermediate \ --signing-cert root-ca.crt --signing-key root-ca.key \ --cn "Corp MITM Proxy CA" --org "Corp" \ --max-path-len 0 --validity 365 \ --out-cert mitm-ca.crt --out-key mitm-ca.key \ --out-chain mitm-chain.crt
# 3. Configure proxy with the intermediate chain# mitm_cert_path: mitm-chain.crt (intermediate + root)# mitm_key_path: mitm-ca.key
# 4. Distribute root CA to clients:# - PEM: trust.pem (for curl --cacert, system trust stores)# - PKCS#12: client-truststore.p12 (for Java applications)Java client trust store usage:
# Use directly as Java truststorejava -Djavax.net.ssl.trustStore=client-truststore.p12 \ -Djavax.net.ssl.trustStoreType=PKCS12 \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar myapp.jar
# Or import into an existing JKS keystorekeytool -importkeystore \ -srckeystore client-truststore.p12 -srcstoretype PKCS12 -srcstorepass changeit \ -destkeystore truststore.jks -deststorepass changeitEnvironment Variable Overrides
Section titled “Environment Variable Overrides”Selected proxy: settings can be overridden via environment variables. The
rewrites:, acl: and trace: blocks have no environment equivalents —
they are structured and must come from the config file.
| Variable | Description |
|---|---|
CONFIG_PATH |
Path to config file (default: config.yaml) |
PROXY_PORT |
Proxy listen port |
PROXY_METRICS_PORT |
Metrics endpoint port |
PROXY_DEFAULT_POLICY |
ALLOW or BLOCK |
PROXY_MITM_ORG |
Organization on generated MITM leaf certificates (see mitm_org) |
PROXY_MAX_CONNECTIONS |
Overrides proxy.max_connections: ceiling on concurrent client connections (default 0 = unlimited). At the cap the listener pauses accepting until one closes, warns, and increments proxy_listener_saturated_total. A hijacked CONNECT tunnel has no deadline, so this is what bounds fd usage against a client that connects and goes silent. |
PROXY_PRESTOP_GRACE |
How long to keep serving after failing /readyz on SIGTERM, before closing the listener (default 10s; set 0 when a preStop hook already sleeps) |
PROXY_MITM_CERT_PATH |
Path to MITM CA certificate (PEM) |
PROXY_MITM_KEY_PATH |
Path to MITM CA private key (PEM) |
PROXY_MITM_KEYSTORE_PATH |
Path to PKCS#12 keystore (.p12) containing cert and key |
PROXY_MITM_KEYSTORE_PASSWORD |
Password for PKCS#12 keystore |
PROXY_OUTGOING_CA_BUNDLE |
Path to PEM CA bundle for upstream TLS |
PROXY_OUTGOING_TRUSTSTORE_PATH |
Path to PKCS#12 truststore for upstream TLS |
PROXY_OUTGOING_TRUSTSTORE_PASSWORD |
Password for PKCS#12 truststore |
PROXY_INSECURE_SKIP_VERIFY |
Set to true to disable all upstream TLS verification |
PROXY_BLOCKED_LOG_PATH |
Path to JSON log file for blocked requests |
Note: Provide either
PROXY_MITM_CERT_PATH/PROXY_MITM_KEY_PATHorPROXY_MITM_KEYSTORE_PATH, not both.Note:
outgoing_ca(list of individual CA cert files) is YAML-only and has no environment variable override. It can be used alongsideoutgoing_ca_bundle— all certificates are merged into one trust pool.
Development
Section titled “Development”# Install development toolsmake install-tools
# Run lintermake lint
# Run testsmake test
# Run end-to-end tests (requires Docker)make test-e2e
# Build binarymake build
# Format codemake fmtDocker
Section titled “Docker”Images are published to ghcr.io/datarocks-ag/go-egress-mitm-proxy on v* tags and carry OCI metadata, so a running container can be traced back to its source:
docker inspect --format '{{json .Config.Labels}}' ghcr.io/datarocks-ag/go-egress-mitm-proxy:4.1.0 | jq# org.opencontainers.image.version v4.1.0# org.opencontainers.image.revision 075e8d2af6cf9f96d3e6da2f36336f25cbbf32da# org.opencontainers.image.created 2026-08-07T13:00:53Zmake docker-build stamps the same labels from your working tree. An image reading revision=unknown was built by neither path.
# Buildmake docker-build
# Runmake docker-run
# Or with docker-composedocker-compose upKubernetes
Section titled “Kubernetes”Deploy as a sidecar container. See doc/k8s/ for example manifests.
# In your application deploymentenv: - name: HTTPS_PROXY value: "http://localhost:8080" - name: HTTP_PROXY value: "http://localhost:8080"Hot Reload
Section titled “Hot Reload”Reload configuration without restarting the proxy:
# Find the process IDpgrep mitm-proxy
# Send SIGHUP to reload configkill -HUP <pid>The proxy will log successful reloads and any errors. SIGHUP also reopens the blocked request log file, enabling log rotation.
What SIGHUP applies: ACL lists, rewrite rules, default_policy, trace
configuration, outbound CA sources, and both log files. Pooled upstream
connections to previous rewrite targets are dropped so new targets take effect
immediately rather than after IdleConnTimeout.
What needs a restart: port, metrics_port, and every mitm_* setting
(mitm_cert_path, mitm_key_path, mitm_keystore_path,
mitm_keystore_password, mitm_org). The MITM CA is loaded once at startup and
its leaf-certificate cache outlives a reload, so rotating the CA requires a
restart. If any of these change, the reload logs a warning naming them rather
than reporting a clean reload that silently ignored them.
Logging
Section titled “Logging”All log output is structured JSON via slog. The verbosity flag controls which log levels are emitted:
| Level | Flag | What is logged |
|---|---|---|
| Warn | (none, default) | Errors and warnings only (upstream failures, config errors). Subcommands default to info. |
| Info | -v |
All info logs including ACCESS line per request: request_id, client, host, action, method, path |
| Debug | -vv |
Adds REQUEST_DETAIL per request: scheme, full URL, proto, remote_addr, content_length, user_agent, content_type, and rewrite target info (target_ip, target_host, original) when a rewrite matched |
| Trace | -vvv |
Adds all request headers to REQUEST_DETAIL |
Debug and trace logging have zero overhead when not enabled — the log construction is gated behind slog.Default().Enabled().
Blocked Request Log
Section titled “Blocked Request Log”When blocked_log_path is configured, the proxy writes a JSON log entry for every request with action BLACK-LISTED or BLOCKED. Each entry includes request_id, client, host, method, target, and action. The log file is reopened on SIGHUP for log rotation support.
target is the request path for a forwarded request. A CONNECT carries host:port in its request-target rather than a path, so rejected CONNECTs record the authority there — the field was previously named path and was empty for exactly those entries, which is the case an audit log most needs to show.
Metrics
Section titled “Metrics”Available at http://localhost:9090/metrics:
| Metric | Type | Labels | Description |
|---|---|---|---|
proxy_traffic_total |
Counter | domain, action | Request count. For a rewritten request domain is the matching rule’s configured pattern (e.g. *.internal.example.com), not the request host; otherwise it is the ACL base domain, or _other |
proxy_request_duration_seconds |
Histogram | action | Request latency |
proxy_active_connections |
Gauge | - | Current connections |
proxy_config_load_errors_total |
Counter | - | Config load failures |
proxy_config_reloads_total |
Counter | - | Successful config reloads |
proxy_upstream_errors_total |
Counter | type | Upstream connection errors |
proxy_response_status_total |
Counter | class | Response status codes (2xx, 4xx, 5xx) |
proxy_bytes_total |
Counter | direction | Bytes transferred (request/response) |
proxy_trace_records_total |
Counter | mode | Emitted trace records (mitm/passthrough) |
proxy_listener_saturated_total |
Counter | - | Times the client connection ceiling (proxy.max_connections, or its PROXY_MAX_CONNECTIONS override) was reached and accepting paused |
Three of these measure something different than a first reading suggests, which matters if you are building alerts on them:
proxy_request_duration_secondsis observed for a forwarded request once the upstream round-trip returns, so the span covers DNS, dial, TLS handshake and upstream think-time — not just proxy overhead. Blocked requests are recorded in the handler, where the elapsed time is the whole request.proxy_active_connectionscounts client connections open at the listener, including hijacked CONNECT tunnels. It is aGaugeFuncreading the live count at scrape time.proxy_trace_records_totalonly advances when a record is actually written. If the destination logger would filter the record, the proxy warns and does not count it, so the counter cannot disagree with the log.
Actions: REWRITTEN, WHITE-LISTED, BLACK-LISTED, ALLOWED-BY-DEFAULT, BLOCKED, PASSTHROUGH, BLACK-LISTED-CONNECT
BLACK-LISTED-CONNECT is recorded at the CONNECT stage when a blacklisted host is intercepted. Interception is preferred over refusing the tunnel because it lets the handler return a readable 403, but the denial is then only recorded if the client completes the TLS handshake with the proxy CA — a certificate-pinning SDK or a JVM with its own truststore never will. Counting the attempt at CONNECT keeps it observable either way, and the separate label stops it inflating the BLACK-LISTED count that HandleRequest records when the handshake does succeed.
A host that is both passthrough and blacklisted is refused outright, since a passthrough tunnel is never inspected; that case counts as BLACK-LISTED.
Health Endpoints
Section titled “Health Endpoints”GET /healthz- Liveness probe (always returns 200 if process is running)GET /readyz- Readiness probe (returns 200 when ready to accept traffic)
Request Tracing
Section titled “Request Tracing”The proxy automatically injects an X-Request-ID header into all forwarded requests for distributed tracing. This ID is also logged with each access log entry.
Selective full-detail tracing
Section titled “Selective full-detail tracing”For deep debugging of a specific subset of traffic, configure a trace: block. Matching requests are emitted as a single aggregated JSON record (keyed by trace_id = X-Request-ID) covering every layer at once — independent of the -v/-vv/-vvv log level.
trace: enabled: true log_path: "/var/log/mitm-proxy/trace.jsonl" # optional; omit to use the main log stream redact_query: true # mask ?token=... query values (default: true) redact_headers: # extends the built-in masked set - "X-Api-Key" log_secrets: false # escape hatch: true logs everything verbatim rules: # OR across rules; within a rule host AND url must both match - host: "*.internal.com" # WildcardToRegex syntax (~ = raw regex) bodies: enabled: true capture: both # request | response | both max_request_bytes: 65536 max_response_bytes: 65536 content_types: ["application/json", "text/*"] on_binary: base64 # base64 | skip - url: "~/v1/(debug|trace)" # matched against the full scheme://host/path?queryEach record captures:
- CONNECT/TCP/TLS — host (with port) and SNI always; plus resolved/connected
IP, dial timing and negotiated TLS version/cipher only when this request
opened a new upstream connection. When a pooled connection is reused those
four fields are absent and the record carries
tcp.connection_reused: trueinstead — there is no per-request TCP layer to report. With keep-alive that is the common case, so their absence is normal rather than a sign tracing is broken. - Request — method, URL, inbound headers, outbound (post-mutation) headers, and the
dropped/added/modified/scheme_changeddiff - Response — status, proto, headers
- Bodies (per rule) — size-capped, content-type-gated (text inline, binary base64/skip), truncation marked; streaming is preserved
Matching: rules are OR’d; within a single rule, host and url must both match (use separate rules for OR). host/url use the same convention as ACL/rewrites (wildcard, or ~ for raw regex).
Bodies are not redacted. redact_headers and redact_query apply to headers and the query string only; renderBody receives no redactor. Enabling bodies writes request and response payloads verbatim (subject to the size caps and content-type gate), so treat a trace log with bodies enabled as containing secrets.
Redaction is secure-by-default. Authorization, Proxy-Authorization, Cookie, and Set-Cookie are always masked, as are the URL-bearing Location, Content-Location and Referer — a 302 in an OAuth flow routinely carries the authorization code in Location, and header values are masked by name, so redact_query (which only sees the request URL) never covered it; redact_headers extends the set, and an entry prefixed with - removes one of the built-ins — redact_headers: ["-location"] makes redirect targets visible while Authorization and Cookie stay masked. Removing a credential-bearing default (Authorization, Proxy-Authorization, Cookie, Set-Cookie) is allowed but logs a warning at startup, and removing a header that is not masked is a load error rather than a silent no-op. redact_query masks query-string values and defaults to true — set it to false to opt out. Set log_secrets: true to disable all redaction (use with care — these logs may be shipped/retained).
Passthrough hosts (non-MITM tunnels) are traced TCP-only: connected IP, dial timing, and bytes transferred. Headers and bodies are inherently invisible because the traffic is not intercepted.
The trace log file is reopened on SIGHUP for log rotation, like blocked_log_path. See doc/examples/configuration.yaml for a complete example.
Client Setup
Section titled “Client Setup”Clients must trust the MITM root CA certificate. You can generate a client trust bundle with gencert (see Certificate Generation) or distribute the CA cert manually:
# Linux (system-wide)sudo cp certs/ca.crt /usr/local/share/ca-certificates/proxy-ca.crtsudo update-ca-certificates
# macOSsudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certs/ca.crt
# curl (per-request)curl --cacert certs/ca.crt -x http://localhost:8080 https://example.com
# Java (using PKCS#12 truststore from gencert --out-client-p12)java -Djavax.net.ssl.trustStore=truststore.p12 \ -Djavax.net.ssl.trustStoreType=PKCS12 \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar myapp.jarArchitecture
Section titled “Architecture”See doc/architecture.md for detailed design documentation.
License
Section titled “License”MIT
Docs for v4.1.1 · built from the release tag