Lesson 6: How the Sandbox is Technically Made

This lesson provides a deep technical dive into OpenShell's sandbox architecture, exploring the multiple layers of Linux kernel security features that create a defense-in-depth isolation model for AI agent execution.

1. Container Technology Foundation

1.1 Runtime Options

OpenShell supports multiple container runtimes through configurable compute drivers:

Driver Use Case Isolation Level
Docker Local development, standard deployments Container-level
Podman Rootless containers, CI/CD pipelines Container-level
MicroVM High-security workloads Hardware-virtualized
Kubernetes Production orchestration Pod-level with securityContext

Source: NVIDIA OpenShell GitHub Repository

1.2 How OpenShell Differs from Standard Docker

While OpenShell uses Docker as a compute driver, it adds several security layers that standard Docker containers lack:

# Standard Docker run
docker run -it ubuntu bash

# OpenShell sandbox creation - adds multiple security layers automatically
openshell sandbox create -- claude

2. Linux Kernel Security Features

Defense in Depth Stack

graph TB subgraph "Defense in Depth Stack" A[Application Layer] --> B[Container Runtime] B --> C[Seccomp-BPF] C --> D[Landlock LSM] D --> E[Linux Namespaces] E --> F[Cgroups v2] F --> G[Linux Kernel] end subgraph "Enforcement Points" H[Network Gateway] -.-> A I[Policy Engine] -.-> H I -.-> D end style D fill:#ff9900,stroke:#333,stroke-width:2px style C fill:#ff6600,stroke:#333,stroke-width:2px style H fill:#0066ff,stroke:#333,stroke-width:2px

2.1 Linux Namespaces

Namespaces provide process isolation by virtualizing system resources. OpenShell uses several namespace types:

Namespace Isolates OpenShell Usage
pid Process IDs Agent processes cannot see host PIDs
net Network stack All egress forced through gateway proxy
mnt Mount points Isolated filesystem view
user User/group IDs sandbox user isolation from host
uts Hostname/domain Isolated hostname per sandbox
ipc IPC mechanisms Isolated shared memory

Source: OpenClaw Security Documentation

2.2 Control Groups (cgroups)

Cgroups enforce resource limits to prevent denial-of-service attacks:

# OpenShell resource limits
ulimit -u 512     # Maximum 512 processes (best-effort)
ulimit -n 65536   # Maximum 65536 file descriptors

These limits prevent a compromised agent from:

2.3 Seccomp-BPF (System Call Filtering)

Seccomp-BPF allows filtering system calls using Berkeley Packet Filter programs. OpenShell uses this to block dangerous syscalls:

// Simplified seccomp filter structure
struct seccomp_data {
    int   nr;                   // System call number
    __u32 arch;                 // Architecture (x86_64, arm64, etc.)
    __u64 instruction_pointer;  // Where the call originated
    __u64 args[6];              // Syscall arguments
};

// Filter return actions (in precedence order)
SECCOMP_RET_KILL_PROCESS  // Terminate immediately
SECCOMP_RET_KILL_THREAD   // Kill only calling thread
SECCOMP_RET_TRAP          // Send SIGSYS signal
SECCOMP_RET_ERRNO         // Return error, don't execute
SECCOMP_RET_LOG           // Log and allow
SECCOMP_RET_ALLOW         // Permit execution

OpenShell Seccomp Configuration:

Source: Linux seccomp(2) man page

2.4 Capabilities Dropping

Linux capabilities divide root privileges into distinct units. OpenShell aggressively drops capabilities using capsh:

# Initial capability drop (entrypoint)
cap_sys_admin      # Mount, namespace manipulation
cap_sys_ptrace     # Process tracing/debugging
cap_net_raw        # Raw socket access
cap_dac_override   # Bypass file permission checks
cap_sys_chroot     # chroot() syscall
cap_fsetid         # Set file SUID/SGID bits
cap_setfcap        # Set file capabilities
cap_mknod          # Create device nodes
cap_audit_write    # Write to audit log
cap_net_bind_service # Bind to ports < 1024

