Skip to main content

Overview

This guide covers best practices for using sandboxec effectively and securely. Learn when to use sandboxec versus other isolation tools, how to determine required rules, and how to optimize performance.

When to Use sandboxec

Good Use Cases

sandboxec is designed for command-level sandboxing with low overhead. Use it when you need:

Third-party CLI Tools

Restrict filesystem access for untrusted or risky binaries
sandboxec --fs rx:/usr --fs rw:$PWD \
  -- third-party-tool

Generated Code

Execute AI-generated or dynamically created scripts in CI/CD
sandboxec --fs rx:/usr --fs rw:/tmp \
  -- python generated_script.py

Local Testing

Test install scripts before granting full access
sandboxec --fs rx:/usr --fs rw:$PWD \
  -- bash install.sh

Build Tools

Restrict build systems to project directories only
sandboxec --fs rx:/usr --fs rw:$PWD \
  --net c:443 -- npm install

From README: Purpose (lines 18-24)

Running untrusted code is often an all-or-nothing choice. Containers and VMs are great tools, but they can be too much for quick command-level isolation. Containers add image and runtime overhead. VMs add stronger isolation with higher setup and resource cost. sandboxec focuses on a narrower job: sandbox one command on the current host, with low overhead and explicit allow rules.

When NOT to Use sandboxec

From README lines 46-53, sandboxec is not suitable when you need:
Use Containers or VMs InsteadDo NOT use sandboxec for:
  • Stronger isolation boundaries than Landlock process sandboxing
  • Multi-tenant isolation between untrusted users/workloads
  • Resource isolation/quotas (CPU, memory, disk, I/O)
  • Custom root filesystems, full runtime images, or OS-level virtualization

Comparison Table

RequirementsandboxecContainersVMs
Command-level isolation⚠️⚠️
Low startup overhead⚠️
Filesystem restrictions
Network restrictions
Resource quotas
Multi-tenant isolation
Custom root filesystem
Kernel isolation
Quick Rule of Thumb
  • Single untrusted command? → sandboxec
  • Multiple processes needing isolation? → Container
  • Untrusted kernel code or multi-tenancy? → VM

Determining Required Rules

Start Minimal, Add as Needed

The safest approach is to start with minimal permissions and add rules when commands fail:
1

Start with minimal rules

sandboxec --fs rx:/usr/bin/command -- /usr/bin/command
2

Run and observe failures

Note “permission denied” errors in stderr
3

Add required paths

sandboxec --fs rx:/usr --fs rx:/lib \
  -- /usr/bin/command
4

Iterate until successful

Repeat until the command works

Common Dependencies

Most commands need these base paths:
fs:
  - rx:/usr        # System binaries and libraries
  - rx:/lib        # Shared libraries
  - rx:/usr/lib    # Additional libraries
  - rw:/tmp        # Temporary files
Why these paths?
  • /usr - Contains most system binaries (/usr/bin) and libraries
  • /lib and /usr/lib - Shared objects (.so files) required at runtime
  • /tmp - Many programs write temporary files

Using strace for Discovery

Use strace to see exactly what files a command accesses:
# Trace file system calls
strace -e trace=file /usr/bin/command 2>&1 | grep -E "(EACCES|ENOENT)"

# Look for permission errors
strace -e trace=openat,stat /usr/bin/command 2>&1 | tee trace.log
Convert strace output to sandboxec rules:
# If strace shows: openat("/usr/lib/x86_64-linux-gnu/libc.so.6")
# Add rule: --fs rx:/usr/lib

Testing with --ignore-if-missing

Use --ignore-if-missing during development to skip non-existent paths:
sandboxec --ignore-if-missing \
  --fs rx:/usr \
  --fs rx:/nonexistent \
  -- command
Do NOT use --ignore-if-missing in production. It can hide configuration errors.

Security Considerations

Security Model (from README lines 67-82)

sandboxec limits what a process can:
  • Read / Write / Execute on the filesystem
  • Bind / Connect on the network (TCP)
Restrictions apply immediately before launching the command and inherit to child processes.
What sandboxec Does NOT Protect AgainstFrom README Important note:
  • Kernel bugs or privileged local attackers
  • Resource exhaustion (CPU, memory, disk)
  • Every possible host interaction outside configured Landlock controls
Treat it as a practical containment layer, not a complete security boundary.

Principle of Least Privilege

Always grant the minimum permissions needed: Bad: Too permissive
sandboxec --fs rwx:/ -- untrusted-script
Good: Minimal permissions
sandboxec --fs rx:/usr --fs rw:/tmp/workspace -- untrusted-script

Read-Only When Possible

Use rx (read+execute) instead of rw (read+write) for system paths:
# System paths: read+execute only
sandboxec --fs rx:/usr --fs rx:/lib \
  # Working directory: read+write
  --fs rw:$PWD \
  -- command

Network Restrictions

Limit network access to required ports:
# HTTPS only
sandboxec --fs rx:/usr --net c:443 -- curl https://api.example.com

# Specific ports for local services
sandboxec --fs rx:/usr --net c:8080 --net c:5432 -- app
Default DenyIf you don’t specify --net rules, all network access is blocked. This is the safest default.

Scoped Restrictions

For even stricter isolation, use --restrict-scoped (requires kernel 6.7+ with Landlock ABI v6):
sandboxec --restrict-scoped \
  --fs rx:/usr --fs rw:/tmp \
  -- isolated-command
