MITM TLS Interception

elide fwd --mitm intercepts HTTPS connections by generating per-host TLS certificates on the fly, signed by a local Certificate Authority. The proxy gets full visibility into encrypted traffic — headers, bodies, timing — while presenting a valid certificate chain to the client.

This page covers setup, how the interception pipeline works, what it can and cannot see, and the security boundaries you need to understand before deploying it.

MITM interception decrypts all HTTPS traffic flowing through the proxy. Only use this on networks and devices you own or have explicit authorization to inspect. Never deploy an open MITM proxy on a shared network.

---

Setup guide

From zero to intercepting HTTPS traffic in four steps.

1. Generate a proxy CA

Create a dedicated Certificate Authority for MITM interception. This is stored separately from the general-purpose Elide CA used by elide crt generate for server certificates:

bash
 elide crt ca --proxy

This creates an ECDSA P-256 CA certificate and private key, valid for 10 years by default. Stored at ~/.local/share/elide/pki/proxy/.

Customize the key type and validity:

bash
 elide crt ca --proxy --key-type ecdsa-p384 --days 365 --name "My Proxy CA"

View the existing proxy CA without regenerating:

bash
 elide crt ca --proxy --show

2. Trust the proxy CA

Install the CA into your system trust store so browsers and TLS clients accept the generated certificates:

bash
 elide crt trust --proxy
Platform-specific behavior:
PlatformTrust store
macOSSystem Keychain (via security add-trusted-cert)
Linux (Debian/Ubuntu)/usr/local/share/ca-certificates/ + update-ca-certificates
Linux (RHEL/Fedora)/etc/pki/ca-trust/source/anchors/ + update-ca-trust
WindowsCurrent User certificate store (via certutil)
On Linux, elide crt trust requires root (or sudo) to write to the system CA directory. On macOS, the Keychain authorization dialog prompts for your password.

3. Start the proxy

bash
 elide fwd --mitm

The proxy loads the CA from ~/.local/share/elide/pki/proxy/, listens on 127.0.0.1:8080, and is ready to intercept.

To bring your own CA instead of the auto-generated one:

bash
 elide fwd --mitm --ca-cert /path/to/ca.pem --ca-key /path/to/ca-key.pem

4. Configure your client

Point your browser or system proxy settings at http://127.0.0.1:8080:

bash
 curl -x http://127.0.0.1:8080 https://example.com

Because the proxy CA is in the system trust store, the client sees a valid certificate chain and connects without warnings.

---

How interception works

Without MITM (default)

Client --CONNECT--> Proxy --TCP--> Origin
       <--200 OK--
       <=========== blind byte relay ===========>

The proxy never sees the plaintext. It relays encrypted bytes bidirectionally.

With MITM enabled

Client --CONNECT--> Proxy                           Origin
       <--200 OK--
       --TLS ClientHello-->
                     [generate leaf cert for host]
       <--TLS ServerHello--
       ==TLS session==      --TLS ClientHello-->
                             <--TLS ServerHello--
                             ==TLS session==
       == decrypted HTTP == == re-encrypted HTTP ==

In detail:

1. The proxy receives CONNECT and validates the target against the ACL (deny hosts, deny ports, then allow rules). 2. It replies 200 Connection Established. 3. The client begins a TLS handshake. The proxy intercepts the ClientHello and extracts the SNI hostname. 4. The proxy generates (or retrieves from cache) a leaf certificate for that hostname, signed by the proxy CA. 5. The proxy completes the TLS handshake with the client using the generated certificate. 6. Simultaneously, the proxy opens its own TLS connection to the real origin. 7. Decrypted HTTP traffic flows through the proxy in both directions — fully inspectable and recordable.

Per-rule MITM overrides

With a PKL configuration file, individual proxy rules can override the global MITM setting. A rule can set mitm_override: true to enable interception for specific hosts only, or mitm_override: false to skip interception for certain hosts even when --mitm is globally active. Rules also support include_hosts and exclude_hosts patterns.

---

Per-host certificate generation

When the proxy intercepts a hostname it has not seen before, it generates a leaf certificate:
PropertyValue
Key algorithmECDSA P-256
Subject CNThe intercepted hostname
Subject Alternative NameDNS name matching the intercepted hostname
Validity24 hours from generation time
Key usageDigital signature, key encipherment
IssuerThe proxy CA (auto-generated or provided via —ca-cert/—ca-key)
CacheLRU, up to 1,024 hostnames (configurable via PKL max_cached_certs)
Certificates are generated and signed by the proxy CA's private key. The certificate and key pair is cached in a concurrent LRU map keyed by hostname; when full, the least recently used entry is evicted.