# Additional drop during setpriv step-down
cap_setuid         # Change UID
cap_setgid         # Change GID
cap_fowner         # Bypass file ownership checks
cap_chown          # Change file ownership
cap_kill           # Send signals to other processes

Setting NEMOCLAW_REQUIRE_CAP_DROP=1 makes these drops fail-closed rather than best-effort.

Source: OpenClaw Security Documentation

3. Landlock LSM (Critical Section)

3.1 What is Landlock?

Landlock is a stackable Linux Security Module (LSM) that enables unprivileged processes to create security sandboxes. Unlike traditional MAC systems (SELinux, AppArmor), Landlock allows applications to sandbox themselves without system administrator involvement.

Traditional vs Landlock Security Models

graph LR subgraph "Traditional Security Models" A[Application] --> B[SELinux/AppArmor] B --> C[Kernel] D[Admin Policy] --> B end subgraph "Landlock Model" E[Application] --> F[Landlock Ruleset] F --> G[Kernel LSM] E -.->|Self-restricts| F end style F fill:#00aa00,stroke:#333,stroke-width:2px

Key Properties:

Source: Linux Kernel Landlock Documentation

3.2 How Landlock Enforces Filesystem Restrictions

Landlock operates through three system calls:

// Step 1: Create a ruleset defining what access rights to handle
struct landlock_ruleset_attr ruleset_attr = {
    .handled_access_fs =
        LANDLOCK_ACCESS_FS_EXECUTE |
        LANDLOCK_ACCESS_FS_WRITE_FILE |
        LANDLOCK_ACCESS_FS_READ_FILE |
        LANDLOCK_ACCESS_FS_READ_DIR |
        LANDLOCK_ACCESS_FS_REMOVE_DIR |
        LANDLOCK_ACCESS_FS_REMOVE_FILE |
        LANDLOCK_ACCESS_FS_MAKE_CHAR |
        LANDLOCK_ACCESS_FS_MAKE_DIR |
        LANDLOCK_ACCESS_FS_MAKE_REG |
        LANDLOCK_ACCESS_FS_MAKE_SOCK |
        LANDLOCK_ACCESS_FS_MAKE_FIFO |
        LANDLOCK_ACCESS_FS_MAKE_BLOCK |
        LANDLOCK_ACCESS_FS_MAKE_SYM |
        LANDLOCK_ACCESS_FS_REFER |
        LANDLOCK_ACCESS_FS_TRUNCATE |
        LANDLOCK_ACCESS_FS_IOCTL_DEV,
};

int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
                                          sizeof(ruleset_attr), 0);

// Step 2: Add rules for specific paths
struct landlock_path_beneath_attr path_beneath = {
    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
                      LANDLOCK_ACCESS_FS_READ_DIR,
    .parent_fd = open("/usr", O_PATH | O_CLOEXEC),
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
                  &path_beneath, 0);

// Step 3: Enforce the ruleset (requires no_new_privs first)
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
landlock_restrict_self(ruleset_fd, 0);
close(ruleset_fd);

3.3 OpenShell's Landlock Configuration

OpenShell applies Landlock with a specific filesystem layout:

Path Access Purpose
/usr Read-only System binaries and libraries
/lib Read-only Shared libraries
/proc Read-only Process information
/dev/urandom Read-only Cryptographic randomness
/app Read-only Application code
/etc Read-only Configuration files
/var/log Read-only Log files (read for debugging)
/sandbox Writable Agent working directory
/tmp Writable Temporary files
/dev/null Writable Null device
/dev/pts Writable Pseudo-terminals
# OpenShell Landlock configuration (conceptual)
landlock:
  compatibility: best_effort  # Falls back gracefully on older kernels

  read_only:
    - /usr
    - /lib
    - /proc
    - /dev/urandom
    - /app
    - /etc
    - /var/log

  writable:
    - /sandbox
    - /tmp
    - /dev/null
    - /dev/pts