--restrict-scoped adds IPC restrictions to prevent certain types of process interaction.

Performance Considerations

Startup Overhead

sandboxec has minimal overhead for most commands:
  • Landlock setup: ~1-2ms
  • Rule parsing: negligible
  • No image build or container startup

The --unsafe-host-runtime Flag

From README lines 155-156 and 266-267:
Performance vs Security Trade-off--unsafe-host-runtime adds read_exec rights for:
  • All binaries in $PATH
  • Their shared library dependencies (discovered via ldd)
This can significantly increase startup latency, especially for short-lived commands.It also weakens least-privilege guarantees by granting broad filesystem access.
When to use --unsafe-host-runtime: ✅ Build tools with many dynamic dependencies:
sandboxec --unsafe-host-runtime \
  --fs rw:$PWD --net c:443 \
  -- npm install
❌ Simple commands with few dependencies:
# Don't use --unsafe-host-runtime
sandboxec --fs rx:/usr -- echo hello

Rule Optimization

Use Broader Paths When Safe

Instead of:
--fs rx:/usr/bin/python3 \
--fs rx:/usr/lib/python3.11 \
--fs rx:/usr/lib/x86_64-linux-gnu
Use:
--fs rx:/usr
Broader paths are more maintainable and have similar performance. Only use narrow paths when security requires it.

Minimize Network Rules

Network rules are checked on every bind/connect call:
# Efficient: one rule for common port
--net c:443

# Less efficient: many rules
--net c:443 --net c:8080 --net c:8443 --net c:9000 ...

Best-Effort Mode

Use --best-effort for compatibility across kernel versions:
sandboxec --best-effort \
  --fs rx:/usr \
  --net c:443 \
  -- command
--best-effort allows sandboxec to run on older kernels by gracefully degrading unsupported features (like network rules on kernels < 6.7).

Testing Sandboxed Commands

Verify Restrictions Work

Test that restrictions are actually applied:
1

Test filesystem restrictions

# Should fail with permission denied
sandboxec --fs rx:/usr -- ls /root

# Should succeed
sandboxec --fs rx:/usr -- ls /usr
2

Test network restrictions

# Should fail (no network access)
sandboxec --fs rx:/usr -- curl https://example.com

# Should succeed
sandboxec --fs rx:/usr --net c:443 -- curl https://example.com
3

Test write restrictions

# Should fail (read-only)
sandboxec --fs rx:/tmp -- touch /tmp/test.txt

# Should succeed
sandboxec --fs rw:/tmp -- touch /tmp/test.txt

Integration Testing

Include sandboxec in your CI/CD pipeline:
# .github/workflows/test.yml
jobs:
  test-sandboxed:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install sandboxec
        run: curl -sSL https://get.sandbox.ec | sh
      
      - name: Run tests in sandbox
        run: |
          sandboxec --fs rx:/usr --fs rw:$PWD \
            -- npm test

Debugging Failed Commands

1

Run without sandboxec first

# Does the command work normally?
/usr/bin/command
2

Add sandboxec with verbose stderr

sandboxec --fs rx:/usr -- /usr/bin/command 2>&1 | tee error.log
3

Check for permission errors

grep -i "permission denied" error.log
grep -i "EACCES" error.log
4

Add missing paths incrementally

sandboxec --fs rx:/usr --fs rx:/lib -- /usr/bin/command

Configuration Management

Use Config Files for Complex Rules

For commands with many rules, use YAML config:
# project-sandbox.yaml
abi: 6
best-effort: true
ignore-if-missing: true
fs:
  - rx:/bin
  - rx:/usr
  - rx:/lib
  - rx:/usr/lib
  - rw:/tmp
  - rw:$PWD
net:
  - c:443
  - c:80
sandboxec --config project-sandbox.yaml -- command

Use Named Profiles for Common Patterns

Instead of duplicating rules, use named profiles:
# Reusable profile for all Python projects
sandboxec -C languages/python -- python script.py

Version Control Your Configs

Commit sandbox configs to your repository:
project/
├── .sandboxec.yaml
├── src/
└── tests/
# Use project config automatically
sandboxec -- make build

Common Patterns

Development Workflow

# Local development with broad access
sandboxec --fs rx:/usr --fs rw:$PWD --net c:443 \
  -- npm run dev

CI/CD Pipeline

# Strict isolation for untrusted code
sandboxec --fs rx:/usr --fs rw:/tmp/build \
  --net c:443 --ignore-if-missing \
  -- build-script

AI Agent Execution

# MCP mode with workspace access
sandboxec --mode mcp \
  --fs rx:/usr --fs rw:$HOME/workspace \
  --net c:443

Quick Testing

# Test install script before running it for real
sandboxec --fs rx:/usr --fs rw:/tmp/test \
  -- bash install.sh --dry-run

Exit Codes and Error Handling

From README lines 257-261:
Exit CodeMeaning
0Wrapped command succeeded
NWrapped command exited with code N
1sandboxec setup failed (parsing error, Landlock failure)
Handle errors appropriately:
#!/bin/bash
sandboxec --fs rx:/usr -- command
exit_code=$?

if [ $exit_code -eq 1 ]; then
    echo "sandboxec configuration error"
elif [ $exit_code -ne 0 ]; then
    echo "Command failed with code $exit_code"
fi

Next Steps

MCP Integration

Use sandboxec with AI agents via MCP protocol

Named Profiles

Use pre-configured profiles for common scenarios

Build docs developers (and LLMs) love