docs: add research note assessing pipelock for egress/exfil control
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,531 @@
|
||||
# Pipelock assessment for claude-bottle egress control
|
||||
|
||||
Research into whether pipelock — an open-source AI agent firewall —
|
||||
is a suitable replacement for, or complement to, the egress-control
|
||||
approaches documented in `network-egress-guard.md` and the content-
|
||||
tripwire approach sketched in `secret-exfil-tripwire-encodings.md`.
|
||||
|
||||
## Summary
|
||||
|
||||
- Pipelock (`luckyPipewrench/pipelock`) is an open-source AI agent
|
||||
firewall implemented as a single Go binary. It sits inline as an HTTP
|
||||
forward proxy and, optionally, applies OS-level process containment. It
|
||||
is the most directly relevant tool found for claude-bottle's egress /
|
||||
data-exfiltration concerns.
|
||||
- Its strongest differentiator over the v1 iptables recommendation is
|
||||
**content-aware DLP**: it matches 48 credential patterns across
|
||||
multiple encodings in outbound request bodies and URL subdomains,
|
||||
covering ground that iptables and smokescreen cannot touch.
|
||||
- The process-containment sandbox (Landlock + seccomp + network
|
||||
namespaces) is **Linux-only at the kernel level**. In-container on
|
||||
macOS Docker Desktop the sandbox degrades to `--best-effort` mode,
|
||||
which relies on `HTTPS_PROXY` env var interception and is bypassable
|
||||
via raw socket calls or env-var unsetting. The proxy scanning layer
|
||||
works on both platforms.
|
||||
- Adopting pipelock as a **sidecar proxy** (replacing the smokescreen v2
|
||||
recommendation) is well-supported and adds DLP and subdomain-entropy
|
||||
DNS exfil detection that smokescreen lacks. The sidecar topology avoids
|
||||
the `--best-effort` degradation problem entirely.
|
||||
- Recommendation: skip pipelock as an in-container self-contained guard
|
||||
(the `--best-effort` caveat is significant on macOS Docker Desktop);
|
||||
adopt it as the **sidecar proxy** in place of smokescreen, pairing it
|
||||
with the in-container iptables+dnsmasq layer from the v1 plan.
|
||||
|
||||
---
|
||||
|
||||
## Identification
|
||||
|
||||
- **Canonical URL**: <https://github.com/luckyPipewrench/pipelock>
|
||||
- **Project site / documentation**: <https://pipelab.org/pipelock/>
|
||||
- **Maintainer**: `luckyPipewrench` (GitHub org); also associated with
|
||||
the `pipelab.org` domain. No named individual maintainer is surfaced in
|
||||
search results; the project presents as a company or team product.
|
||||
- **License**: dual-licensed. Core under **Apache 2.0**; an `enterprise/`
|
||||
subtree under **Elastic License v2 (ELv2)**, per the badges and `LICENSE`
|
||||
layout in the project README
|
||||
([README](https://github.com/luckyPipewrench/pipelock/blob/main/README.md)).
|
||||
Which capabilities sit on which side of that line is a question this note
|
||||
does not answer; before adoption, the `enterprise/` directory should be
|
||||
inspected to confirm that the features cited below (DLP patterns,
|
||||
subdomain entropy, MCP scanning, request redaction, sidecar topology) are
|
||||
available under the Apache 2.0 core. Mediator-signed action receipts and
|
||||
some attestation features may be ELv2-gated.
|
||||
- **Current stable release**: v2.3.0, published **2026-04-25** per the
|
||||
GitHub release metadata
|
||||
([release v2.3.0](https://github.com/luckyPipewrench/pipelock/releases/tag/v2.3.0)).
|
||||
An accompanying Help Net Security announcement appeared 2026-05-04
|
||||
([helpnetsecurity.com](https://www.helpnetsecurity.com/2026/05/04/pipelock-open-source-ai-agent-firewall/));
|
||||
earlier rough drafts of this note conflated the announcement date with
|
||||
the release date.
|
||||
- **Project age**: repository created **2026-02-08** per the GitHub API,
|
||||
so ~3 months old at the time of this note. v2.1.1, v2.1.2, v2.2.0, v2.3.0
|
||||
tags exist, indicating active iteration.
|
||||
- **Language / distribution**: Go; ships as a single static binary (~12–20 MB,
|
||||
sources differ slightly). Available via Docker image
|
||||
(`ghcr.io/luckypipewrench/pipelock:latest`), Homebrew
|
||||
(`brew install luckyPipewrench/tap/pipelock`), and
|
||||
`go install`. Stated zero runtime dependencies.
|
||||
|
||||
**What it actually does.** Pipelock is an application-layer HTTP forward
|
||||
proxy designed specifically for AI agent deployments. It sits between the
|
||||
agent process and the network and applies an 11-layer scanning pipeline to
|
||||
all outbound HTTP, HTTPS (via CONNECT), WebSocket, and MCP traffic. The
|
||||
pipeline covers: scheme enforcement, CRLF injection detection, path traversal
|
||||
blocking, domain blocklisting, DLP credential matching, subdomain entropy
|
||||
analysis, SSRF protection (RFC 1918 + metadata endpoints blocked by default),
|
||||
rate limiting, URL length limits, and per-domain data budgets. An optional
|
||||
sandbox mode on Linux wraps the agent child process with Landlock filesystem
|
||||
restrictions, seccomp syscall filtering, and a network namespace that forces
|
||||
all traffic through pipelock's scanner. On macOS the equivalent is
|
||||
`sandbox-exec` with dynamic SBPL profiles. The tool is positioned at the
|
||||
intersection of egress control and AI-specific threat vectors (prompt
|
||||
injection, MCP tool poisoning, credential leaks in LLM outputs).
|
||||
|
||||
**Disambiguation note.** The name "pipelock" could in principle refer to
|
||||
Python pip lockfile tooling or other unrelated utilities. In the context of
|
||||
containerized AI agent egress filtering there is exactly one relevant project:
|
||||
`luckyPipewrench/pipelock`. No other tool bearing that name addresses the
|
||||
egress / DLP problem space.
|
||||
|
||||
---
|
||||
|
||||
## Capabilities vs the threat model
|
||||
|
||||
### HTTP(S) egress
|
||||
|
||||
Pipelock covers HTTP and HTTPS (CONNECT) egress in full. Its forward proxy
|
||||
mode requires zero code changes in the agent — Claude Code already respects
|
||||
`HTTP_PROXY` / `HTTPS_PROXY` environment variables, which is the standard
|
||||
injection vector
|
||||
([Claude Code network config docs](https://code.claude.com/docs/en/network-config)).
|
||||
In `strict` mode, all outbound HTTP is blocked unless the destination is on
|
||||
the `api_allowlist`; in `balanced` and `audit` modes the allowlist is advisory.
|
||||
This covers threat 1 (HTTP(S) data exfiltration) from the `network-egress-guard.md`
|
||||
threat model.
|
||||
|
||||
The proxy scanning pipeline also covers outbound WebSocket and MCP traffic,
|
||||
which the iptables approach allows through on the basis of IP address alone
|
||||
and which smokescreen passes without content inspection. Pipelock's
|
||||
bidirectional MCP scanner catches credential leaks in MCP tool call arguments
|
||||
and prompt-injection payloads in tool responses, neither of which an IP- or
|
||||
CONNECT-hostname-based firewall can detect.
|
||||
|
||||
What it leaves uncovered at this layer: raw TCP (non-HTTP), UDP, and ICMP.
|
||||
These are the same gaps documented for smokescreen in `network-egress-guard.md`
|
||||
and must be closed by a complementary iptables or `--network none` layer.
|
||||
|
||||
### DNS exfiltration
|
||||
|
||||
This is pipelock's most significant advantage over the v1 iptables
|
||||
recommendation and over smokescreen. The `network-egress-guard.md` note
|
||||
flags DNS tunneling (threat 2) as a real gap in IP-based allowlists and
|
||||
notes that the only partial mitigations are a controlled resolver (dnsmasq)
|
||||
plus blocking direct UDP/53 access to non-allowlisted resolvers.
|
||||
|
||||
Pipelock addresses the same problem from a different angle: it runs a
|
||||
**subdomain entropy analysis** pass against every URL before resolving it.
|
||||
This catches base64- and hex-encoded keys embedded in subdomain labels —
|
||||
the dominant DNS exfiltration technique — by detecting abnormally high
|
||||
Shannon entropy in the leftmost subdomain components. Crucially, the DLP
|
||||
scanner runs **before** DNS resolution, meaning a secret encoded into a
|
||||
subdomain cannot escape via the DNS query itself even if the HTTP request
|
||||
is later blocked
|
||||
([pipelab.org/pipelock/](https://pipelab.org/pipelock/)).
|
||||
A DEV Community post by the maintainer explicitly addresses the
|
||||
"AI agents leak API keys through DNS queries" scenario
|
||||
([dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d](https://dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d)).
|
||||
|
||||
Coverage gaps remain. Pipelock operates at the HTTP proxy layer; it does
|
||||
not intercept raw UDP port 53 traffic from the container. An agent making a
|
||||
direct DNS query (not mediated through pipelock's resolver) can still
|
||||
exfiltrate via DNS. Closing this gap still requires one of:
|
||||
- Blocking direct UDP/53 from the container via iptables and routing all
|
||||
DNS through a controlled resolver, or
|
||||
- Combining pipelock's subdomain-entropy check (effective at the URL layer)
|
||||
with an in-container dnsmasq that blocks non-allowlisted queries.
|
||||
|
||||
The dnsmasq complement from the v1 plan therefore remains relevant even if
|
||||
pipelock is adopted as the sidecar proxy.
|
||||
|
||||
### RFC 1918 / lateral movement
|
||||
|
||||
Pipelock blocks connections to private IP ranges, cloud metadata endpoints
|
||||
(169.254.169.254), and link-local addresses as part of its SSRF protection
|
||||
layer, which is active by default
|
||||
([pipelock/docs/configuration.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/configuration.md)).
|
||||
This matches the behavior of smokescreen, which also blocks RFC 1918 by
|
||||
default and was cited in `network-egress-guard.md` as a primary advantage
|
||||
of the proxy sidecar approach over raw iptables rules.
|
||||
|
||||
Pipelock adds DNS rebinding protection on top, which smokescreen does not
|
||||
document: it checks that the IP a hostname resolves to at connection time
|
||||
is not in a private range, guarding against the class of attack where a
|
||||
benign domain is resolved to a public IP during the allowlist check and then
|
||||
re-resolved to a private IP when the connection is made.
|
||||
|
||||
Domains can be whitelisted from the SSRF IP check individually (for
|
||||
legitimate VPN-routed internal APIs), while the DLP scanner continues to
|
||||
apply to those connections
|
||||
([pipelab.org/pipelock/](https://pipelab.org/pipelock/)).
|
||||
|
||||
### Content-based exfil detection (DLP)
|
||||
|
||||
This is the area where pipelock most directly covers ground not addressed
|
||||
by either the v1 iptables approach or the smokescreen sidecar, and it maps
|
||||
directly to the problem space of `secret-exfil-tripwire-encodings.md`.
|
||||
|
||||
The DLP layer matches **48 credential patterns** across the URL, headers, and
|
||||
request body. It uses four checksum validators (Luhn, mod-97, ABA routing
|
||||
numbers, WIF bitcoin keys) to reduce false positives and applies
|
||||
encoding-aware decoding before matching: at minimum base64, hex, multi-layer
|
||||
URL encoding, and Unicode evasion (confusable characters, combining marks,
|
||||
control character insertion, field splitting, whitespace manipulation)
|
||||
([pipelab.org/pipelock/](https://pipelab.org/pipelock/),
|
||||
[github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md)).
|
||||
The subdomain entropy check covers environment variable leaks (values 16+
|
||||
characters with Shannon entropy above 3.0) in base64, hex, and base32 forms.
|
||||
|
||||
As of v2.3.0, the DLP layer also covers **request redaction**: matched
|
||||
secrets are rewritten in-flight with typed placeholders
|
||||
(`<pl:aws-access-key:1>`, etc.) rather than simply blocked, preserving the
|
||||
class information for downstream tooling while stripping the value from the
|
||||
outbound request
|
||||
([pipelab.org/blog/pipelock-v230-release/](https://pipelab.org/blog/pipelock-v230-release/)).
|
||||
This addresses the use case described in `secret-exfil-tripwire-encodings.md`
|
||||
where the goal is detection rather than total blocking.
|
||||
|
||||
Coverage relative to the encoding catalog in `secret-exfil-tripwire-encodings.md`:
|
||||
the 25-encoding table in that note ranges from base64 (covered) through
|
||||
ROT13, double base64, and gzip+base64. Pipelock's documented encoding
|
||||
coverage (base64, hex, URL encoding, Unicode normalization) covers
|
||||
approximately the first ten entries of that catalog. There is no documented
|
||||
coverage for ROT13, ROT47, gzip+base64, or steganographic encodings — consistent
|
||||
with that note's conclusion that those are diminishing-returns cases for a
|
||||
non-adversarially-sophisticated agent.
|
||||
|
||||
The encoding-normalization pipeline applies before the pattern match, so
|
||||
a secret base64-encoded inside a JSON body will be decoded and matched; it
|
||||
is not necessary to pre-compute all encoded forms and load them as separate
|
||||
patterns, which was the DIY approach sketched in `secret-exfil-tripwire-encodings.md`.
|
||||
|
||||
### Scope gaps
|
||||
|
||||
The following threat-model items from `network-egress-guard.md` are
|
||||
**not** addressed by pipelock at the proxy layer:
|
||||
|
||||
- **Raw TCP (non-HTTP)**: direct database connections, LDAP, custom
|
||||
protocols, and anything that does not go through `HTTPS_PROXY`. These
|
||||
require a complementary IP-based default-deny layer (iptables or
|
||||
`--network none`).
|
||||
- **UDP**: DNS queries bypassing the proxy, QUIC, ICMP covert channels.
|
||||
Pipelock's subdomain entropy check fires on URLs it sees; it does not
|
||||
intercept raw UDP 53 packets.
|
||||
- **Domain fronting**: an agent can send `CONNECT allowed-host.com:443`
|
||||
through the proxy but embed a different SNI inside the TLS session.
|
||||
Pipelock does not perform TLS inspection (no CA trust injection) and
|
||||
cannot verify SNI vs. CONNECT header. The same limitation is shared
|
||||
with smokescreen and is documented in `network-egress-guard.md` as a
|
||||
known gap for the non-TLS-terminating proxy approach.
|
||||
- **SSH egress content**: SSH sessions to permitted hosts are opaque.
|
||||
Same limitation noted in both prior research notes.
|
||||
- **Agent killing the proxy process**: if pipelock runs inside the same
|
||||
container as the agent rather than as a sidecar, the agent's shell
|
||||
access could kill the proxy process. The sidecar topology mitigates this.
|
||||
|
||||
---
|
||||
|
||||
## Fit for claude-bottle
|
||||
|
||||
### Deployment topology
|
||||
|
||||
Pipelock explicitly supports two deployment shapes relevant to
|
||||
claude-bottle:
|
||||
|
||||
**Sidecar proxy.** A separate container running pipelock on an
|
||||
`--internal` Docker network, with the agent container's only route to the
|
||||
outside world through the proxy. Pipelock can generate a ready-made Docker
|
||||
Compose file for this topology:
|
||||
|
||||
```
|
||||
pipelock generate docker-compose --agent claude-code -o docker-compose.yaml
|
||||
```
|
||||
|
||||
The agent container is placed on a network with `internal: true` (which
|
||||
removes the default gateway at the iptables level inside Docker's network
|
||||
plumbing); the only reachable address is the pipelock container. The agent
|
||||
sets `HTTPS_PROXY` / `HTTP_PROXY` to the pipelock service address. This is
|
||||
structurally identical to the smokescreen sidecar described in
|
||||
`network-egress-guard.md`, but with the richer scanning pipeline. The
|
||||
Docker image is available at `ghcr.io/luckypipewrench/pipelock:latest`
|
||||
([pipelock/docs/guides/deployment-recipes.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/guides/deployment-recipes.md)).
|
||||
|
||||
**In-container with sandbox enabled.** Pipelock forks the agent as a child
|
||||
process, wraps it with OS-level containment, and routes all traffic through
|
||||
its own scanner. This avoids a second container but introduces the
|
||||
`--best-effort` degradation problem described below and means the agent and
|
||||
the proxy run in the same failure domain.
|
||||
|
||||
The sidecar topology is recommended for claude-bottle because it matches
|
||||
the existing bash-orchestrated multi-container model (the SSH key agent
|
||||
already uses a separate process), keeps pipelock outside the agent's kill
|
||||
reach, and avoids the `--best-effort` issue on macOS Docker Desktop.
|
||||
|
||||
The claude-bottle manifest model would need one new piece of plumbing: a
|
||||
per-agent pipelock ACL YAML generated from the manifest's `allowlist`
|
||||
and `ssh` entries, analogous to what the smokescreen section of
|
||||
`network-egress-guard.md` already sketches. The `cli.sh` changes required
|
||||
are the same pattern: `docker network create --internal`, `docker run` for
|
||||
the proxy container, then `docker run` for the agent with `HTTPS_PROXY`
|
||||
injected.
|
||||
|
||||
### macOS Docker Desktop compatibility
|
||||
|
||||
The proxy layer of pipelock (the HTTP forward proxy, DLP scanning, SSRF
|
||||
blocking, subdomain entropy analysis) works on both macOS and Linux. The
|
||||
Docker image and Homebrew package are available for macOS. There are no
|
||||
Linux-specific kernel dependencies for the proxy functionality itself.
|
||||
|
||||
The **sandbox mode** is where the platform split matters. On Linux the
|
||||
sandbox uses Landlock LSM, seccomp, and network namespaces, all requiring
|
||||
Linux 5.13+ kernel features. On macOS it uses `sandbox-exec` with SBPL
|
||||
profiles, which requires macOS 13+. When pipelock runs **inside** a Docker
|
||||
container on macOS Docker Desktop, the container's kernel is Linux (LinuxKit),
|
||||
so the sandbox mode attempts to use the Linux path. Whether the LinuxKit
|
||||
kernel version in Docker Desktop ships with the necessary Landlock ABI v1
|
||||
support (Linux 5.13+) and allows `CLONE_NEWUSER` for unprivileged network
|
||||
namespaces is a version-dependent question not authoritatively answered in
|
||||
the pipelock documentation.
|
||||
|
||||
Pipelock itself documents that if `CLONE_NEWUSER` is unavailable inside a
|
||||
container, it falls back to `--best-effort` mode:
|
||||
|
||||
> When user namespaces are unavailable, pipelock cannot create a network
|
||||
> namespace for the child process, so outbound traffic enforcement relies
|
||||
> only on HTTP_PROXY / HTTPS_PROXY environment variables, which can be
|
||||
> bypassed.
|
||||
> ([github.com/luckyPipewrench/pipelock](https://github.com/luckyPipewrench/pipelock))
|
||||
|
||||
In the **sidecar topology** this problem disappears: pipelock runs as its
|
||||
own container with no child-process sandbox, and enforcement is achieved via
|
||||
Docker's `--internal` network flag (which removes the default route at the
|
||||
iptables level in the LinuxKit VM). The agent cannot bypass this by unsetting
|
||||
`HTTPS_PROXY` because there is literally no route to the internet from its
|
||||
network namespace. The sidecar is therefore the correct deployment shape for
|
||||
macOS Docker Desktop.
|
||||
|
||||
### Sandbox mode in-container caveat
|
||||
|
||||
Even on a Linux Docker Engine host, running pipelock in sandbox mode inside
|
||||
a container requires careful capability configuration. The sandbox uses
|
||||
`CLONE_NEWUSER` to create an unprivileged network namespace; Docker's default
|
||||
seccomp profile may block this syscall. The fix is either to adjust the
|
||||
container's seccomp profile, set `kernel.unprivileged_userns_clone=1` on the
|
||||
host, or use the sidecar topology instead. In `--best-effort` mode pipelock
|
||||
emits a startup `WARNING: DEGRADED` log line and relies only on
|
||||
proxy-environment-variable interception — meaning a subprocess that calls
|
||||
`unsetenv("HTTPS_PROXY")` before making a socket call bypasses the scanner.
|
||||
|
||||
The pipelock documentation explicitly acknowledges this bypass and recommends
|
||||
the sidecar topology as the kernel-enforced equivalent for containerized
|
||||
deployments
|
||||
([github.com/luckyPipewrench/pipelock](https://github.com/luckyPipewrench/pipelock)).
|
||||
|
||||
### Configuration shape and manifest integration
|
||||
|
||||
Pipelock is configured via a single YAML file. A starter config can be
|
||||
generated with `pipelock generate config --preset balanced > pipelock.yaml`.
|
||||
The config watcher picks up changes at runtime (100ms debounce on SIGHUP or
|
||||
file events), so per-agent ACL updates do not require a container restart.
|
||||
|
||||
For claude-bottle, the relevant per-agent configuration is the domain
|
||||
allowlist. The manifest already captures the necessary inputs: the `ssh`
|
||||
array has target hostnames, and an `allowlist` key is planned for the v2
|
||||
egress work (per `network-egress-guard.md`). Generating a per-agent pipelock
|
||||
YAML from those manifest fields is the same pattern as the per-agent
|
||||
smokescreen ACL YAML described in that note — a straightforward `lib/` script
|
||||
producing a temp-file YAML and `docker cp`'ing it into the proxy container.
|
||||
|
||||
The YAML format is more expressive than smokescreen's YAML ACL: it also
|
||||
carries DLP sensitivity settings, per-domain data budgets, and rate limits.
|
||||
For a first integration pass, only the `api_allowlist` section needs
|
||||
per-agent population; the rest of the defaults are appropriate for the
|
||||
claude-bottle threat model.
|
||||
|
||||
### Runtime footprint
|
||||
|
||||
A single Go binary, ~12–20 MB (sources report slightly different figures; the
|
||||
GitHub description says "~20 MB" and the randomcpu.com writeup says "~12 MB").
|
||||
Zero runtime dependencies; the Go standard library is statically linked. This
|
||||
is consistent with claude-bottle's low-dependency principle. Adding Go as a
|
||||
host build dependency is not required — the binary is fetched from a Docker
|
||||
image or Homebrew.
|
||||
|
||||
The Docker image at `ghcr.io/luckypipewrench/pipelock:latest` is the natural
|
||||
integration point for the sidecar topology. No new runtimes or package managers
|
||||
are needed on the host; the manifest-and-launch scripts stay in bash.
|
||||
|
||||
One new concept is the pipelock YAML config file format. It is documented and
|
||||
generated by `pipelock generate config`; the schema is YAML with clear keys. It
|
||||
is simpler to reason about than iptables rules and does not require per-container
|
||||
privilege grants.
|
||||
|
||||
---
|
||||
|
||||
## Comparison table
|
||||
|
||||
The axes match those used in `network-egress-guard.md` with three additions
|
||||
(DLP/content detection, DNS subdomain entropy, project recency) to capture
|
||||
pipelock's differentiators.
|
||||
|
||||
| | iptables+dnsmasq (v1) | smokescreen sidecar (v2) | pipelock sidecar | do nothing |
|
||||
|---|---|---|---|---|
|
||||
| HTTP(S) egress blocking | hostname-resolved IPs only; CDN over-permissioning risk | hostname (CONNECT) | hostname (CONNECT) + 11-layer scan | none |
|
||||
| DNS exfil — raw UDP | blocked (dnsmasq + iptables) | not blocked | not blocked (proxy layer only) | not blocked |
|
||||
| DNS subdomain entropy detection | no | no | yes, pre-resolution | no |
|
||||
| Blocks RFC 1918 by default | only if explicitly added to rules | yes | yes, + DNS rebinding | no |
|
||||
| Content-based DLP (credential patterns) | no | no | yes, 48 patterns + encoding normalization | no |
|
||||
| MCP / WebSocket scanning | no | no | yes, bidirectional | no |
|
||||
| Domain fronting bypass | possible | possible | possible (no TLS termination) | n/a |
|
||||
| macOS Docker Desktop (sidecar mode) | yes | yes | yes | yes |
|
||||
| macOS Docker Desktop (in-container sandbox) | yes | n/a | degraded (--best-effort) | yes |
|
||||
| NET_ADMIN / NET_RAW required | yes | no | no (sidecar) | no |
|
||||
| New runtime on host | no | Go image (docker pull) | Go image (docker pull) | no |
|
||||
| Config format | bash script + iptables rules | YAML ACL | YAML (generated by CLI) | n/a |
|
||||
| Implementation effort | low–medium (adapt existing script) | medium | medium (similar to smokescreen) | none |
|
||||
| Project maturity (as of 2026-05-08) | battle-tested (Linux kernel + Anthropic devcontainer) | battle-tested (Stripe production) | v2.3.0 (2026-04-25), repo ~3mo old, Apache 2.0 core + ELv2 enterprise | n/a |
|
||||
|
||||
---
|
||||
|
||||
## Recommendation
|
||||
|
||||
**Adopt pipelock as the v2 sidecar proxy in place of smokescreen.**
|
||||
|
||||
The reasoning:
|
||||
|
||||
1. **It replaces smokescreen with a strict superset.** Pipelock covers
|
||||
everything smokescreen covers (CONNECT-based hostname allowlisting,
|
||||
RFC 1918 blocking, Docker `--internal` network isolation) and adds DLP,
|
||||
subdomain-entropy DNS exfil detection, MCP scanning, and request
|
||||
redaction. The integration shape for claude-bottle is identical: a
|
||||
separate container on an internal Docker network, with the agent's
|
||||
`HTTPS_PROXY` pointing at it. The `cli.sh` changes are the same pattern.
|
||||
|
||||
2. **The DLP layer is the most direct answer to the content-tripwire gap.**
|
||||
The `secret-exfil-tripwire-encodings.md` note concluded that no
|
||||
off-the-shelf tool did exactly what was needed and sketched a ~200-line
|
||||
Python DIY. Pipelock's 48-pattern, encoding-aware DLP pipeline with
|
||||
built-in false-positive reduction is a production-ready answer to that
|
||||
gap, without writing custom code.
|
||||
|
||||
3. **The `--best-effort` problem does not apply to the sidecar topology.**
|
||||
The concern about Linux namespace availability inside Docker containers
|
||||
is real, but irrelevant when pipelock runs as a sidecar in its own
|
||||
container. The network isolation is enforced by Docker's `--internal`
|
||||
flag at the LinuxKit level, not by pipelock's in-process sandbox.
|
||||
|
||||
4. **The v1 iptables layer should be kept.** Pipelock covers HTTP/HTTPS
|
||||
only; raw TCP, UDP, and ICMP still need the iptables default-deny.
|
||||
The existing v1 plan (iptables + dnsmasq for DNS + `NET_ADMIN`) remains
|
||||
necessary as the IP-level floor. Pipelock sits above it, not instead of it.
|
||||
The dnsmasq layer also closes the raw-UDP-53 gap that pipelock's proxy
|
||||
layer cannot reach.
|
||||
|
||||
5. **The project is young.** v2.3.0 shipped on 2026-04-25, the repository
|
||||
was created 2026-02-08 — roughly three months of public history at the
|
||||
time of this note. The `luckyPipewrench` org does not appear to be a
|
||||
well-known security vendor with a published track record. This is a
|
||||
legitimate reason for caution. Smokescreen has years of production use at
|
||||
Stripe. Pipelock should be evaluated against its code (Apache 2.0 core,
|
||||
ELv2 enterprise subtree) before being trusted at the enforcement boundary.
|
||||
The appropriate response is to keep the iptables layer as the primary
|
||||
enforcement mechanism and treat pipelock as an additional detection and
|
||||
content-inspection layer, not the sole barrier.
|
||||
|
||||
**Revised tiering:**
|
||||
|
||||
- **v1 (unchanged):** in-container iptables + ipset + dnsmasq, as specified
|
||||
in `network-egress-guard.md`. This closes the IP, TCP, UDP, and raw DNS
|
||||
attack surface.
|
||||
- **v2 (updated):** pipelock sidecar instead of smokescreen sidecar. Adds
|
||||
hostname-based allowlisting, content DLP, subdomain entropy analysis, and
|
||||
MCP scanning on top of the v1 IP layer. Implementation effort is comparable
|
||||
to the smokescreen plan; capabilities are a strict superset for the
|
||||
claude-bottle threat model.
|
||||
- **DIY tripwire script (deferred):** the `secret-exfil-tripwire-encodings.md`
|
||||
DIY sketch can be deferred entirely if pipelock's DLP patterns cover the
|
||||
secrets in use. Custom patterns (for secrets not matching pipelock's 48
|
||||
built-ins) can be added via pipelock's signed rule bundle mechanism when
|
||||
and if needed.
|
||||
|
||||
---
|
||||
|
||||
## Open questions
|
||||
|
||||
1. **Who is `luckyPipewrench`?** The maintainer is not publicly identified
|
||||
as an individual or a named organization with a security track record.
|
||||
Before relying on pipelock at a detection boundary, a code review of the
|
||||
DLP matching and scanning pipeline is warranted. The repo is public under
|
||||
Apache 2.0 at <https://github.com/luckyPipewrench/pipelock>.
|
||||
|
||||
2. **Does the pipelock Docker image introduce supply-chain risk?**
|
||||
Pulling `ghcr.io/luckypipewrench/pipelock:latest` brings in a binary
|
||||
from an unvetted source. Pinning by digest (as the CLAUDE.md recommends
|
||||
for supply-chain hygiene) and building from source are both options.
|
||||
|
||||
3. **What is the actual DLP false-positive rate for the secrets claude-bottle
|
||||
agents use?** The 48 patterns cover well-known credential formats. Custom
|
||||
patterns can be added but the mechanism (signed rule bundles) is not
|
||||
documented in detail in public search results. Before v2, testing against
|
||||
real agent traffic is needed to establish the baseline FP rate.
|
||||
|
||||
4. **Does the dnsmasq layer remain necessary alongside pipelock sidecar?**
|
||||
Yes, for raw UDP/53 interception. Pipelock's subdomain entropy check fires
|
||||
on URLs it processes; it does not intercept DNS packets that bypass the
|
||||
proxy. The controlled resolver closes that gap. Re-evaluate if pipelock
|
||||
adds a DNS interception mode in a future release.
|
||||
|
||||
5. **Which features are Apache 2.0 vs ELv2-only?** The repo carries an
|
||||
`enterprise/` subtree under the Elastic License v2. Several features
|
||||
discussed in this note (mediator-signed action receipts, signed rule
|
||||
bundles, possibly some scanning passes) may live there. Before scoping
|
||||
integration work, audit which capabilities cited above are in the
|
||||
Apache 2.0 core and which require accepting ELv2 terms (which permit
|
||||
internal use and modification but prohibit offering pipelock as a
|
||||
managed service). For claude-bottle's local-Docker single-user use case,
|
||||
ELv2 is likely acceptable, but the determination should be explicit.
|
||||
|
||||
6. **Can pipelock's YAML config be generated per-agent from the manifest in
|
||||
a way that handles the `ssh` array correctly?** The `ssh` array in
|
||||
`claude-bottle.json` contains hostnames, ports, and `KnownHostKey` entries.
|
||||
These need to be mapped to pipelock's `api_allowlist` (for HTTP) and
|
||||
potentially to a separate bypass for the SSH socket. SSH is opaque to the
|
||||
HTTP proxy and does not go through `HTTPS_PROXY`; the allowlist entry is
|
||||
relevant only if the SSH host is also reached over HTTPS for key exchange
|
||||
or API calls.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `luckyPipewrench/pipelock` GitHub: <https://github.com/luckyPipewrench/pipelock>
|
||||
- Pipelock project site: <https://pipelab.org/pipelock/>
|
||||
- Pipelock v2.3.0 release post: <https://pipelab.org/blog/pipelock-v230-release/>
|
||||
- Pipelock v2.3 upgrade guide: <https://pipelab.org/learn/pipelock-v230-upgrade/>
|
||||
- Pipelock v2.2.0 release (newreleases.io): <https://newreleases.io/project/github/luckyPipewrench/pipelock/release/v2.2.0>
|
||||
- Help Net Security announcement (2026-05-04): <https://www.helpnetsecurity.com/2026/05/04/pipelock-open-source-ai-agent-firewall/>
|
||||
- Pipelock configuration reference: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/configuration.md>
|
||||
- Pipelock security assurance doc: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md>
|
||||
- Pipelock deployment recipes: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/guides/deployment-recipes.md>
|
||||
- Pipelock agent firewall explainer: <https://pipelab.org/agent-firewall/>
|
||||
- Cloudflare Sandboxes + Pipelock two-layer egress: <https://pipelab.org/learn/cloudflare-sandboxes-pipelock/>
|
||||
- Pipelock vs Docker MCP Gateway comparison: <https://pipelab.org/compare/pipelock-vs-docker-mcp-gateway/>
|
||||
- DEV Community — AI agents leak API keys through DNS queries: <https://dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d>
|
||||
- DeepWiki entry: <https://deepwiki.com/luckyPipewrench/pipelock>
|
||||
- randomcpu.com writeup: <https://randomcpu.com/pipelock-agent-firewall-for-ai-coding-tools/>
|
||||
- `stripe/smokescreen` (v2 baseline): <https://github.com/stripe/smokescreen>
|
||||
- Anthropic `init-firewall.sh` (v1 baseline): <https://github.com/anthropics/claude-code/blob/main/.devcontainer/init-firewall.sh>
|
||||
- Claude Code network config docs: <https://code.claude.com/docs/en/network-config>
|
||||
- Prior research — network egress guard: `docs/research/network-egress-guard.md`
|
||||
- Prior research — secret exfil tripwire encodings: `docs/research/secret-exfil-tripwire-encodings.md`
|
||||
|
||||
Research conducted 2026-05-08.
|
||||
Reference in New Issue
Block a user