Zum Inhalt springen

Sichere Ausführung: Docker-Sandbox

Wissen

Ein Agent, der Code ausführen, Dateien schreiben oder Shell-Befehle starten kann, ist mächtig -- und gefährlich. Ein falsch konfigurierter Agent kann Dateien löschen, auf sensible Daten zugreifen oder das Host-System destabilisieren. Die Lösung: Jede Tool-Ausführung passiert in einer isolierten Docker-Sandbox.

Warum Sandboxing Pflicht ist

Ohne Sandbox hat ein Agent dieselben Berechtigungen wie der Prozess, der ihn ausführt. Das bedeutet:

  • Dateisystem-Zugriff: Der Agent kann jede Datei lesen und schreiben, die der Prozess erreicht
  • Netzwerk-Zugriff: Der Agent kann beliebige HTTP-Requests senden -- auch an interne Services
  • Prozess-Kontrolle: Der Agent kann andere Prozesse starten oder beenden
  • Ressourcen-Verbrauch: Ein Endlos-Loop oder eine Speicher-Explosion betrifft das Host-System

In der Produktion ist das inakzeptabel. Docker-Container bieten eine kontrollierte Umgebung mit definierten Grenzen.

Container-Architektur für Agents

┌─────────────────────────────────────────────┐
│  Host-System                                │
│  ┌───────────────────────────────────────┐  │
│  │  Agent-Orchestrator                   │  │
│  │  (Planner + Reviewer laufen hier)     │  │
│  └───────────┬───────────────────────────┘  │
│              │ Docker API                    │
│  ┌───────────▼───────────────────────────┐  │
│  │  Sandbox-Container (kurzlebig)        │  │
│  │  - Executor-Tools laufen hier         │  │
│  │  - Kein Netzwerk (optional)           │  │
│  │  - Begrenzter Speicher (512MB)        │  │
│  │  - CPU-Limit (1 Core)                 │  │
│  │  - Timeout: 30 Sekunden              │  │
│  │  - Read-only Dateisystem (+ /tmp)     │  │
│  └───────────────────────────────────────┘  │
└─────────────────────────────────────────────┘

Verstehen

Dockerfile für die Agent-Sandbox

# Minimales Image für Agent-Tool-Ausführung
FROM python:3.12-slim

# Sicherheits-Basics: Kein Root-User
RUN groupadd -r sandbox && useradd -r -g sandbox sandbox

# Nur die nötigsten Pakete
RUN pip install --no-cache-dir \
    requests==2.32.0 \
    beautifulsoup4==4.12.3

# Arbeitsverzeichnis
WORKDIR /workspace

# Kein Root
USER sandbox

# Standard-Timeout: 30 Sekunden
ENV TIMEOUT=30

ENTRYPOINT ["python", "-c"]

Tool-Ausführung im Container

import docker
import json
from dataclasses import dataclass

@dataclass
class SandboxConfig:
    image: str = "agent-sandbox:latest"
    memory_limit: str = "512m"
    nano_cpus: int = 1_000_000_000  # 1 CPU core
    timeout: int = 30
    network_disabled: bool = True
    read_only: bool = True

class DockerSandbox:
    def __init__(self, config: SandboxConfig | None = None):
        self.config = config or SandboxConfig()
        self.client = docker.from_env()

    def execute(self, code: str) -> dict:
        """Führt Code sicher in einem Container aus."""
        try:
            container = self.client.containers.run(
                image=self.config.image,
                command=code,
                mem_limit=self.config.memory_limit,
                nano_cpus=self.config.nano_cpus,
                network_disabled=self.config.network_disabled,
                read_only=self.config.read_only,
                tmpfs={"/tmp": "size=64m"},
                detach=True,
                remove=False
            )

            # Auf Ergebnis warten mit Timeout
            result = container.wait(timeout=self.config.timeout)
            logs = container.logs().decode("utf-8")
            exit_code = result["StatusCode"]

            container.remove()

            return {
                "success": exit_code == 0,
                "output": logs,
                "exit_code": exit_code
            }

        except docker.errors.ContainerError as e:
            return {"success": False, "output": str(e), "exit_code": 1}
        except Exception as e:
            return {"success": False, "output": f"Sandbox-Fehler: {e}", "exit_code": -1}

# Nutzung im Multi-Agent System
sandbox = DockerSandbox()
result = sandbox.execute(
    "print('Hallo aus der Sandbox!'); import os; print(os.getcwd())"
)

TypeScript-Variante mit Dockerode

import Docker from "dockerode";

interface SandboxResult {
  success: boolean;
  output: string;
  exitCode: number;
}

class DockerSandbox {
  private docker: Docker;

  constructor(private image = "agent-sandbox:latest") {
    this.docker = new Docker();
  }

  async execute(code: string): Promise<SandboxResult> {
    const container = await this.docker.createContainer({
      Image: this.image,
      Cmd: [code],
      HostConfig: {
        Memory: 512 * 1024 * 1024,   // 512MB
        CpuCount: 1,
        NetworkMode: "none",
        ReadonlyRootfs: true,
        Tmpfs: { "/tmp": "size=64m" },
      },
    });

    await container.start();
    const result = await container.wait();
    const logs = await container.logs({ stdout: true, stderr: true });

    await container.remove();

    return {
      success: result.StatusCode === 0,
      output: logs.toString(),
      exitCode: result.StatusCode,
    };
  }
}

Resource Limits: Die wichtigsten Parameter

ParameterEmpfohlener WertWarum?
Memory512MBReicht für die meisten Tool-Aufgaben, verhindert Speicher-Explosionen
CPU1 CoreVerhindert, dass ein Container das Host-System auslastet
Timeout30 SekundenFängt Endlos-Loops ab -- die meisten Tools brauchen < 5s
NetworkDisabledVerhindert Datenexfiltration und ungewollte API-Calls
FilesystemRead-only + /tmpAgent kann temporäre Dateien schreiben, aber nichts Persistentes ändern
PID-Limit50Verhindert Fork-Bombs

Anwenden

Dein Agent muss als Tool eine externe API aufrufen (z.B. GitHub). Wie löst du das in einer Sandbox mit deaktiviertem Netzwerk?

Reflektieren

Sandboxing ist nicht paranoid, sondern professionell. Jeder produktive Agent, der Code ausführt oder mit externen Systemen interagiert, braucht definierte Grenzen. Docker-Container sind der pragmatischste Weg dorthin: Sie sind etabliert, gut dokumentiert und in jede CI/CD-Pipeline integrierbar. Der Aufwand für das Setup amortisiert sich beim ersten verhinderten Incident.