Политики безопасности для выполнения кода
Ограничения возможностей, изоляция сети, ограничения файловой системы и тайм-ауты.
«Политики безопасности для выполнения кода» — бесплатный урок AI Agents на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Почему одних политик недостаточно
Сказать LLM «не обращайся к сети» — это не средство обеспечения безопасности, а всего лишь подсказка. LLM может проигнорировать ее, быть взломана, или сгенерированный код может косвенно нарушить это правило.
Настоящие политики безопасности должны применяться на слое выполнения, а не только в подсказке.
Обзор ограничений возможностей
Ограничения возможностей определяют, что процесс в песочнице может делать на уровне OS. Возможности Linux разделяют привилегии суперпользователя на отдельные разрешения, которые можно отключать по одному.
Отказ от всех возможностей, кроме минимально необходимых, называется принципом наименьших привилегий.
import docker
client = docker.from_env()
# Drop all Linux capabilities
output = client.containers.run(
'python:3.12-slim',
'python -c "print(\"safe run\")"',
cap_drop=['ALL'],
network_disabled=True,
mem_limit='256m',
auto_remove=True
)
print(output.decode())Блокировка записи на диск за пределами /tmp
Используйте корневую файловую систему только для чтения и подключайте только /tmp в режиме записи. Это не позволяет коду изменять системные файлы или сохранять данные за пределами предназначенной для временных данных области.
import tempfile, docker
client = docker.from_env()
with tempfile.TemporaryDirectory() as tmpdir:
output = client.containers.run(
'python:3.12-slim',
'python -c "open(\"/tmp/ok.txt\",\"w\").write(\"ok\"); print(open(\"/tmp/ok.txt\").read())"',
read_only=True,
tmpfs={'/tmp': 'size=64m,mode=1777'},
network_disabled=True,
mem_limit='128m',
auto_remove=True
)
print(output.decode()) # 'ok' — /tmp write allowed
# Writing anywhere else raises ReadOnlyFileSystemПрофили seccomp: фильтрация системных вызовов
seccomp (режим безопасных вычислений) позволяет указать список разрешенных системных вызовов. Любой вызов, отсутствующий в списке, приводит к завершению процесса с SIGSYS.
Docker поставляется с профилем seccomp по умолчанию. Можно предоставить пользовательский профиль JSON, чтобы сделать ограничения более строгими.
import json, docker
client = docker.from_env()
# Minimal seccomp profile: only allow read, write, open, close, exit
seccomp_profile = json.dumps({
'defaultAction': 'SCMP_ACT_ERRNO',
'architectures': ['SCMP_ARCH_X86_64'],
'syscalls': [
{'names': ['read', 'write', 'open', 'openat', 'close',
'fstat', 'mmap', 'mprotect', 'munmap', 'brk',
'exit', 'exit_group', 'rt_sigaction',
'rt_sigprocmask', 'futex'],
'action': 'SCMP_ACT_ALLOW'}
]
})
output = client.containers.run(
'python:3.12-slim',
'python -c "print(1+1)"',
security_opt=[f'seccomp={seccomp_profile}'],
auto_remove=True
)
print(output.decode())Ограничения ресурсов: CPU, память, время
Три ограничения ресурсов, которые должна применять каждая песочница:
- Память:
mem_limit— предотвращает атаки OOM - CPU:
nano_cpusилиcpu_quota— предотвращает исчерпание ресурсов CPU - Время: ограничение времени выполнения — предотвращает бесконечные циклы
output = client.containers.run(
'python:3.12-slim',
'python -c "import time; time.sleep(100)"',
mem_limit='128m',
nano_cpus=500_000_000, # 0.5 CPU
network_disabled=True,
auto_remove=True,
timeout=10 # kill after 10 seconds
)
# Raises ReadTimeout after 10 secondsРазрешенный список импортов Python
Перед выполнением кода просканируйте AST в поисках операторов импорта. Отклоняйте любой импорт, отсутствующий в списке разрешений. Это дополнительный уровень защиты, а не замена средств управления на уровне OS, но он помогает заранее обнаруживать очевидные нарушения политик.
import ast
ALLOWED_IMPORTS = {'math', 'statistics', 'json', 'csv', 'datetime', 'collections', 're'}
def check_imports(code: str) -> list[str]:
tree = ast.parse(code)
blocked = []
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
if alias.name.split('.')[0] not in ALLOWED_IMPORTS:
blocked.append(alias.name)
elif isinstance(node, ast.ImportFrom):
if node.module and node.module.split('.')[0] not in ALLOWED_IMPORTS:
blocked.append(node.module)
return blocked
print(check_imports('import socket; print(1)')) # ['socket']
print(check_imports('import math; print(math.pi)')) # []Обнаружение опасных шаблонов кода
Помимо импортов, проверяйте наличие шаблонов, позволяющих обойти ограничения на импорт: __import__, exec(), eval(), compile(), open() с путями за пределами /tmp.
import ast
DANGEROUS_CALLS = {'exec', 'eval', 'compile', '__import__', 'breakpoint'}
def check_dangerous_calls(code: str) -> list[str]:
tree = ast.parse(code)
found = []
for node in ast.walk(tree):
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
if node.func.id in DANGEROUS_CALLS:
found.append(node.func.id)
elif isinstance(node.func, ast.Attribute):
if node.func.attr in DANGEROUS_CALLS:
found.append(node.func.attr)
return found
print(check_dangerous_calls('exec("import os")')) # ['exec']Ограничения размера вывода
Бесконечный цикл печати может создать гигабайты вывода. Ограничивайте стандартный вывод и стандартный поток ошибок разумным размером (например, 1 МБ), а при превышении обрезайте вывод или завершайте работу контейнера.
MAX_OUTPUT_BYTES = 1 * 1024 * 1024 # 1 MB
def run_with_output_limit(client, code: str) -> dict:
container = client.containers.run(
'python:3.12-slim',
f'python -c "{code}"',
mem_limit='256m',
network_disabled=True,
detach=True
)
try:
container.wait(timeout=30)
logs = container.logs(stdout=True, stderr=True)
if len(logs) > MAX_OUTPUT_BYTES:
return {'success': False, 'error': 'Output too large'}
return {'success': True, 'output': logs.decode(errors='replace')}
finally:
container.remove(force=True)
if __name__ == '__main__':
class FakeContainer:
def wait(self, timeout=30): pass
def logs(self, stdout=True, stderr=True): return b'Analysis complete: 42 rows processed.'
def remove(self, force=True): pass
class FakeContainers:
def run(self, *args, **kwargs): return FakeContainer()
class FakeClient:
containers = FakeContainers()
result = run_with_output_limit(FakeClient(), 'print(42)')
print('Success:', result['success'])
print('Output :', result['output'])
Флаг запрета новых привилегий
Параметр безопасности no-new-privileges не позволяет процессу получать дополнительные привилегии через двоичные файлы с setuid или sudo. Даже если код запускает оболочку, он не может повысить привилегии.
output = client.containers.run(
'python:3.12-slim',
'python -c "import os; print(os.getuid())"',
user='nobody', # run as non-root
security_opt=['no-new-privileges:true'], # no privilege escalation
cap_drop=['ALL'], # no capabilities
network_disabled=True,
mem_limit='128m',
auto_remove=True
)
print(output.decode()) # numeric UID of 'nobody'Объединение всех политик
Песочница промышленного уровня объединяет все уровни защиты: отключённые возможности ОС, профиль seccomp, файловую систему только для чтения, запрет новых привилегий, отключённую сеть, ограничения ресурсов и время ожидания. Многоуровневая защита гарантирует, что обход одного слоя будет остановлен другим.
import tempfile, json, docker
client = docker.from_env()
def maximum_security_run(code: str) -> dict:
with tempfile.TemporaryDirectory() as tmpdir:
try:
out = client.containers.run(
'python:3.12-slim',
f'python -c "{code}"',
user='nobody',
security_opt=['no-new-privileges:true'],
cap_drop=['ALL'],
network_disabled=True,
read_only=True,
tmpfs={'/tmp': 'size=32m,mode=1777'},
mem_limit='128m',
nano_cpus=500_000_000,
auto_remove=True,
timeout=15
)
return {'success': True, 'output': out.decode()}
except Exception as e:
return {'success': False, 'error': str(e)}Журналирование нарушений политик
При обнаружении нарушения политики (заблокированного импорта, опасного вызова или истечения времени ожидания) записывайте событие вместе с ID сеанса агента, хешем фрагмента кода и типом нарушения. Эти данные поступают на панель безопасности и помогают со временем настраивать политики.
import hashlib, logging, time
logging.basicConfig(level=logging.INFO)
security_logger = logging.getLogger('sandbox.security')
def policy_check_and_run(code: str, session_id: str) -> dict:
violations = check_imports(code) + check_dangerous_calls(code)
code_hash = hashlib.sha256(code.encode()).hexdigest()[:16]
if violations:
security_logger.warning(
'POLICY_VIOLATION session=%s hash=%s violations=%s',
session_id, code_hash, violations
)
return {'success': False, 'error': f'Blocked: {violations}'}
result = maximum_security_run(code)
security_logger.info(
'EXEC session=%s hash=%s success=%s',
session_id, code_hash, result['success']
)
return resultЧто делает настройка «defaultAction: SCMP_ACT_ERRNO» в профиле seccomp
Профили seccomp управляют тем, какие системные вызовы разрешено выполнять процессу. Поле defaultAction определяет, что произойдёт с системными вызовами, которые явно не перечислены в профиле.
Повторение политик безопасности
Эффективная безопасность выполнения кода требует нескольких уровней защиты: отключённых возможностей Linux, фильтрации системных вызовов seccomp, файловой системы только для чтения с временным пространством tmpfs, запрета новых привилегий, отключённой сети, ограничений памяти/CPU/времени и проверки импортов на уровне AST.
Ни одного слоя недостаточно — многоуровневая защита означает, что каждый слой компенсирует обходы других.
Часто задаваемые вопросы
Урок «Политики безопасности для выполнения кода» бесплатный?
Да — полный текст урока «Политики безопасности для выполнения кода» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Политики безопасности для выполнения кода»?
Ограничения возможностей, изоляция сети, ограничения файловой системы и тайм-ауты. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Политики безопасности для выполнения кода»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Песочницы агентов на основе Docker
- Изоляция в VM для защищённых агентов, работающих с кодом
- E2B и облачные сервисы песочниц
- Политики безопасности для выполнения кода