DEV Community

ZeroTrust Architect
ZeroTrust Architect

Posted on • Edited on • Originally published at cacheguard.com

TLS Interception at the Gateway: Certificate Chains, SNI, and the SSL Pinning Problem

TLS inspection at a forward proxy — sometimes called SSL interception or SSL mediation — is a controlled man-in-the-middle. The proxy terminates the client's TLS session, reads the plaintext, and opens a new TLS session to the upstream server. Understanding the mechanics matters for deployment, debugging, and reasoning about what the proxy can and cannot see.

What is SSL Inspection?

The certificate generation pipeline

When the proxy intercepts a CONNECT request for www.example.com:443, it must present a certificate for www.example.com to the client — a certificate the client's browser will accept. Generating this on the fly requires:

  1. The proxy fetches the real certificate from www.example.com (or uses cached metadata)
  2. It extracts the Subject Alternative Names and CN from the real cert
  3. It generates a new certificate with matching SANs, signed by the proxy's local CA
  4. It presents this dynamically generated certificate to the client

The client browser validates the chain: generated cert → proxy CA → (trusted if proxy CA is in browser trust store). If the proxy CA is not trusted, the browser throws NET::ERR_CERT_AUTHORITY_INVALID.

CA distribution

The proxy CA must be added to every client's trust store before enabling TLS inspection. Deployment methods:

Windows (domain-joined): Group Policy Object → Computer Configuration → Windows Settings → Security Settings → Public Key Policies → Trusted Root Certification Authorities.

# Verify CA is present after GPO application
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*CacheGuard*" }
Enter fullscreen mode Exit fullscreen mode

macOS (MDM): Push CA cert via configuration profile → PayloadType: com.apple.security.root.

Linux (system-wide):

cp proxy-ca.crt /usr/local/share/ca-certificates/
update-ca-certificates
Enter fullscreen mode Exit fullscreen mode

Firefox: Firefox maintains its own trust store independent of the OS. The CA must be added separately via policies (certificates.certs[] in policies.json) or manually.

SNI and the hostname visibility problem

In TLS 1.3, the client sends the server_name extension (SNI) in the ClientHello. The SNI is transmitted in plaintext — the proxy sees it without decryption. This means the proxy knows which hostname the client is connecting to even before the TLS handshake completes, regardless of whether inspection is enabled.

With inspection enabled, the proxy can additionally see:

  • The full HTTP request line and headers
  • The request body
  • The full HTTP response headers and body

Without inspection, the proxy sees only: SNI (hostname), destination IP, port, and approximate traffic volume.

SSL pinning: the hard case

SSL pinning is a client-side mechanism where an application refuses any TLS certificate except a specifically expected one (or one signed by a specifically expected CA). The application ignores the OS trust store.

When the proxy presents its dynamically generated certificate for api.bank.com, a pinned application compares the certificate fingerprint (or CA public key) against its embedded expectation. They do not match. The application refuses to connect.

Common pinning implementations:

  • Certificate pinning: Exact certificate fingerprint match
  • Public key pinning: CA or leaf public key hash match (more flexible — survives certificate renewal if the key doesn't change)
  • HPKP (deprecated): Browser-level public key pinning via HTTP header

Handling pinned applications at the proxy:

The only correct solution is an exception — the proxy bypasses TLS inspection for pinned destinations, allowing them to connect directly.

# Squid: exempt pinned destinations from ssl-bump
acl ssl_no_bump_domains ssl::server_name_regex -i \
    api.bank.com \
    pin.cloudflare.com \
    apple.com

ssl_bump splice ssl_no_bump_domains
ssl_bump stare all
ssl_bump bump all
Enter fullscreen mode Exit fullscreen mode

Identifying pinned applications requires monitoring your proxy logs for SSL certificate error events after enabling inspection — these are usually pinning failures.

What the proxy cannot see even with inspection

  • ESNI/ECH (Encrypted Client Hello): Encrypts the SNI itself using a public key published in DNS. The proxy sees only the encrypted outer hello. Full deployment is not yet universal (2026) but growing.
  • Certificate Transparency logs: CT does not affect proxy visibility, but it means all dynamically generated certificates would appear in CT logs if submitted — they are not (proxy CAs are not CT-logging).
  • mTLS (mutual TLS): If the upstream requires a client certificate from the actual end device, the proxy cannot satisfy this requirement without the client's private key.

CacheGuard calls this feature SSL mediation. It generates the local CA at installation, provides a download link for the CA cert to push to clients, and maintains a bypass list for pinned domains.

https://www.cacheguard.com/ssl-inspection/


Originally published on the CacheGuard Blog. CacheGuard is free and open source — GitHub.

Top comments (0)