Generation is fast — ECDSA P-256 key generation plus signing takes under 1ms on modern hardware. The first connection to a new hostname pays this cost; subsequent connections within the cache window are instant.

---

What gets intercepted

Visible with MITM

  • All HTTP methods (GET, POST, PUT, DELETE, PATCH, etc.)
  • Request and response headers
  • Request and response bodies
  • WebSocket frames after the HTTP upgrade
  • HTTP/1.1 and HTTP/2 over the proxy-to-origin leg

Not visible

  • L4 relay connections (--l4) — SNI-routed TCP relay never terminates TLS. Only the hostname from the ClientHello and byte counts are visible.
  • Non-HTTP protocols via CONNECT — if a client tunnels SSH or another protocol over CONNECT, the proxy relays bytes without understanding the inner protocol.
  • Certificate-pinned clients — mobile apps and some browsers that pin certificates will reject the proxy's generated certificate. These connections fail with a TLS error on the client side. The proxy cannot override pinning.
  • QUIC/HTTP3 — QUIC bypasses the proxy entirely (no CONNECT). Block UDP/443 at the network level to force clients back to TCP.

---

Certificate Transparency

The PKI crate includes a local Certificate Transparency log. When configured, every leaf certificate generated by the MITM interceptor can be submitted to this log, and the resulting Signed Certificate Timestamp (SCT) is embedded in the leaf certificate as an X.509 extension (OID 1.3.6.1.4.1.11129.2.4.2, per RFC 6962).

This is useful for auditing which hosts were intercepted and when. The log is stored locally — nothing is submitted to public CT logs.

CT logging for the MITM proxy is available through PKL configuration. Not currently exposed as a CLI flag.

---

Security considerations

Trust boundary

Installing the proxy CA into the system trust store means any process running as the same user can present certificates signed by that CA. This is the same trust model as corporate proxy appliances and tools like mitmproxy or Charles Proxy.

Recommendations

Scope the CA to the proxy's lifetime. Remove it from the trust store when you are done:
bash
 elide crt untrust --proxy
Use a dedicated browser profile. Import the CA into a specific browser profile or Firefox's own certificate store instead of trusting it system-wide. This limits the blast radius. Protect the CA private key. The key at ~/.local/share/elide/pki/proxy/ can sign certificates for any hostname. Treat it like a root credential. File permissions are set to 0600 on creation. Do not expose the proxy to untrusted networks. Bind to 127.0.0.1 (the default) or use --allow-clients to restrict access. Regenerate the CA periodically:
bash
 elide crt ca --proxy --force
 elide crt trust --proxy

What the proxy does NOT do

  • It does not modify traffic by default. MITM provides read-only visibility. Per-rule header mutations are available via PKL proxy rules, but body modification is not yet supported.
  • It does not exfiltrate data. All recorded traffic stays local — in memory, or written to the HAR file configured in PKL.
  • It does not bypass certificate pinning on the client side.

---

Example: full inspection workflow

bash
# 1. Create and trust the proxy CA (one-time setup)
 elide crt ca --proxy
 elide crt trust --proxy

# 2. Start the proxy with MITM
 elide fwd --mitm

# 3. Configure your browser to use http://127.0.0.1:8080 as the proxy.
#    Browse normally. All HTTPS traffic is decrypted and visible in the TUI.

# 4. Stop the proxy (Ctrl-C or 'q' in the TUI).

# 5. Remove the CA from the trust store when done
 elide crt untrust --proxy

For traffic recording and HAR export, use a PKL configuration file with the recording section:

bash
 elide fwd --mitm --config proxy.pkl

The admin API (when --admin-port is set) provides endpoints for live inspection and on-demand HAR export.

---

Troubleshooting

Client shows certificate warning

The proxy CA is not trusted. Run elide crt trust --proxy and restart your browser. Firefox maintains its own certificate store independent of the system — you may need to import the CA manually via browser settings.

The proxy CA certificate is at ~/.local/share/elide/pki/proxy/ca.pem.

"no proxy CA found" error

The proxy CA has not been generated. Run elide crt ca --proxy first, or provide your own via --ca-cert and --ca-key.

Mobile app refuses to connect

Mobile apps frequently use certificate pinning. The proxy's generated certificate will not match the pinned cert, and the app rejects the connection. This is by design and cannot be bypassed.

curl works but browser does not

Verify the browser is actually using the proxy — some browsers bypass the system proxy for localhost or specific domains. Also check that the CA is in the correct trust store for your browser (system vs. browser-specific).

CONNECT denied even though target seems allowed

Deny rules are checked before allow rules. If the hostname matches a --block pattern or the port matches a --deny-ports entry, the request is rejected regardless of allow rules. When --allow-hosts is set, default-deny is active and any host not matching an allow pattern is rejected.

See also