go-egress-mitm-proxy

A MITM HTTP/HTTPS proxy that implements split-brain DNS for egress traffic control in containerised environments. Decide per host where traffic goes, what gets through and which headers travel with it.

  • v4.1.1 · latest
  • MIT
  • Go
  • Kubernetes sidecar
  • outbound HTTP/2
docker pull ghcr.io/datarocks-ag/go-egress-mitm-proxy:4.1.1

What it does

  • Split-brain DNS — via TCP dial interception, routing to target_ip or target_host
  • Path-based routing — different paths on one domain to different backends
  • ACL — whitelist, blacklist and passthrough with exact, wildcard and regex matching
  • Header policies — inject headers, strip sensitive ones, automatic X-Request-ID
  • Upstream TLS trust — custom PEM bundle or PKCS#12 truststore, per-target bypass
  • Hot reload — apply config changes with SIGHUP, no restart
  • Audit & tracing — JSON log of blocked requests, opt-in request traces with redaction
  • Operable — Prometheus metrics, health endpoints, graceful shutdown
configuration.yaml
proxy:
  port: "8080"
  metrics_port: "9090"
  default_policy: "BLOCK"   # Options: ALLOW, BLOCK
  outgoing_ca_bundle: ""    # Optional: path to PEM CA bundle for upstream TLS verification
  outgoing_ca: []           # Optional: list of individual CA cert files (can be combined with bundle)
  # outgoing_ca:
  #   - "certs/internal-ca.crt"
  #   - "certs/partner-ca.crt"

  # Optional: PKCS#12 truststore for upstream TLS verification (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

  # Ceiling on concurrent client connections (0 or unset = unlimited).
  # A hijacked CONNECT tunnel carries no deadline at any layer, so a client that
  # completes the CONNECT and then goes silent holds a file descriptor and a
  # goroutine until the process dies. This is what bounds that. At the cap the
  # proxy pauses accepting until a connection closes, warns, and increments
  # proxy_listener_saturated_total.
  # max_connections: 2048

  # MITM CA certificate - provide either PEM cert+key or a PKCS#12 keystore (not both)
  # Generate with: mitm-proxy gencert --type root --cn "My CA" --out-cert certs/ca.crt --out-key certs/ca.key
  # Or for intermediate CA: mitm-proxy gencert --type intermediate --signing-cert root.crt --signing-key root.key --out-chain certs/chain.crt --out-key certs/ca.key

  # Option A: PEM files (use --out-chain for intermediate CA setup)
  mitm_cert_path: "certs/ca.crt"
  mitm_key_path: "certs/ca.key"

  # Option B: PKCS#12 keystore (generate with: mitm-proxy gencert --out-p12 certs/ca.p12 --p12-password changeit)
  # mitm_keystore_path: "certs/ca.p12"
  # mitm_keystore_password: "changeit"

  # Optional: Organization on the MITM leaf certificates the proxy mints.
  # Default is goproxy's built-in "GoProxy untrusted MITM proxy Inc". Captured at
  # startup -- SIGHUP does not reload it.
Excerpt from doc/examples/configuration.yaml

How a request flows

Your app keeps calling the real hostnames. The proxy decides at dial time — not in DNS — whether and where the connection goes.

  1. Intercept

    The app sends HTTP and HTTPS through the proxy. TLS is terminated with your own internal CA.

  2. Check the ACL

    Whitelist, blacklist or passthrough. Anything unmatched follows default_policy — BLOCK answers 403.

  3. Rewrite

    Route by domain and path to a target IP or host, change scheme or port, inject or drop headers.

  4. Forward

    Dial the real target over HTTP/1.1 or HTTP/2, verified against your upstream CA bundle.

Patterns that don't fail open

ACLs, rewrites and traces share one domain syntax. Matching is case-insensitive, trailing dots are stripped, and regex patterns are anchored — so a denylist entry can't be slipped past.

PatternMatchesDoes not match
example.comexample.com, EXAMPLE.com, example.com.api.example.com
*.example.comapi.example.com, a.b.example.comexample.com (the apex)
*every host—
~^(.+\.)?example\.com$the apex and any subdomainexample.com.evil.net

Run it as a sidecar

Point your app at the proxy with the standard proxy variables. /healthz and /readyz plug into Kubernetes probes; on SIGTERM it fails readiness first, then drains in-flight requests.

deployment.yaml (app container)
env:
  - name: HTTPS_PROXY
    value: "http://localhost:8080"
  - name: HTTP_PROXY
    value: "http://localhost:8080"

Full documentation, configuration reference and how-tos.

Read the docs →

Data Rocks AG

[ONE OR TWO LINES ON WHAT DATA ROCKS DOES AND FOR WHOM]

Visit datarocks.ch →