3.4 Why Kernel-Level Enforcement Matters

Landlock enforcement happens inside the kernel, which provides critical security guarantees:

  1. Bypass-proof - Application code cannot disable or circumvent restrictions
  2. Race-free - No TOCTOU (time-of-check-time-of-use) vulnerabilities
  3. Universal - Applies to all access methods (direct syscalls, library calls, etc.)
  4. Inherited - Child processes automatically sandboxed

Important: Landlock layout changes require sandbox re-creation; they cannot be hot-reloaded at runtime.

Kernel Requirements:

# Verify Landlock is active
dmesg | grep landlock
# Should show: "landlock: Up and running"

# Check ABI version
cat /sys/kernel/security/landlock/abi

Source: Landlock Official Documentation, Linux Kernel Documentation

4. Network Interception Architecture

4.1 Gateway Proxy Model

OpenShell routes all sandbox egress through a CONNECT proxy gateway. This is enforced at the network namespace level - the sandbox has no direct internet access.

Gateway Proxy Model

sequenceDiagram participant Agent as AI Agent participant Gateway as OpenShell Gateway participant Policy as Policy Engine participant Target as External API Agent->>Gateway: CONNECT api.github.com:443 Gateway->>Gateway: Read /proc/<pid>/exe Gateway->>Gateway: Walk process tree Gateway->>Gateway: Compute SHA256 of binary Gateway->>Policy: Check policy for binary + destination alt Policy Allows Policy-->>Gateway: ALLOW (with inspection level) Gateway->>Target: Forward request Target-->>Gateway: Response Gateway-->>Agent: Response else Policy Denies Policy-->>Gateway: DENY Gateway-->>Agent: HTTP 403 + error JSON Gateway->>Gateway: Log denial event else Binary Hash Mismatch Gateway-->>Agent: HTTP 403 (binary modified) end

4.2 Binary Identification and Verification

The gateway identifies calling binaries through kernel-trusted mechanisms:

  1. Read /proc/<pid>/exe - Kernel-provided executable path (cannot be spoofed)
  2. Walk process tree for ancestor binaries
  3. Compute SHA256 hash of each binary on first use
  4. Cache hash and verify on subsequent requests
  5. Block if binary hash changes (detects runtime replacement attacks)
# Example: Agent process calls curl which makes HTTP request
# Gateway sees:
#   - Direct binary: /usr/bin/curl
#   - Parent binary: /usr/bin/python3
#   - Grandparent: /app/agent.py

# Policy can specify rules for any ancestor in the chain

4.3 Protocol Inspection Levels

OpenShell supports multiple protocol inspection depths:

Protocol Inspection Level What Gets Checked
none (L4-only) Transport Host, port, binary only - TCP relayed without inspection
rest HTTP Auto-detects/terminates TLS, checks method and path
websocket WebSocket Matches upgrade requests, methods, paths
json-rpc RPC Parses JSON-RPC, matches method names
mcp MCP Protocol Matches tool names and parameter patterns

4.4 Implementation: eBPF vs iptables

OpenShell's network interception uses a gateway proxy model rather than kernel-level packet filtering:

The gateway proxy approach allows:

# Inside sandbox - all egress goes through gateway
sandbox$ curl -sS https://api.github.com/zen
# Request flow: sandbox -> network namespace -> gateway -> policy check -> internet

# Policy-denied request
sandbox$ curl -sS -X POST https://api.github.com/repos/octocat/hello/issues -d '{}'
{"error":"policy_denied","detail":"POST /repos/octocat/hello/issues not permitted by policy"}

4.5 SSRF Protection

OpenShell includes built-in SSRF (Server-Side Request Forgery) protection:

Source: OpenClaw Security Documentation

5. The Policy Engine Internals

5.1 Architecture Overview

Policy Engine Architecture

