| name | filesystem-isolation-chroot |
| description | Filesystem isolation via chroot and pivot_root. Building minimal rootfs, chroot jail setup, pivot_root for OCI containers, preventing chroot escapes, and read-only bind-mount overlays. Sources: zetamatta/go-chroot, opencontainers/runc. |
/filesystem-isolation-chroot
When to Use
- Prevent Agent from seeing or modifying files outside
/workspaces
- Building a minimal rootfs for a sandboxed subprocess
- Understanding pivot_root vs chroot security differences
- Constructing read-only overlay mounts over a workspace
Do NOT use for
- Replacing full namespace isolation (chroot alone is not a security boundary)
- Production container runtimes (use runc / bubblewrap)
Minimal rootfs for chroot
#!/usr/bin/env bash
ROOTFS="${1:-/tmp/sandbox-rootfs}"
mkdir -p "$ROOTFS"/{bin,lib,lib64,lib/x86_64-linux-gnu,proc,sys,dev,tmp,etc}
cp "$(which busybox)" "$ROOTFS/bin/sh"
copy_libs() {
local bin="$1"
ldd "$bin" 2>/dev/null | awk '{print $3}' | grep '^/' | while read -r lib; do
local dest="$ROOTFS${lib}"
mkdir -p "$(dirname "$dest")"
cp -n "$lib" "$dest" 2>/dev/null || true
done
}
copy_libs /bin/bash && cp /bin/bash "$ROOTFS/bin/bash"
echo "nobody:x:65534:65534:nobody:/:/bin/sh" > "$ROOTFS/etc/passwd"
echo "nobody:x:65534:" > "$ROOTFS/etc/group"
echo "[rootfs] built at $ROOTFS"
Chroot jail execution
enter_chroot() {
local rootfs="$1"; shift
local cmd=("$@")
mount -t proc proc "$rootfs/proc"
mount -t sysfs sysfs "$rootfs/sys"
mount -t tmpfs tmpfs "$rootfs/tmp"
chroot "$rootfs" /bin/sh -c "
exec su -s /bin/sh nobody -c '${cmd[*]}'
"
umount "$rootfs/proc" "$rootfs/sys" "$rootfs/tmp" 2>/dev/null || true
}
enter_chroot /tmp/sandbox-rootfs "ls -la /workspaces 2>&1 || echo 'no /workspaces — isolated'"
pivot_root (stronger than chroot)
do_pivot_root() {
local new_root="$1"
mount --bind "$new_root" "$new_root"
mkdir -p "$new_root/old-root"
cd "$new_root"
pivot_root . old-root
umount -l /old-root
rmdir /old-root 2>/dev/null || true
}
Read-only overlay with overlayfs
setup_overlay_workspace() {
local src="$1"
local work="/tmp/ovl-work"; local upper="/tmp/ovl-upper"; local merged="/tmp/sandbox-ws"
mkdir -p "$work" "$upper" "$merged"
mount -t overlay overlay \
-o "lowerdir=$src,upperdir=$upper,workdir=$work" \
"$merged"
echo "[overlay] workspace mounted at $merged (writes go to $upper)"
}
Chroot escape patterns to block
Anti-Fake-Pass Checklist
❌ chroot without dropping CAP_SYS_CHROOT → classic chroot escape possible
❌ No mount namespace → host /proc visible inside jail via bind mounts
❌ /proc mounted but not masked → information leak of host kernel state
❌ rootfs writable by sandboxed user → can replace binaries, escape
❌ pivot_root without --bind mount of new_root → EINVAL at runtime
❌ Overlay upper dir not on same filesystem type as workdir → EXDEV error
❌ tmpfs /tmp without size limit → sandbox can fill host disk