DiamondLightSource / DiamondLightSource/claude-sandbox

Egress jail: pasta is never reaped — orphaned processes outlive their holder netns

Open
#12 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Shell
Stars
0
Forks
2
Avg merge
3h 11m
Merged PRs (30d)
26

Description

## Summary

`netns_launch` starts pasta and never stops it. Observed live on the dogfood box: a pasta process alive in the host netns whose holder PID no longer exists.

```
pasta PID 415189 (host netns, alive)
holder PID 172187 (gone)
```

pasta normally exits when the namespace it serves goes away, so this is a leak that only shows up when that shutdown path doesn't fire — but when it doesn't, the process is orphaned indefinitely.

## Where

In `claude-shadow`:

- `jail_fail()` kills the **holder** only:
```bash
jail_fail() {
echo "claude-sandbox: egress jail $1" >&2
if [ -n "${2:-}" ]; then kill "$2" 2>/dev/null || true; fi
exit 1
}
```
- `netns_launch()` waits on the holder, removes its temp files, and exits. pasta's PID is never captured, never killed, and there is no `trap` covering the launch.

So on every abnormal exit after the pasta attach — a `jail_fail` from the holder's routing steps, a signal to the shadow, or anything that kills the holder without tearing the netns down cleanly — pasta is left behind.

## Impact

Low severity, but not zero:

- Orphaned processes accumulate across sessions on long-lived devcontainers.
- Each one holds a `/dev/net/tun` fd and its own tap state.
- A stale pasta whose netns is gone is confusing during diagnosis: it looks like an active jail when there is none. (It cost time during the investigation in #11 — the log path `/tmp/claude-pasta.log` pointed at a mount namespace that no longer existed.)

## Related: staged resolv.conf temp file leaks on the fail path

Same function, same cause. `netns_launch` cleans up with:

```bash
rm -f "$CLAUDE_JAIL_READY" "${CLAUDE_SANDBOX_JAIL_RESOLV:-}"
```

That line is only reached on the normal path. Every `jail_fail` exit leaves the staged `/tmp/claude-jail-resolv.XXXXXX` behind. Since #11 made `jail_stage_dns` stage unconditionally, this now applies to **every** host rather than only stub-resolver ones, so the leak rate goes up.

## Suggested fix

Capture pasta's PID after attach and install a single `trap` in `netns_launch` that reaps pasta and removes both temp files on EXIT/INT/TERM, so the normal path, the `jail_fail` paths, and signal deaths all converge on one cleanup. Keep it fail-soft — cleanup must never turn a working launch into a failed one.

Worth checking whether `jail_fail` should also take the pasta PID, or whether the trap alone is sufficient (the trap is preferable: one place, no extra parameter threading through every call site).

Contributor guide

Open the contributing guide

Research direction

Start at the netns_launch and jail_fail entry points in claude-shadow, then trace how pasta is attached and how the holder exits. Verify that normal, failure, and signal paths converge on cleanup of the pasta process and both temporary files, without turning a successful launch into a failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
linux, shell
Domain
networking, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.