◉CLAUDE LABJP
●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 38 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 38 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-07-25Advanced

Locking down Claude Code sandbox egress with strictAllowlist

Tightening automation egress with strictAllowlist in Claude Code v2.1.219, plus measured failure timings that tell a policy deny from DNS and real outages, and three defects that made my own recon scripts return nothing.

Claude Code272SandboxSecurity12Automation45Networking

✦ Premium Article

Late one night, one of my automated pipelines stopped partway through without saying why.

The logs showed something that looked like a connection timeout. I blamed the network, re-ran it, and sometimes it went through. Classic "flaky network" behavior. Except the network was fine. A single host I had forgotten to add to the allowlist was being quietly refused.

This happened right after I turned on sandbox.network.strictAllowlist, added in Claude Code v2.1.219 (2026-07-24). As an indie developer running this pipeline solo, I want to share the field notes from folding that setting into my automation. I will spend less time on how to enable it and more on what broke afterward and how I tracked it down.

The later half of this article covers something I only found after publishing the first version: the observation scripts I wrote to watch the lockdown were themselves broken. I ran them again with the kind of input they were built for, and they returned nothing at all.

A "deny" wears the mask of an outage

The idea behind strictAllowlist is simple. For commands run inside the sandbox, refuse any outbound connection to a host that is not on the allowlist. It is a sensible way to run automation with a safe default.

But running it taught me that the deny itself is less troublesome than how the deny appears.

A connection to a host missing from the allowlist looks, from the application's point of view, like an ordinary connection failure. Most tools interpret that as "the network is unhealthy" and surface a retry or timeout message. In other words, an intentional policy block shows up wearing the face of a random network outage. That was the first pitfall. To work around this trap, observe the facts before you guess at the cause.

So the first thing I did was not to write an allowlist. It was to observe, once and completely, which hosts the pipeline actually reaches out to.

Discover the hosts you really touch

The "it probably connects to this host" in your head is not reliable. Trace connect(2) and collect destinations as facts. The script below wraps any command and prints the hostnames it connects to.

#!/usr/bin/env bash
# egress-recon.sh — discover which hosts a command actually connects to
# usage: ./egress-recon.sh <command to run...>
#   e.g. ./egress-recon.sh bash deploy-pipeline.sh
# NOTE: this version has three defects described later. The corrected script is
#       in "The observation harness was the first thing that broke"
set -euo pipefail
 
TRACE="$(mktemp)"
trap 'rm -f "$TRACE"' EXIT
 
# follow only connect(2), recording destination addresses
# -f: follow child processes (git and npm spawn many)
strace -f -qq -e trace=connect -o "$TRACE" "$@" || true
 
# extract IPv4 destinations: sin_addr=inet_addr("x.x.x.x")
grep -oE 'inet_addr\("[0-9.]+"\)' "$TRACE" \
  | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' \
  | sort -u > "$TRACE.ip"
 
echo "# destination hosts (reverse-resolved, deduped)"
while read -r ip; do
  # skip local / link-local
  case "$ip" in 127.*|0.0.0.0|169.254.*) continue;; esac
  host="$(getent hosts "$ip" | awk '{print $2}')"
  printf '%-24s %s\n' "${host:-"(no PTR)"}" "$ip"
done < "$TRACE.ip" | sort -u

The key flag is -f. Both git push and npm install hand the real work to child processes. Watch only the parent and you miss the connections that matter.

Running this once in my environment, the destinations came to eleven hosts. I had expected four. Roughly 2.7x what I anticipated.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A runnable script that traces connect(2) to reveal every host your pipeline actually contacts
✦Measured failure timings (0.02ms, 1.22ms, exactly 3s) that separate a policy block from DNS and from a real outage
✦Three defects that made the recon script return an empty list on exactly the addresses it was written for, with the corrected scripts and their real output
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-09-05
My deny rule was watching a filename, not the routes that reach it
One line in deny let several other spellings of the same file walk right past. Here is the small matrix I build to list every route to a protected file, and how I re-check it on upgrade day.
⟐ Claude Code2026-09-01
Drop the quotes on a heredoc and the prices in your log quietly change
An unquoted heredoc runs the variables and backticks inside its body. Here are the four ways my log got rewritten, how the three quoting forms compare, a safe placeholder-and-sed pattern, and a small script for auditing what you already have.
⟐ Claude Code2026-08-22
Handing a long job to another session — and the completion marker for when the notification never arrives
How to use notify_when_idle to hear when another Claude Code session finishes, and a small completion marker that keeps you from waiting forever when the notification is dropped.