graph TB subgraph "Policy Engine Architecture" A[Policy YAML Files] --> B[Policy Parser] B --> C[Rule Compiler] C --> D[In-Memory Rule Store] E[Incoming Request] --> F[Request Classifier] F --> G{Binary Matcher} G --> H{Destination Matcher} H --> I{Protocol Inspector} I --> J{Method/Path Matcher} D --> G D --> H D --> I D --> J J --> K{Decision} K -->|Allow| L[Forward Request] K -->|Deny| M[Block + Log] K -->|Route| N[Inference Router] M --> O[Audit Log] L --> O N --> O end subgraph "Operator Interface" P[TUI Dashboard] --> Q[Approval Queue] Q --> D end style K fill:#ffcc00,stroke:#333,stroke-width:2px style D fill:#00ccff,stroke:#333,stroke-width:2px

5.2 Five Protection Layers

Layer Enforcement Point Runtime Changeable Scope
Network OpenShell gateway Yes (openshell policy set) Egress filtering
Filesystem Landlock LSM + mounts No (requires re-creation) Path access control
Process Container runtime No (requires re-creation) Syscalls, capabilities
Gateway Auth OpenShell gateway No (set at build time) Credential management
Inference OpenShell gateway Yes (openshell inference set) LLM routing

5.3 Policy Configuration Structure

# Example OpenShell policy.yaml
version: 1
name: github-readonly

