How CubeSandbox Uses Namespaces for Process Isolation

CubeSandbox achieves Linux process isolation by creating a dedicated global mount namespace for the cubelet daemon plus per-sandbox containerd namespaces that embed isolation metadata into every request context.

CubeSandbox (the cubelet component) implements a two-layer namespace strategy that isolates each sandboxed workload while maintaining coordination with the host system. This approach combines kernel-level Linux namespaces with containerd's contextual namespace system to provide complete process isolation without the overhead of virtual machines.


Global Mount Namespace for the Cubelet Process

When the cubelet binary starts, it immediately establishes a private view of the host's mount hierarchy through a helper process called newCubeMnt. This setup ensures the cubelet can operate safely while still receiving host mount events.

The newCubeMnt Bootstrap

In Cubelet/cmd/cubelet/main.go (lines 61-71), the cubelet spawns a child process with dedicated namespaces:

func newCubeMnt() error {
    // Create a child process that has its own PID+mount namespaces:
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
    }
    // Start the child, bind-mount the shared directory, make the root rslave
}

The helper process performs three critical operations:

  1. Creates a new mount namespace using CLONE_NEWNS to isolate the cubelet's filesystem view from the host
  2. Creates a new PID namespace using CLONE_NEWPID to prevent process ID leakage
  3. Re-shares the host's mount propagation tree by binding the mount-namespace directory to /run/cube-containers/shared with rslave propagation

This configuration allows the cubelet to maintain a private mount hierarchy while still receiving host mount events (such as new network storage mounts) through the shared propagation tree.


Per-Sandbox Namespaces via containerd Context

Every sandbox operation in CubeSandbox carries a namespace identifier embedded in the Go context.Context. This mechanism, borrowed from containerd/pkg/namespaces, ensures consistent isolation metadata throughout a sandbox's lifecycle.

Context Injection and Retrieval

CubeSandbox uses two key functions from the containerd namespaces package:

  • namespaces.WithNamespace(ctx, ns) — Injects a namespace into a context
  • namespaces.NamespaceRequired(ctx) — Retrieves the namespace from a context (returns error if missing)

In Cubelet/storage/local.go (lines 666-670), the Create function extracts and persists the namespace:

ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
    return ret.Err(errorcode.ErrorCode_InvalidParamFormat, err.Error())
}

// Store the namespace in the sandbox metadata
info := &StorageInfo{
    Namespace: ns,
    SandboxID: sandboxID,
}
if err := l.writeBackendFileInfo(ctx, sandboxID, info); err != nil {
    return err
}

The namespace identifier is stored in the StorageInfo struct's Namespace field and logged as part of the request's metadata for auditability.

Runtime Integration

When launching a sandbox's container runtime, the cubelet passes the same context to runc or other runtime helpers. The runtime then translates this contextual namespace into actual Linux namespace syscalls (unshare) for PID, UTS, IPC, and mount isolation.


Linux Namespace Types Used by CubeSandbox

CubeSandbox leverages multiple Linux namespace types to provide comprehensive isolation:

Namespace Implementation Purpose
PID CLONE_NEWPID in newCubeMnt and runtime unshare Guarantees each sandbox runs with its own process ID space; the parent cubelet cannot see inside sandbox processes
Mount CLONE_NEWNS in newCubeMnt and per-sandbox setup Provides private filesystem views; the cubelet's bootstrap ensures safe mount propagation
UTS Per-sandbox unshare in runtime Isolates hostname and domain name per sandbox
IPC Per-sandbox unshare in runtime Separates System V shared memory, semaphores, and message queues
Network Cubelet/network package Isolates network interfaces, routing tables, and firewall rules per sandbox

The network namespace implementation resides in the separate Cubelet/network package, which handles creation and teardown independently of the other namespaces.


Complete Lifecycle Example

This code demonstrates how CubeSandbox combines both namespace layers:

// 1. Attach namespace to request context (used throughout lifecycle)
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
    return ret.Err(errorcode.ErrorCode_InvalidParamFormat, err.Error())
}

// 2. Store in sandbox metadata
info := &StorageInfo{
    Namespace: ns,
    SandboxID: sandboxID,
}

// 3. Pass context to runtime; runc builds syscalls:
//    unshare(CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID)
if err := runc.Start(ctx, opts); err != nil {
    return err
}

The context propagation ensures that the same namespace identifier flows from the initial API request through storage allocation to container runtime execution.


Key Files and Implementation Details

Component File Path Purpose
Mount namespace bootstrap Cubelet/cmd/cubelet/main.go Contains newCubeMnt, Cloneflags setup, and needNewMnt helper
Namespace context handling Cubelet/storage/local.go Shows namespaces.NamespaceRequired and StorageInfo.Namespace persistence
Context propagation tests Cubelet/storage/local_test.go Uses namespaces.WithNamespace(context.Background(), namespaces.Default) for verification
Runtime integration Cubelet/services/cubebox/runc_container_op.go Passes namespaced context to runc helpers
Network isolation Cubelet/network/ Implements per-sandbox network namespace creation

According to the test file Cubelet/storage/local_test.go, unit tests verify correct namespace propagation using namespaces.WithNamespace(context.Background(), namespaces.Default), confirming that the context carries the expected identifier through the entire request chain.


Summary

CubeSandbox uses namespaces for process isolation through two complementary mechanisms:

  • Global mount namespace established at cubelet startup via newCubeMnt with CLONE_NEWNS | CLONE_NEWPID, providing a private filesystem view while maintaining host mount propagation
  • Per-sandbox containerd namespaces embedded in Go contexts via namespaces.WithNamespace, persisted in StorageInfo, and passed to the runtime for syscall-level isolation

This dual approach ensures that each sandbox operates in an isolated Linux namespace environment while the cubelet maintains secure coordination with the host system.


Frequently Asked Questions

What is the difference between the cubelet's global namespace and per-sandbox namespaces?

The global mount namespace (created by newCubeMnt at lines 61-71 of main.go) isolates the cubelet daemon itself from the host's mount hierarchy. Per-sandbox namespaces are logical identifiers stored in request contexts that the runtime translates into actual Linux namespaces (PID, UTS, IPC, mount) for each individual sandbox. The global namespace protects the cubelet; the per-sandbox namespaces protect each workload from others.

How does CubeSandbox ensure mount events from the host still propagate to sandboxes?

The newCubeMnt helper binds the mount-namespace directory to /run/cube-containers/shared with rslave propagation. This configuration allows mount events from the host's shared tree to propagate into the cubelet's private view, ensuring that new network storage mounts remain visible while the cubelet's own mount operations remain isolated.

What happens if a sandbox context is missing the namespace identifier?

The namespaces.NamespaceRequired(ctx) function returns an error if the namespace is absent from the context. In Cubelet/storage/local.go line 666, this error propagates to ret.Err(errorcode.ErrorCode_InvalidParamFormat, err.Error()), causing the request to fail with an invalid parameter code. This strict validation prevents unnamespaced sandboxes from being created.

Is network namespace handled differently from other namespaces?

Yes. While PID, UTS, IPC, and mount namespaces are managed through the runc runtime integration in Cubelet/services/cubebox/runc_container_op.go, the network namespace is implemented in the separate Cubelet/network package. This separation allows independent lifecycle management and custom CNI integration for network policies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →