Fine-grained HTTP filtering for Claude Code
Coding agents such as Claude Code are becoming more powerful every day without commensurate security
and governance tooling. The result is a world where solo developers happily run claude --dangerously-skip-permissions
for hours unmoderated while many of the world's most important organizations have barely tried agentic developmentLearned from our experience at Coder . I've been working on a tool called httpjail
in an effort to make agents available to everyone.
The tool is focused on mitigating these classes of risks:
Risk | Example |
---|---|
Agents performing destructive actions | Deleting your database |
Agents leaking sensitive information | Exposing API keys or credentials |
Agents operating with more authority than desired | Pushing straight to main instead of opening a PR |
Agents may transgress accidentally (user misinterpretation) or intentionally (prompt injection).
There is a class of risks at the file-system interface too, but, I believe existing tooling (containers) is sufficient here. Existing network isolation tools rely on IP-based rules. In our case, they're imprecisecentralized, anycast load balancers power much of the internet and require constant maintenanceIPs change randomly and they're not a part of a service's standard "contract".
httpjail
httpjail
implements an HTTP(S) interceptor alongside process-level network isolation. Under default configuration, all DNS (udp:53) is permitted and
all other non-HTTP(S) traffic is blocked.
httpjail
rules are either JavaScript expressions or custom programs. This approach makes
them far more flexible than traditional firewalls and avoids the learning curve of a DSL.
Block all HTTP requests other than the LLM API traffic itself:
$ httpjail --js "r.host === 'api.anthropic.com'" -- claude "build something great"
Allow only GET
requests i.e. make the internet read-only:
$ httpjail --js "r.method === 'GET'" -- claude "build something great"
Only allow hosts in a whitelist.txt
file:
$ httpjail --sh "grep -qx \"$HTTPJAIL_HOST\" whitelist.txt" -- claude "research these APIs"
How it works
In a nutshell:
Strong| NS[Create namespace
+ nftables redirect
+ setuid $SUDO_USER] Start -->|macOS/--weak
Weak| Env[Set $HTTP_PROXY
env vars] end subgraph "2. Target Process" direction TB Target[Target Process
e.g., claude] Target --> Request[HTTP/HTTPS
Request] Request --> Route{Route
Request} end subgraph "3. Interception" direction TB Proxy[Proxy :8080/:8443] Proxy --> Rules{Evaluate
JS/Script Rules} end subgraph "4. Result" direction TB Internet[✓ Internet Access] Blocked[✗ 403 Blocked] Bypass[⚠️ BYPASSED!
weak mode only] end NS --> Target Env --> Target Route -->|Strong: forced
via nftables| Proxy Route -->|Weak: respects
$HTTP_PROXY| Proxy Route -->|Weak: ignores
$HTTP_PROXY| Bypass Rules -->|Allow| Internet Rules -->|Deny| Blocked style NS fill:#00d4ff,color:#0a0a0a style Env fill:#00d4ff,color:#0a0a0a style Proxy fill:#404040 style Blocked fill:#8b2635 style Internet fill:#2a7f62 style Bypass fill:#8b2635
macOS (Weak Mode)
macOS uses --weak/-w
mode by default (see #7).
In weak mode, we rely on process cooperation via the standard HTTP_PROXY
/HTTPS_PROXY
environment variables. This mode is less of a jail and more of a suggestion that the majority of
well-meaning applications happen to comply with.
Security
In the weak jail, it's trivial for the agent to escape the jail by funnelling requests through a
shim program that disregards the HTTP_PROXY
environment variable.
Even the strong jail is not perfect. There are potential escape hatches in the filesystem. For example, the agent could create a container via a Docker socket which would spawn outside the network namespace.
To combine filesystem and network isolation into one, httpjail
has a --docker-run
flag that works like this:
httpjail --js "r.host === 'api.github.com'" --docker-run -- \
--rm alpine:latest wget -qO- https://api.github.com
I believe there's still much value in this approach even with imperfect isolation. In my experience, models seldom try to escape restrictions intentionally placed by the user. And, if the jail is rendered ineffective by prompt injection, it wasn't doing its job in the first place.
Server Mode
For the strongest level of isolation, you can:
- Run
httpjail --server
on a standalone server. - Configure the network firewall to only permit 80/443 traffic to the proxy server.
- Optionally, you may redirect all traffic to the proxy server, otherwise you will need to take care in ensuring HTTP_PROXY is set in your environments and all of your web-faring applications respect it.
- Run processes as usual in the development environment.
Request] Check{Respects
$HTTP_PROXY?} end subgraph "Network Layer" FW[Network Firewall] Config{Config
Mode?} FW --> Config Config -->|Redirect All
80/443 Traffic| Force[Forced to Proxy] Config -->|Allow Only
Proxy IP| Check Check -->|Yes| Voluntary[To Proxy] Check -->|No| Drop[✗ Dropped] end subgraph "httpjail --server" Decision{Evaluate
Rules} end subgraph Result direction TB API[✓ Allowed APIs] Blocked[✗ Blocked] end Request --> FW Force --> Decision Voluntary --> Decision Decision -->|Allow| API Decision -->|Deny| Blocked style Request fill:#404040 style Decision fill:#00d4ff,color:#0a0a0a style FW fill:#404040 style Blocked fill:#8b2635 style Drop fill:#8b2635 style API fill:#2a7f62 style Force fill:#2a7f62 style Voluntary fill:#4a7c59
Try it out
cargo install httpjail
Also, check out the GitHub repository for more details.