network:
  endpoints:
    - host: api.github.com
      port: 443
      protocol: rest
      binaries:
        - /usr/bin/curl
        - /usr/bin/gh
        - "**/python*"      # Glob: matches any python binary
      methods:
        - GET               # Read-only operations
      paths:
        - /repos/**         # Any repository endpoint
        - /users/**         # User information
        - /zen              # API health check

    - host: "*.githubusercontent.com"
      port: 443
      protocol: none        # L4 only - for raw file downloads
      binaries:
        - "**"              # Any binary
      access: full          # No method/path restrictions

inference:
  routes:
    - pattern: "api.anthropic.com"
      provider: anthropic-managed
      model: claude-sonnet-4-20250514

filesystem:
  # Locked at creation - cannot be changed at runtime
  writable:
    - /sandbox
    - /tmp

process:
  # Locked at creation
  capabilities_drop:
    - cap_sys_admin
    - cap_sys_ptrace
    - cap_net_raw

5.4 Decision Flow

When a request arrives, the policy engine evaluates in order:

  1. Binary identification - Read /proc/<pid>/exe, verify hash
  2. Destination matching - Check host:port against allowlist
  3. Binary glob matching - Does the binary match any pattern?
  4. Protocol inspection - If protocol specified, parse and extract fields
  5. Method/path matching - For HTTP, check verb and URL path
  6. SSRF check - Always block internal addresses
  7. Final decision - Allow, Deny, or Route to inference

5.5 Logging and Auditing

All policy decisions are logged for security auditing:

# View sandbox logs including policy decisions
openshell logs my-sandbox --tail

# Example log output
[2024-01-15T10:23:45Z] ALLOW  binary=/usr/bin/curl dest=api.github.com:443 method=GET path=/repos/nvidia/openshell
[2024-01-15T10:23:47Z] DENY   binary=/usr/bin/curl dest=api.github.com:443 method=POST path=/repos/nvidia/openshell/issues reason=method_not_allowed
[2024-01-15T10:23:50Z] ROUTE  binary=/app/agent dest=api.anthropic.com:443 routed_to=anthropic-managed model=claude-sonnet-4-20250514

# Full diagnostic bundle
openshell debug --sandbox my-sandbox --output debug.tar.gz

5.6 Operator Approval Flow

When a request is blocked, operators can approve it through the TUI:

# Launch terminal UI
openshell term

# In TUI: blocked requests appear in approval queue
# Operator can:
#   - Approve for this session (persists until sandbox destroyed)
#   - Deny permanently
#   - Add to policy file for future sandboxes

Source: OpenClaw Sandbox Management Documentation, OpenShell GitHub

6. Build-Time Security Hardening

OpenShell removes potentially dangerous tools from the runtime image:

# Removed from runtime image (cannot be used even if sandbox is compromised)
gcc gcc-12 g++ g++-12 cpp cpp-12  # Compilers
make                               # Build tools
netcat-openbsd netcat-traditional ncat  # Network utilities

PATH is locked to prevent binary injection:

# Hardened PATH (set at entrypoint)
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

7. User Isolation Model

Component User Rationale
Agent processes sandbox Unprivileged user for agent code
Gateway (root-entrypoint) gateway Separate user prevents agent from accessing gateway memory
Gateway (managed topology) sandbox Uses same UID due to no_new_privs constraint

8. Summary: Complete Security Stack

Complete OpenShell Security Stack

graph TB subgraph "OpenShell Security Stack" A[AI Agent Code] subgraph "Application Layer" B[Policy Engine
Network rules, inference routing] C[Gateway Proxy
Binary verification, protocol inspection] end subgraph "Container Layer" D[Docker/Podman/K8s
Image isolation, resource limits] E[User Isolation
sandbox/gateway users] end subgraph "Kernel Layer" F[Landlock LSM
Filesystem restrictions] G[Seccomp-BPF
Syscall filtering] H[Namespaces
pid, net, mnt, user, uts, ipc] I[Cgroups
Resource limits] J[Capabilities
Privilege reduction] end K[Linux Kernel] A --> B B --> C C --> D D --> E E --> F F --> G G --> H H --> I I --> J J --> K end style F fill:#ff9900,stroke:#333,stroke-width:3px style G fill:#ff6600,stroke:#333,stroke-width:2px style B fill:#0066ff,stroke:#333,stroke-width:2px

Quiz: Test Your Understanding

Question 1

Why does OpenShell use Landlock LSM instead of relying solely on container mount restrictions?

Show Answer

Landlock provides kernel-level enforcement that is bypass-proof. Unlike container mounts which can potentially be circumvented through symbolic links, mount namespace manipulation, or container escape vulnerabilities, Landlock rules are enforced inside the kernel where they cannot be disabled by application code. Additionally, Landlock restrictions are inherited by child processes and cannot be removed once applied - only tightened.

Question 2

How does the OpenShell gateway identify which binary made an outbound network request?

Show Answer

The gateway reads /proc/<pid>/exe, which is a kernel-provided symlink to the executable that cannot be spoofed by the process. It also walks the process tree to identify ancestor binaries and computes SHA256 hashes of each binary on first use. If a binary's hash changes between requests (indicating runtime replacement), the request is blocked.

Question 3

What is the enforcement order for security mechanisms when a new sandbox process starts?

Show Answer

The enforcement order is: namespace entry (process enters isolated namespaces), privilege drop (capabilities dropped via capsh), Landlock (filesystem restrictions applied), then seccomp (syscall filtering enabled). This order ensures that by the time user code runs, all protections are in place and cannot be circumvented.

Question 4

Which OpenShell protection layers can be modified at runtime, and which require sandbox re-creation?

Show Answer

Runtime changeable: Network policies (via openshell policy set) and Inference routing (via openshell inference set).

Require re-creation: Filesystem restrictions (Landlock layout), Process constraints (capabilities, seccomp), and Gateway authentication (set at build time).

Question 5

Why does OpenShell use a gateway proxy model for network interception instead of eBPF or iptables?

Show Answer

The gateway proxy model enables deep protocol inspection at layers 4-7, including HTTP methods/paths, WebSocket messages, JSON-RPC methods, and MCP tool calls. iptables only operates at L3/L4 and cannot inspect application-layer protocols. eBPF would require elevated privileges inside the sandbox, which would weaken isolation. The user-space gateway can also terminate TLS for encrypted traffic inspection and inject credentials for managed inference endpoints.


References