An expected EPERM after denying setgroups in a new user namespace can still leave inherited supplementary groups attached to the child. Query the remaining group count and abort before exec unless the credential state is actually clean. Add regression coverage for both an inherited-group failure and a clean no-groups launch.
NsJail
Linux process isolation tool using namespaces, resource limits, and seccomp-bpf syscall filters.
Features
- Multiple isolation modes: TCP listener (inetd-style), standalone single-run, continuous re-execution
- Namespace isolation: UTS, MOUNT, PID, IPC, NET, USER, CGROUPS, TIME
- Filesystem constraints:
chroot(),pivot_root(), read-only mounts, custom/procandtmpfs - Resource limits: CPU time, memory, file descriptors, process count
- Syscall filtering: Kafel seccomp-bpf policies
- Network isolation: Cloned/isolated Ethernet interfaces, MACVLAN support, userland networking (pasta)
- Cgroup integration: Memory, PID, CPU, net_cls control (v1 and v2)
- Configuration: Protobuf-based config files or command-line arguments
Installation
Build from source
# Install dependencies (Debian/Ubuntu)
sudo apt-get install autoconf bison flex gcc g++ git libprotobuf-dev libnl-route-3-dev libtool make pkg-config protobuf-compiler
git clone https://github.com/google/nsjail.git
cd nsjail
make
Docker
docker build -t nsjail .
docker run --privileged --rm -it nsjail nsjail --user 99999 --group 99999 --chroot / -- /bin/bash
Quick Start
Basic isolated shell
./nsjail -Mo --chroot / --user 99999 --group 99999 -- /bin/bash
Network service (inetd-style)
./nsjail -Ml --port 9000 --chroot /chroot --user 99999 --group 99999 -- /bin/sh -i
Re-running process (useful for fuzzing)
./nsjail -Mr --chroot / -- /bin/echo "test"
Usage
Execution Modes
| Flag | Mode | Description |
|---|---|---|
-Ml |
LISTEN |
TCP server, fork process per connection |
-Mo |
ONCE |
Execute once, exit |
-Me |
EXECVE |
Direct execution without supervisor |
-Mr |
RERUN |
Execute repeatedly (useful for fuzzing) |
Common Options
# Filesystem
-c, --chroot DIR Chroot directory (default: /)
-R, --bindmount_ro SRC Read-only bind mount (SRC or SRC:DST)
-B, --bindmount SRC Read-write bind mount (SRC or SRC:DST)
-T, --tmpfsmount DST Tmpfs mount at DST
-m, --mount SRC:DST:TYPE:OPTS Arbitrary mount
# User/Group
-u, --user UID User ID inside jail (default: current)
-g, --group GID Group ID inside jail (default: current)
-U, --uid_mapping I:O:C Custom UID mapping (requires newuidmap)
-G, --gid_mapping I:O:C Custom GID mapping (requires newgidmap)
# Namespaces
-N, --disable_clone_newnet Disable network namespace
--disable_clone_newuser Disable user namespace
--disable_clone_newpid Disable PID namespace
--enable_clone_newtime Enable time namespace (kernel >= 5.3)
# Resource Limits
-t, --time_limit SEC Wall-time limit in seconds (default: 600)
--rlimit_as MB Address space limit in MB
--rlimit_cpu SEC CPU time limit in seconds
--rlimit_nofile N Max open files
--oom_score_adj VALUE OOM score adjustment for the sandbox (-1000 to 1000)
# Security
-P, --seccomp_policy FILE Seccomp-bpf policy file (Kafel syntax)
--seccomp_string POLICY Inline seccomp policy
--cap CAP_NAME Retain capability (can specify multiple)
--keep_caps Retain all capabilities
--use_core_scheduling Enable core scheduling (SMT-level isolation, requires kernel >= 5.14)
# Networking
--iface_own IFACE Move interface into jail
--macvlan_iface IFACE Clone interface as MACVLAN
--use_pasta Enable userland networking (pasta)
# Configuration
-C, --config FILE Load protobuf config file
Configuration Files
NsJail uses Protocol Buffers for configuration. Schema: config.proto
Example configuration
name: "bash-jail"
mode: ONCE
hostname: "JAILED"
cwd: "/tmp"
time_limit: 100
max_cpus: 1
rlimit_as: 512
rlimit_cpu: 10
rlimit_nofile: 32
clone_newnet: true
clone_newuser: true
clone_newns: true
clone_newpid: true
clone_newipc: true
clone_newuts: true
uidmap {
inside_id: "0"
outside_id: ""
count: 1
}
gidmap {
inside_id: "0"
outside_id: ""
count: 1
}
mount {
src: "/"
dst: "/"
is_bind: true
rw: false
}
mount {
dst: "/proc"
fstype: "proc"
}
mount {
dst: "/tmp"
fstype: "tmpfs"
rw: true
}
seccomp_string: "ALLOW { read, write, exit, exit_group } DEFAULT KILL"
Load with:
./nsjail --config myconfig.cfg
Override command:
./nsjail --config myconfig.cfg -- /usr/bin/id
Example Configs
- bash-with-fake-geteuid.cfg: Bash with fake root UID
- firefox-with-net-wayland.cfg: Sandboxed Firefox with networking
- home-documents-with-xorg-no-net.cfg: Document viewer with X11, no network
Use Cases
CTF Challenge Hosting
Isolate networked services for security challenges:
./nsjail -Ml --port 8000 \
--chroot /srv/ctf \
--user 65534 --group 65534 \
--time_limit 60 \
--rlimit_as 128 \
--rlimit_cpu 10 \
-- /srv/ctf/challenge
Fuzzing
Continuously re-run potentially crashing programs:
./nsjail -Mr \
--chroot / \
--user 99999 \
--time_limit 10 \
--rlimit_as 512 \
--seccomp_string 'ALLOW { ... } DEFAULT KILL' \
-- /path/to/target @@
Desktop Application Sandboxing
Run untrusted GUI applications:
./nsjail --config configs/firefox-with-net-wayland.cfg
Minimal Environment Execution
Run program with minimal filesystem access:
./nsjail -Mo \
-R /lib/x86_64-linux-gnu \
-R /lib64 \
-R /usr/bin/find \
-R /dev/urandom \
-- /usr/bin/find /
Advanced Features
Userland Networking (pasta)
Uses pasta for userspace networking without root privileges:
./nsjail --user 1000 --group 1000 \
--use_pasta \
--chroot / \
-- /usr/bin/curl https://example.com
Or via config:
user_net {
enable: true
ip: "10.255.255.2"
gw: "10.255.255.1"
ip6: "fc00::2"
gw6: "fc00::1"
tcp_ports: "80,443"
enable_dns: true
}
MACVLAN Network Isolation
Clone physical interface (requires root):
sudo ./nsjail \
--macvlan_iface eth0 \
--macvlan_vs_ip 192.168.1.100 \
--macvlan_vs_nm 255.255.255.0 \
--macvlan_vs_gw 192.168.1.1 \
-- /bin/bash
Seccomp-bpf Filtering
Kafel policy syntax:
./nsjail --seccomp_string '
POLICY example {
ALLOW {
read, write, open, close,
mmap, munmap, brk,
exit_group
}
DEFAULT KILL
}
USE example DEFAULT KILL
' -- /bin/program
Cgroups Resource Control
./nsjail \
--cgroup_mem_max $((512*1024*1024)) \
--cgroup_pids_max 32 \
--cgroup_cpu_ms_per_sec 800 \
-- /bin/cpu_intensive_program
Troubleshooting
Permission Denied Errors
-
CLONE_NEWUSER required: Run with
--disable_clone_newuser(requires root) or ensure user namespaces are enabled:sysctl kernel.unprivileged_userns_clone # Should be 1 -
Mount errors: Check that
/procis not overmounted:cat /proc/mounts | grep /proc
Resource Not Available
Check available namespaces:
ls -la /proc/self/ns/
Disable unsupported namespaces (e.g., --disable_clone_newcgroup for kernel < 4.6).
Debugging
Enable verbose logging:
./nsjail -v --config myconfig.cfg
Documentation
- Configuration schema: config.proto
- Seccomp policies: Kafel documentation
- Example configs: configs/
Contact
- Mailing list: nsjail@googlegroups.com
- Issues: GitHub Issues
This is not an official Google product.