Files
opensandbox-group--opensandbox/oseps/0004-secure-container-runtime.md
wehub-resource-sync e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

792 lines
30 KiB
Markdown

---
title: Pluggable Secure Container Runtime Support
authors:
- "@hittyt"
creation-date: 2026-02-05
last-updated: 2026-02-09
status: implementing
---
# OSEP-0004: Pluggable Secure Container Runtime Support
<!-- toc -->
- [Summary](#summary)
- [Motivation](#motivation)
- [Goals](#goals)
- [Non-Goals](#non-goals)
- [Requirements](#requirements)
- [Proposal](#proposal)
- [Notes/Constraints/Caveats](#notesconstraintscaveats)
- [Risks and Mitigations](#risks-and-mitigations)
- [Design Details](#design-details)
- [API and SDK Impact](#api-and-sdk-impact)
- [Server Configuration](#server-configuration)
- [Infrastructure Prerequisites](#infrastructure-prerequisites)
- [Runtime Resolver](#runtime-resolver)
- [Startup Validation](#startup-validation)
- [Docker Mode Implementation](#docker-mode-implementation)
- [Kubernetes Mode Implementation](#kubernetes-mode-implementation)
- [BatchSandboxProvider](#batchsandboxprovider)
- [AgentSandboxProvider](#agentsandboxprovider)
- [Pooled Sandbox Consistency](#pooled-sandbox-consistency)
- [Test Plan](#test-plan)
- [Drawbacks](#drawbacks)
- [Alternatives](#alternatives)
- [Infrastructure Needed](#infrastructure-needed)
- [Upgrade & Migration Strategy](#upgrade--migration-strategy)
<!-- /toc -->
## Summary
This proposal introduces secure container runtime support for OpenSandbox, enabling sandboxes to run in secure container runtimes such as gVisor, Firecracker, and Kata Containers. This provides hardware-level isolation for executing untrusted AI-generated code, protecting the host system from potential malicious behavior.
The secure runtime is configured at the **server level**: administrators choose a single secure runtime in the server configuration, and all sandboxes on that server transparently use it. SDK users and API callers require no code changes — the isolation upgrade is entirely an infrastructure-level decision.
## Motivation
OpenSandbox is designed to execute untrusted code generated by AI models (such as Claude, GPT-4, Gemini). While standard container isolation (runc) provides process-level isolation, it may not be sufficient for scenarios where:
1. **Untrusted Code Execution**: AI-generated code could potentially contain malicious behavior, including container escape attempts
2. **Multi-tenant Environments**: Different users' sandboxes may require stronger isolation guarantees
3. **Compliance Requirements**: Some industries require hardware-level virtualization for security compliance
Secure container runtimes like gVisor, Firecracker, and Kata Containers provide additional isolation layers:
| Runtime | Isolation Mechanism | Use Case |
|---------|-------------------|----------|
| gVisor | User-space kernel (syscall interception) | General workloads, low overhead |
| Kata Containers (QEMU) | Full VM with QEMU hypervisor | Maximum isolation, compatibility |
| Kata Containers (Firecracker) | MicroVM with Firecracker hypervisor | High density, minimal footprint |
| Kata Containers (CLH) | Cloud Hypervisor | Balanced performance and isolation |
### Goals
1. **Server-Level Configuration**: Secure runtime is configured once at the server level; all sandboxes use the same runtime
2. **Transparent to SDK Users**: No SDK or API changes required — upgrading isolation is purely an infrastructure decision
3. **Dual-Mode Compatibility**: Work seamlessly in both Local Docker and Kubernetes deployment modes
4. **Graceful Fallback**: Default to standard runc when no secure runtime is configured
5. **Validation**: Verify runtime availability at server startup and before sandbox creation, with clear error messages
### Non-Goals
1. **Runtime Installation**: OpenSandbox will not install or configure secure container runtimes; this is the responsibility of infrastructure administrators
2. **Per-Request Runtime Selection**: SDK users cannot choose or override the secure runtime on a per-sandbox basis; this is an infrastructure-level decision managed by administrators
3. **Runtime-Specific Features**: Exposing all features of each secure runtime (e.g., gVisor platforms, Kata hypervisors) is out of scope for the initial implementation
4. **Performance Optimization**: Tuning secure runtimes for optimal performance is left to operators
5. **Multiple Runtimes on One Server**: A single server instance supports exactly one secure runtime; mixed runtimes require separate server deployments
## Requirements
| ID | Requirement | Priority |
|----|-------------|----------|
| R1 | Server configuration defines the secure runtime for all sandboxes | Must Have |
| R2 | Support gVisor, Kata (including Firecracker backend) as runtime types | Must Have |
| R3 | Validate runtime availability at server startup | Must Have |
| R4 | Work in both Docker and Kubernetes modes | Must Have |
| R5 | Default to runc when no secure runtime is configured | Must Have |
| R6 | Clear error messages when configured runtime is unavailable | Should Have |
| R7 | No SDK or API changes required for existing users | Should Have |
## Proposal
We propose adding a `[secure_runtime]` section to the server configuration file (`~/.sandbox.toml`). When configured, **all sandboxes** on that server transparently run in the specified secure runtime. No changes to the Sandbox Lifecycle API or SDKs are required.
```
Server Config Backend
┌──────────────────────┐ ┌─────────────────┐
│ [secure_runtime] │ │ Docker: │
│ type = "gvisor" │ ┌────→ │ --runtime= │
│ docker_runtime │ │ │ runsc │
│ = "runsc" │─────┤ ├─────────────────┤
│ k8s_runtime_class │ │ │ Kubernetes: │
│ = "gvisor" │ └────→ │ runtimeClass- │
│ │ │ Name: gvisor│
└──────────────────────┘ └─────────────────┘
│ Infrastructure admin configures once
│ SDK users require NO code changes
```
### Notes/Constraints/Caveats
1. **Infrastructure Dependency**: Secure runtimes must be pre-installed and configured on the host (Docker) or cluster (Kubernetes) before use
2. **Performance Overhead**: Secure runtimes add latency and resource overhead compared to runc:
| Runtime | Isolation Mechanism | Startup Overhead | Memory Overhead | Best For |
|---------|---------------------|------------------|-----------------|----------|
| **runc** (default) | Process-level cgroups | ~0ms | Minimal | Trusted workloads, local development |
| **gVisor** | User-space kernel (syscall interception) | ~10-50ms | ~50MB | General workloads with low overhead |
| **Kata (QEMU)** | Full VM with QEMU hypervisor | ~500ms | ~20-50MB | Maximum compatibility and isolation |
| **Kata (Firecracker)** | MicroVM with Firecracker hypervisor | ~125ms | ~5MB | High density, minimal footprint |
| **Kata (CLH)** | Cloud Hypervisor | ~200ms | ~10-20MB | Balanced performance and isolation |
Warm start performance (from pre-warmed Pool):
| Runtime | Cold Start | Warm Start (from Pool) | Memory per Sandbox |
|---------|-----------|------------------------|-------------------|
| runc | ~500ms | ~50ms | ~5MB |
| gVisor | ~550ms | ~100ms | ~50MB |
| Kata (QEMU) | ~1000ms | ~200ms | ~20-50MB |
| Kata (Firecracker) | ~625ms | ~125ms | ~5MB |
The actual hypervisor is determined by the `RuntimeClass` handler configured by the SRE administrator (e.g., `kata-qemu`, `kata-clh`, `kata-fc`).
> **Note**: Firecracker is not a standalone OCI runtime. In this OSEP, `secure_runtime="firecracker"` maps to Kata Containers with the Firecracker hypervisor backend (`kata-fc`). See [Server Configuration](#server-configuration) for details.
3. **Compatibility**: Not all container images work with all secure runtimes:
- gVisor: Some syscalls may not be implemented; check [gVisor compatibility](https://gvisor.dev/docs/user_guide/compatibility/)
- Kata (QEMU/CLH): Generally most compatible but highest overhead
- Kata + Firecracker (`kata-fc`): Limited device support; some workloads requiring specific kernel features may not work
4. **execd Injection**: The execd binary injection mechanism must work within secure runtime constraints
5. **Pooled Sandbox Consistency (Kubernetes)**: In Kubernetes mode with resource pools (Pool CRD), the Pool's `runtimeClassName` must match the server's `[secure_runtime]` configuration. Since both are managed by the same SRE administrator, this is an operational requirement validated at server startup.
### Risks and Mitigations
| Risk | Impact | Mitigation |
|------|--------|------------|
| Runtime unavailable at creation time | Sandbox creation fails | Pre-validation with clear error messages |
| Syscall compatibility issues | Application may not work | Document known limitations per runtime |
| Performance degradation | Slower sandbox creation | Allow users to choose based on security/performance tradeoff |
| Configuration complexity | Operational burden | Provide sensible defaults and clear documentation |
## Design Details
> **Note**: Code snippets in this section are illustrative and demonstrate the design intent. Actual implementation may differ in structure and details.
### API and SDK Impact
**No changes to the Sandbox Lifecycle API or SDKs are required.**
The `CreateSandboxRequest` schema remains unchanged. The secure runtime is applied transparently by the server based on its configuration. Existing SDK code works as-is:
```python
# This code works identically whether the server uses runc or gVisor.
# The SDK user does not need to know or care about the secure runtime.
sandbox = await Sandbox.create(
image="python:3.11",
entrypoint=["python", "-c", "print('hello')"],
)
```
This is a key advantage of server-level configuration: upgrading from runc to gVisor is a pure infrastructure change that requires zero application code modifications.
### Server Configuration
Extension to `~/.sandbox.toml`. A single `[secure_runtime]` section configures the secure runtime for **all sandboxes** on this server:
```toml
[runtime]
type = "docker" # or "kubernetes"
execd_image = "opensandbox/execd:v1.0.21"
# Secure container runtime configuration.
# When enabled, ALL sandboxes on this server use the specified runtime.
# Comment out or leave type empty to use standard runc.
[secure_runtime]
# Runtime type identifier. Supported values:
# "gvisor" - gVisor (runsc), user-space kernel isolation
# "kata" - Kata Containers (QEMU backend), VM-level isolation
# "firecracker" - Kata Containers with Firecracker backend (K8s only)
# "" - Standard runc (default, no secure runtime)
type = ""
# Docker mode: --runtime parameter name
# Ignored when runtime.type = "kubernetes"
docker_runtime = "runsc"
# Kubernetes mode: pod.spec.runtimeClassName value
# Ignored when runtime.type = "docker"
k8s_runtime_class = "gvisor"
```
**Configuration examples** (pick ONE per server, these are separate config files):
Example 1 — gVisor on Docker:
```toml
# ~/.sandbox.toml
[runtime]
type = "docker"
execd_image = "opensandbox/execd:v1.0.21"
[secure_runtime]
type = "gvisor"
docker_runtime = "runsc"
k8s_runtime_class = "gvisor"
```
Example 2 — Kata Containers (QEMU) on Kubernetes:
```toml
# ~/.sandbox.toml
[runtime]
type = "kubernetes"
execd_image = "opensandbox/execd:v1.0.21"
[secure_runtime]
type = "kata"
docker_runtime = "kata-runtime"
k8s_runtime_class = "kata-qemu"
```
Example 3 — Kata + Firecracker on Kubernetes:
> Firecracker is a VMM, not an OCI runtime. It cannot serve as a CRI implementation directly. This OSEP recommends using Firecracker via Kata Containers (`kata-fc` handler), which is the mature, production-ready approach. The alternative (`firecracker-containerd`) is less actively maintained and not recommended.
```toml
# ~/.sandbox.toml
[runtime]
type = "kubernetes"
execd_image = "opensandbox/execd:latest"
[secure_runtime]
type = "firecracker"
docker_runtime = "" # Not supported in Docker mode
k8s_runtime_class = "kata-fc"
```
### Infrastructure Prerequisites
OpenSandbox does not install secure runtimes. The following must be configured by infrastructure administrators.
#### Docker Mode - gVisor Setup
**Step 1: Install gVisor runsc**
For Docker mode, you only need to install the **runsc** OCI runtime:
```bash
# Ubuntu/Debian
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | \
sudo tee /etc/apt/sources.list.d/gvisor.list
sudo apt-get update && sudo apt-get install -y runsc
# Verify installation
runsc --version
```
> **Note**: For Docker mode, only `runsc` is required. The `containerd-shim-runsc-v1` is only needed for Kubernetes/containerd.
**Step 2: Configure Docker daemon**
Use the `runsc install` command to automatically configure Docker daemon:
```bash
sudo runsc install
```
Or manually edit `/etc/docker/daemon.json`:
```json
{
"runtimes": {
"runsc": {
"path": "/usr/bin/runsc",
"runtimeArgs": [
"--platform=systrap",
"--network=host"
]
}
}
}
```
```bash
sudo systemctl restart docker
```
**Step 3: Verify installation**
```bash
docker run --runtime=runsc hello-world
```
#### Docker Mode - Kata Containers Setup
##### System Requirements
Kata Containers requires hardware virtualization support. Verify your system meets the following requirements:
**Hardware Virtualization Support:**
```bash
# Check if CPU supports hardware virtualization (VT-x for Intel, AMD-V for AMD)
lscpu | grep Virtualization
# Expected output: Virtualization: VT-x (Intel) or AMD-V (AMD)
# Alternatively on Intel
grep -E --color=auto 'vmx|svm' /proc/cpuinfo
# Expected: vmx (Intel) or svm (AMD) flags present
```
**KVM Module:**
```bash
# Check if KVM module is loaded
lsmod | grep kvm
# Expected: kvm_intel (Intel) or kvm_amd (AMD)
# If not loaded, load KVM module
sudo modprobe kvm_intel # For Intel
# or
sudo modprobe kvm_amd # For AMD
```
**Kernel Requirements:**
- Linux kernel 5.10 or later recommended
- KVM enabled in kernel config
**Docker Requirements:**
- Docker 20.10 or later
- `/etc/docker/daemon.json` configured for Kata runtime
##### Installation
Download and install Kata Containers static binaries from GitHub releases:
```bash
# Find the latest release at https://github.com/kata-containers/kata-containers/releases
KATA_VERSION="3.27.0"
wget https://github.com/kata-containers/kata-containers/releases/download/${KATA_VERSION}/kata-static-${KATA_VERSION}-amd64.tar.zst
# Extract to root directory - Kata will be installed in /opt/kata
zstd -d kata-static-${KATA_VERSION}-amd64.tar.zst
tar -xvf kata-static-${KATA_VERSION}-amd64.tar -C /
# Create symbolic links for PATH access
sudo ln -sf /opt/kata/bin/kata-runtime /usr/local/bin/kata-runtime
sudo ln -sf /opt/kata/bin/containerd-shim-kata-v2 /usr/local/bin/containerd-shim-kata-v2
# Verify installation
kata-runtime --version
```
##### Configure Docker Daemon
Edit `/etc/docker/daemon.json` to register Kata as a runtime:
```json
{
"default-runtime": "runc",
"runtimes": {
"kata": {
"runtimeType": "io.containerd.kata.v2"
}
}
}
```
Restart Docker to apply changes:
```bash
sudo systemctl restart docker
# Verify Kata is available in Docker
docker info | grep -A5 Runtimes
# Expected output should include "io.containerd.runc.v2 kata"
```
#### Kubernetes Mode - RuntimeClass Setup
Cluster administrators must create RuntimeClass resources:
```yaml
# gVisor RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc # Matches containerd handler name
scheduling:
nodeSelector:
kubernetes.io/arch: amd64
---
# Kata Containers (QEMU backend) RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata-qemu
handler: kata-qemu
---
# Kata Containers (Firecracker backend) RuntimeClass
# This is what secure_runtime="firecracker" maps to
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata-fc
handler: kata-fc
```
containerd configuration (`/etc/containerd/config.toml`):
```toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc.options]
TypeUrl = "io.containerd.runsc.v1.options"
ConfigPath = "/etc/containerd/runsc.toml"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-qemu]
runtime_type = "io.containerd.kata-qemu.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-fc]
runtime_type = "io.containerd.kata-fc.v2"
```
Create the gVisor configuration file:
```bash
sudo tee /etc/containerd/runsc.toml > /dev/null <<'EOF'
[runsc]
platform = "ptrace"
EOF
```
Restart containerd:
```bash
sudo systemctl restart containerd
```
##### Kata Containers on Kubernetes
Follow the [official Kata Containers installation guide](https://github.com/kata-containers/kata-containers/blob/main/tools/packaging/kata-deploy/helm-chart/README.md).
Quick installation using Helm:
```bash
# Install kata-deploy which will set up Kata Containers via DaemonSet
helm install kata-deploy "oci://ghcr.io/kata-containers/kata-deploy-charts/kata-deploy" --version "3.27.0" --namespace kube-system --create-namespace
# Wait for kata-deploy pods to be ready
kubectl wait --for=condition=ready pod -l name=kata-deploy -n kube-system --timeout=300s
```
> **Note**: The `kata-deploy` DaemonSet will automatically configure containerd on all nodes. Manual containerd configuration is not required when using kata-deploy.
Verify installation:
```bash
# Check RuntimeClasses
kubectl get runtimeclass
# Expected output:
# NAME HANDLER AGE
# kata kata-qemu 10m
# kata-qemu kata-qemu 10m
# kata-clh kata-clh 10m
# kata-fc kata-fc 10m
# Test Kata with a simple pod
kubectl run test-kata --restart=Never --image=hello-world --runtime-class=kata-qemu
kubectl logs test-kata
kubectl delete pod test-kata
```
### Runtime Resolver
The server reads `[secure_runtime]` at startup and resolves it to the backend-specific identifier based on the deployment mode:
```python
class SecureRuntimeResolver:
"""Resolves secure runtime config to backend-specific parameters."""
def __init__(self, config: AppConfig):
self.secure_runtime = config.secure_runtime # may be None
self.runtime_mode = config.runtime.type # "docker" or "kubernetes"
def get_docker_runtime(self) -> Optional[str]:
"""Return Docker --runtime value, or None for runc."""
if not self.secure_runtime or not self.secure_runtime.type:
return None
if not self.secure_runtime.docker_runtime:
raise ConfigError(
f"Secure runtime '{self.secure_runtime.type}' is not supported "
f"in Docker mode (docker_runtime is empty)."
)
return self.secure_runtime.docker_runtime
def get_k8s_runtime_class(self) -> Optional[str]:
"""Return K8s runtimeClassName, or None for cluster default."""
if not self.secure_runtime or not self.secure_runtime.type:
return None
return self.secure_runtime.k8s_runtime_class
```
### Startup Validation
The server validates the configured secure runtime at startup, failing fast if the runtime is unavailable:
```python
def validate_secure_runtime_on_startup(config: AppConfig, docker_client=None, k8s_client=None):
"""Validate secure runtime availability at server startup."""
sr = config.secure_runtime
if not sr or not sr.type:
logger.info("No secure runtime configured; using standard runc.")
return
if config.runtime.type == "docker":
if not sr.docker_runtime:
raise ConfigError(
f"secure_runtime.type='{sr.type}' but docker_runtime is empty. "
f"This runtime is not supported in Docker mode."
)
info = docker_client.info()
available = info.get("Runtimes", {}).keys()
if sr.docker_runtime not in available:
raise ConfigError(
f"Docker runtime '{sr.docker_runtime}' is not available. "
f"Available runtimes: {list(available)}. "
f"Please install and configure it in /etc/docker/daemon.json."
)
else: # kubernetes
try:
k8s_client.read_runtime_class(sr.k8s_runtime_class)
except ApiException as e:
if e.status == 404:
raise ConfigError(
f"RuntimeClass '{sr.k8s_runtime_class}' does not exist. "
f"Please create it in the cluster."
)
raise
logger.info(f"Secure runtime '{sr.type}' validated successfully.")
```
### Docker Mode Implementation
Changes to `server/opensandbox_server/services/docker.py`. The runtime is read from server config, not from the request:
```python
class DockerSandboxService(SandboxService):
def __init__(self, config: Optional[AppConfig] = None):
# ... existing initialization ...
self.resolver = SecureRuntimeResolver(self.app_config)
# Runtime is resolved once at init; already validated at startup
self.docker_runtime = self.resolver.get_docker_runtime()
async def create_sandbox(self, request: CreateSandboxRequest) -> CreateSandboxResponse:
# ... existing code ...
container = self.docker_client.containers.run(
image=request.image.uri,
# ... other parameters ...
runtime=self.docker_runtime, # "runsc", "kata-runtime", or None
)
```
### Kubernetes Mode Implementation
Both Kubernetes workload providers inject `runtimeClassName` from server config. The `runtimeClassName` is resolved once at service initialization (already validated at startup).
#### BatchSandboxProvider
Changes to `server/opensandbox_server/services/k8s/batchsandbox_provider.py`:
- **CRD**: `sandbox.opensandbox.io/v1alpha1` BatchSandbox
- **Pod spec path**: `spec.template.spec`
```python
class BatchSandboxProvider:
def __init__(self, config: AppConfig, ...):
# ... existing initialization ...
self.resolver = SecureRuntimeResolver(config)
self.runtime_class = self.resolver.get_k8s_runtime_class()
def create_workload(self, request: CreateSandboxRequest, ...):
# ... existing code ...
if self.runtime_class:
runtime_manifest["spec"]["template"]["spec"]["runtimeClassName"] = self.runtime_class
# ... template merge ...
```
#### AgentSandboxProvider
Changes to `server/opensandbox_server/services/k8s/agent_sandbox_provider.py`:
- **CRD**: `agents.x-k8s.io/v1alpha1` Sandbox
- **Pod spec path**: `spec.podTemplate.spec`
```python
class AgentSandboxProvider:
def __init__(self, config: AppConfig, ...):
# ... existing initialization ...
self.resolver = SecureRuntimeResolver(config)
self.runtime_class = self.resolver.get_k8s_runtime_class()
def create_workload(self, request: CreateSandboxRequest, ...):
# ... existing code ...
pod_spec = self._build_pod_spec(request, ...)
if self.runtime_class:
pod_spec["runtimeClassName"] = self.runtime_class
runtime_manifest["spec"]["podTemplate"]["spec"] = pod_spec
# ... template merge ...
```
#### Provider Comparison
| Aspect | BatchSandboxProvider | AgentSandboxProvider |
|--------|---------------------|---------------------|
| CRD Kind | `BatchSandbox` | `Sandbox` |
| Pod Spec Path | `spec.template.spec` | `spec.podTemplate.spec` |
| Pool Support | Yes (`poolRef`) | No |
| Runtime Source | Server config | Server config |
#### Pooled Sandbox Consistency
In Kubernetes mode with resource pools (Pool CRD), the Pool's `runtimeClassName` must match the server's `[secure_runtime]` configuration. Since both are managed by the same SRE administrator, this is an operational requirement.
**Pool configuration by SRE administrator:**
```yaml
apiVersion: sandbox.opensandbox.io/v1alpha1
kind: Pool
metadata:
name: gvisor-pool
spec:
template:
spec:
runtimeClassName: "gvisor" # Must match server's secure_runtime.k8s_runtime_class
containers:
- name: sandbox-container
image: python:3.11
capacitySpec:
bufferMax: 10
bufferMin: 2
poolMax: 20
poolMin: 5
```
The server validates this consistency at startup. If the Pool's `runtimeClassName` does not match the server config, the server logs a warning and refuses to use that pool.
### Compatibility Matrix
| Secure Runtime | Local Docker | Kubernetes | Notes |
|---------------|--------------|------------|-------|
| gVisor (runsc) | Full support | Full support | Docker `--runtime=runsc`; K8s via RuntimeClass |
| Kata Containers | Full support | Full support | Docker `--runtime=kata-runtime`; K8s via RuntimeClass |
| Firecracker | Not supported | Via Kata (`kata-fc`) | Not a Docker OCI runtime; use Kata with Firecracker hypervisor backend in K8s |
| Custom runtimes | Via config | Via RuntimeClass | Requires pre-installation |
## Test Plan
### Unit Tests
| Test Case | Description |
|-----------|-------------|
| Config parsing | Verify `SecureRuntimeConfig` correctly parses TOML |
| Resolver (Docker) | Verify `get_docker_runtime()` returns correct value or None |
| Resolver (K8s) | Verify `get_k8s_runtime_class()` returns correct value or None |
| Empty type handling | Verify fallback to runc when `type = ""` |
| Firecracker in Docker | Verify error when `docker_runtime` is empty in Docker mode |
### Integration Tests
| Test Case | Description |
|-----------|-------------|
| Startup validation (Docker) | Server fails to start when configured runtime not in Docker daemon |
| Startup validation (K8s) | Server fails to start when RuntimeClass doesn't exist |
| Docker + gVisor | Create sandbox on Docker host with `[secure_runtime] type = "gvisor"` |
| Docker + Kata | Create sandbox on Docker host with `[secure_runtime] type = "kata"` |
| K8s + gVisor | Create sandbox in cluster with gVisor RuntimeClass |
| K8s + kata-fc | Create sandbox in cluster with kata-fc RuntimeClass |
| Pool consistency | Server warns when Pool runtimeClassName doesn't match config |
### E2E Tests
| Test Case | Description |
|-----------|-------------|
| SDK unaware of runtime | SDK creates sandbox without any runtime parameter; runs in gVisor |
| Runtime isolation verification | Verify syscall interception in gVisor sandbox |
| Fallback behavior | Verify standard runc when `[secure_runtime]` not configured |
| execd injection under gVisor | Verify execd binary injection works within gVisor runtime |
## Drawbacks
1. **Operational Complexity**: Administrators must install and configure secure runtimes
2. **Performance Overhead**: Secure runtimes add startup latency and memory overhead
3. **Compatibility Issues**: Some workloads may not work with certain runtimes
4. **Documentation Burden**: Requires comprehensive setup guides for each runtime
## Alternatives
### Alternative 1: Per-Request Runtime Selection
**Approach**: Add a `secureRuntime` field to `CreateSandboxRequest`, allowing SDK users to choose the runtime per sandbox (e.g., `secure_runtime="gvisor"`).
**Pros**:
- Maximum flexibility for users
- Different sandboxes can use different runtimes on the same server
- Supports mixed security levels (trusted vs untrusted workloads)
**Cons**:
- Secure runtime is fundamentally an infrastructure decision, not a per-request decision
- API callers could potentially downgrade security
- Adds complexity to SDK and API surface
- Most deployments only use one runtime; per-request selection is rarely needed
**Decision**: Rejected. Secure runtime selection is an infrastructure-level concern that should be managed by administrators, consistent with how Docker (`daemon.json`) and Kubernetes (`RuntimeClass`) handle runtime configuration. Per-request selection may be revisited as a future enhancement if demand arises.
### Alternative 2: Automatic Runtime Detection
**Approach**: Automatically detect and use the most secure available runtime.
**Pros**:
- Zero configuration
- Always uses best available isolation
**Cons**:
- Unpredictable behavior across environments
- May break workloads with runtime incompatibilities
- Performance impact without administrator consent
**Decision**: Rejected. Explicit administrator choice is preferred for security/performance tradeoffs.
## Infrastructure Needed
- **Testing Environments**:
- Docker host with gVisor (runsc) configured
- Docker host with Kata Containers (kata-runtime) configured
- Kubernetes cluster with gVisor RuntimeClass (`runsc`)
- Kubernetes cluster with Kata QEMU RuntimeClass (`kata-qemu`)
- Kubernetes cluster with Kata + Firecracker RuntimeClass (`kata-fc`)
- **CI/CD Updates**:
- Add integration tests for secure runtime validation
- Add E2E tests with gVisor-enabled environment
- **Documentation**:
- User guide: How to use secure runtimes
- Admin guide: How to set up gVisor/Kata/Firecracker
- API reference updates
## Upgrade & Migration Strategy
### Backward Compatibility
- **No API breaking changes**: `CreateSandboxRequest` schema is unchanged
- **No SDK changes**: Existing SDK code works as-is
- **Default behavior unchanged**: Without `[secure_runtime]` config, sandboxes use standard runc
- **Existing configurations work**: The new `[secure_runtime]` section is optional
### Migration Path
1. **Phase 1**: Install and configure secure runtime on infrastructure (Docker daemon or K8s RuntimeClass)
2. **Phase 2**: Add `[secure_runtime]` section to server configuration
3. **Phase 3**: Restart server — all sandboxes now use the secure runtime
4. No SDK or application code changes required at any phase
### Documentation Updates
- Add infrastructure setup guide for gVisor/Kata/Firecracker
- Add server configuration reference for `[secure_runtime]`
- Add troubleshooting guide for runtime compatibility issues