TLS Interception Setup Guide#
Pipelock can intercept CONNECT tunnel traffic by performing a TLS MITM: it terminates TLS with the client using a forged certificate, scans the decrypted request and response, then forwards to the upstream server over a separate TLS connection. This closes the body-blindness gap that exists with opaque CONNECT tunnels.
Without TLS interception, CONNECT tunnels only get hostname-level scanning (blocklist, SSRF, rate limiting). With it, you get full DLP on request bodies/headers and response injection detection.
What is enforced when interception is off#
An HTTPS request through a CONNECT tunnel is encrypted end to end, so with interception disabled Pipelock sees the CONNECT host and handshake, but cannot read the inner request path, query, body, or response. For HTTPS traffic in that mode:
- Enforced (from CONNECT metadata or tunnel accounting): destination allowlist/blocklist, SSRF on the CONNECT host, URL DLP/entropy checks that apply to the CONNECT host, connection rate limits, data budget, CONNECT handshake-header DLP, kill switch, and receipt gates. A
request_policyor contract rule is enforced only to the extent it matches CONNECT metadata (the synthetichttps://host/); any rule that matches inner HTTPS method, path, query, headers, or body requires interception. - Not applied to the encrypted HTTPS content: path/query entropy, body DLP,
request_body_scanning, andresponse_scanning. These operate on plaintext the proxy never sees for HTTPS, so they silently do not inspect HTTPS request paths, bodies, or responses.
This is expected behavior, not a bypass: tunnel-level controls still apply, but HTTPS content visibility does not exist unless Pipelock terminates TLS. Plaintext HTTP through the forward proxy is unaffected (its full URL and body are visible, so all scanners apply).
When the forward proxy accepts CONNECT tunnels and interception is off, Pipelock prints a startup and hot-reload advisory so the coverage gap is never silent:
WARNING: tls_interception: TLS interception is disabled while the forward proxy accepts CONNECT tunnels. HTTPS traffic is not decrypted, so content scanning (request_body_scanning, response_scanning, path/query entropy, and body DLP) does not inspect HTTPS request paths, bodies, or responses. Pipelock still enforces controls that evaluate from CONNECT metadata or tunnel accounting (destination allowlist/blocklist, SSRF on the CONNECT host, rate limits, data budget, CONNECT handshake-header DLP, kill switch, and receipt gates); request_policy and contract rules that match inner HTTPS method, path, query, headers, or body require tls_interception. Enable tls_interception (and distribute its CA) to scan HTTPS content, or accept tunnel-level-only visibility for HTTPS content as a deliberate posture.
Enable interception (below) to scan HTTPS content, or accept tunnel-level-only HTTPS content visibility as a deliberate posture.
Quick Start#
# 1. Generate a CA
pipelock tls init
# 2. Trust it (prints platform-specific instructions)
pipelock tls install-ca
# 3. Enable in config
cat >> pipelock.yaml << 'EOF'
tls_interception:
enabled: true
EOF
# 4. Run
pipelock run --config pipelock.yaml
Step 1: Generate the CA#
pipelock tls init
This creates two files in ~/.pipelock/:
ca.pem: the CA certificate (share this, it's public)ca-key.pem: the CA private key (protect this,0600permissions)
Options:
| Flag | Default | Description |
|---|---|---|
--out | ~/.pipelock | Output directory |
--org | Pipelock | Organization name in certificate subject |
--validity | 87600h (10 years) | How long the CA is valid |
--force | false | Overwrite existing files |
Custom output directory:
pipelock tls init --out /etc/pipelock/tls --org "My Company"
If using a custom directory, set ca_cert and ca_key in your config:
tls_interception:
enabled: true
ca_cert: /etc/pipelock/tls/ca.pem
ca_key: /etc/pipelock/tls/ca-key.pem
Use
pipelock tls init— don't hand-mint the CA with openssl RSA. Pipelock's CA loader (certgen.LoadCA) callsx509.ParseECPrivateKeyon the key file, so it requires an ECDSA private key (any curve thecrypto/ecdsapackage accepts — P-224, P-256, P-384, P-521). RSA and Ed25519 CA keys are rejected at startup withload TLS CA: parse ec private keyand pipelock exits rather than run without interception.pipelock tls initgenerates a P-256 ECDSA CA, which is what end-entity certs pipelock mints at runtime also use; if you need an org-rooted CA chain, an ECDSA intermediate (e.g.openssl ecparam -name prime256v1) signed from your root will load. End-entity certs for your upstream servers signed by this CA can be RSA without issue — the ECDSA constraint is only on the CA key itself.
Step 2: Trust the CA#
The agent (or whatever makes HTTPS connections through pipelock) must trust the CA certificate. Otherwise TLS handshakes fail with certificate verification errors.
System Trust Store#
pipelock tls install-ca
This prints platform-specific instructions. You still need to run the commands it shows.
Linux (Debian/Ubuntu):
sudo cp ~/.pipelock/ca.pem /usr/local/share/ca-certificates/pipelock-ca.crt
sudo update-ca-certificates
Linux (RHEL/Fedora):
sudo cp ~/.pipelock/ca.pem /etc/pki/ca-trust/source/anchors/pipelock-ca.crt
sudo update-ca-trust extract
macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ~/.pipelock/ca.pem
Windows (elevated Command Prompt):
certutil -addstore -f "ROOT" %USERPROFILE%\.pipelock\ca.pem
Per-Application Trust#
Some tools ignore the system trust store. Set the CA path explicitly:
Node.js / npm:
export NODE_EXTRA_CA_CERTS=~/.pipelock/ca.pem
Python (requests/httpx):
export REQUESTS_CA_BUNDLE=~/.pipelock/ca.pem
export SSL_CERT_FILE=~/.pipelock/ca.pem
Go:
export SSL_CERT_FILE=~/.pipelock/ca.pem
curl:
curl --cacert ~/.pipelock/ca.pem https://example.com
# Or set globally:
export CURL_CA_BUNDLE=~/.pipelock/ca.pem
Step 3: Configure#
Minimal config:
tls_interception:
enabled: true
Full options:
tls_interception:
enabled: true
ca_cert: "" # default: ~/.pipelock/ca.pem
ca_key: "" # default: ~/.pipelock/ca-key.pem
max_response_bytes: 5242880 # 5MB, block responses larger than this
passthrough_domains: # bypass interception for these domains
- "*.pinned-service.com"
- "api.payment-provider.com"
Passthrough Domains#
Some services use certificate pinning or mutual TLS that breaks under interception. Add them to passthrough_domains:
tls_interception:
enabled: true
passthrough_domains:
- "*.apple.com" # Apple services pin certificates
- "mtls.internal.corp.com" # mTLS endpoint
Passthrough connections are spliced (bidirectional byte copy) without decryption. Hostname-level scanning (blocklist, SSRF, SNI verification) still applies.
Supports exact match (api.example.com) and wildcard prefix (*.example.com matches sub.example.com and deep.sub.example.com, but not the apex example.com).
Fail-Closed Behavior#
TLS interception is fail-closed:
- Compressed responses (Content-Encoding other than identity): blocked (scanning would be bypassed)
- Responses larger than
max_response_bytes: blocked - TLS handshake failures: connection closed
- Certificate generation errors: connection closed
Verifying It Works#
# Start pipelock with TLS interception
pipelock run --config pipelock.yaml &
# Test through the proxy (should succeed)
HTTPS_PROXY=http://127.0.0.1:8888 curl -s https://example.com
# Test DLP through CONNECT tunnel (should be blocked)
HTTPS_PROXY=http://127.0.0.1:8888 \
curl -s "https://httpbin.org/post" \
-d "token=AKIAIOSFODNN7EXAMPLE"
Check the pipelock logs (stderr) for scan results.
Troubleshooting#
"certificate signed by unknown authority"#
The agent doesn't trust the pipelock CA. Either:
- Install the CA in the system trust store (Step 2)
- Set the per-application CA env var (see above)
- Add the domain to
passthrough_domainsif you can't modify the client
"x509: certificate is valid for X, not Y"#
The hostname in the request doesn't match what pipelock generated. This usually means a DNS or proxy misconfiguration. Check that HTTPS_PROXY is set correctly and the target hostname resolves properly.
Compressed response blocked#
Pipelock blocks compressed responses during interception because it can't scan content it can't read. The upstream server sent Content-Encoding: gzip (or similar). Pipelock's transport sets Accept-Encoding: identity to request uncompressed responses, but some servers ignore this.
If you trust the domain, add it to passthrough_domains.
Performance#
TLS interception adds latency for the MITM handshake. Pipelock mitigates this with:
- Connection pooling (shared
http.Transportreuses TCP+TLS connections) - Bounded certificate cache (avoids regenerating leaf certs for repeated hosts)
- ECDSA P-256 keys (faster than RSA for signing)
For high-throughput environments, consider using passthrough for trusted high-volume domains.