Skip to content

Latest commit

 

History

History
128 lines (89 loc) · 10.7 KB

File metadata and controls

128 lines (89 loc) · 10.7 KB

Безопасное исполнение ненадёжного кода

Это check-list к моему докладу на конференции TechLead Conf X 2025.

Тема доклада: "Безопасное исполнение ненадёжного кода" (5 июня 2025).

1. Уровни изоляции

Выбираем между стабильностью получаемых результатов и скоростью запуска и взаимодействия с ненадёжным кодом.

  • Тот же процесс. Исполнение кода в адресном пространстве процесса приложения. Минимальная изоляция. Примеры: интерпретация скриптов, визуальные языки программирования, плагины. Возможные решения: самоизоляция, статические анализаторы, централизованный реестр плагинов.

  • Отдельный процесс. Исполнение кода в отдельном процессе, на машине приложения. Средняя степень изоляции, максимально быстрое взаимодействие. Запуск кода производить от имени пользователя с ограниченным набором прав на доступ к ресурсам ОС.

  • Отдельный узел (песочница). Исполнение кода в отдельном процессе, на отдельной машине. Максимальная степень изоляции, медленное взаимодействие, подверженное сетевым сбоям.

2. Использование песочницы

  • Пересоздание или переиспользование. Пересоздание, если требуется особое окружение для каждой исполняемой задачи или идентичность этого окружения перед каждым запуском. Вариант решения: при небольшом потоке задач пул песочницы можно создавать заранее. Выбираем между стабильностью получаемых результатов и скоростью запуска.

  • Последовательное или параллельное исполнение. Последовательное, если важен порядок исполнения задач, нужен эксклюзивный доступ к некоторому ресурсу или задачи слишком ресурсоёмки. Важно: параллельное исполнение увеличивает время выполнения отдельных задач и может привести к перегрузке песочницы. Выбираем между стабильностью получаемых результатов и высокой пропускной способностью.

3. Управление песочницами

  • Оркестрация. Приложение совмещает две роли: оркестратор исполнения и оператор песочниц. Оркестратор координирует процесс исполнения кода; оператор отслеживает доступные песочницы и их состояние. Оператор может быть выделен в отдельный discovery-сервис, либо может быть реализован с использованием Kubernetes API. Основной плюс: прямолинейность алгоритма. Минусы: синхронное взаимодействие с песочницами, отслеживание доступности песочниц и балансировка нагрузки на них, неопределенность результата при отсутствии ответа, плохая масштабируемость.

  • Самоорганизация. Приложение публикует задачи на исполнение в очередь задач и принимает результаты исполнения из очереди результатов. Агент - мини-сервис, работающий в песочнице, получает задачи из очереди задач, координирует процесс исполнения кода, публикует результаты исполнения в очередь результатов. Основной минус: распределенная обработка. Плюсы: скорость и стабильность взаимодействия, контролируемая нагрузка на песочницы, хорошая масштабируемость.

4. Запуск процесса

  • Ограничение прав. Процесс должен запускаться от имени пользователя с максимально ограниченным набором прав на доступ к ресурсам ОС.

  • Ограничение ресурсов. Лимитирование ресурсов процесса средствами ОС (системный API), с использованием cgroup или специализированных rootless-инструментов, например, ProcessSandbox или Bubblewrap. Выбор зависит от требований к скорости запуска процесса. Важно: использование cgroup в Docker/Kubernetes требует эскалации привилегий контейнера, что небезопасно само по себе!

  • Анализ поведения и результатов. Почти всегда требуется программирование с учётом особенностей проекта.

Ограничение ресурсов с использованием инфраструктуры (например, Kubernetes Limits), cgroup или средств ОС задаёт жесткие границы, которые приводят к принудительному завершению процесса. Это осложняет анализ поведения и результатов. В этом случае желательно использовать подход с "программной мягкой границей" (watchdog). Пример реализации подхода и его описание - ProcessSandbox.

5. Операционная система

  • Заменить init-процесс на tini. Это позволит избежать проблему утечки PIDs в случае, когда много процессов завершаются принудительно. Утечка PIDs может незаметно остановить работоспособность системы.

  • Установить разумный лимит на количество PIDs. Лимит на PIDs задаётся для пользователя, а не для запускаемого процесса! По этой причине он должен быть разумно большим.

6. Многоконтейнерные поды

Приложение и песочница могут работать в разных контейнерах одного Kubernetes-пода. Это избавит от проблем сетевого взаимодействия между приложением и песочницей, т.к. контейнеры Kubernetes-пода будут работать на одном и том же узле. Недостатки такой схемы:

  • Плохая масштабируемость. Соотношение "приложение-песочница" 1:1.

  • Плохая утилизация ресурсов. Сможет ли приложение достаточно нагрузить песочницу?

  • Возможность перегрузки песочницы. Песочница только одна, справится ли она с потоком?

  • Риск нарушить работоспособность приложения. Например, через Shared Volumes.

7. Подготовка образов

  • Заменить init-процесс на tini. Нужно добавить пару строк в Dockerfile, см. инструкцию на странице проекта.

  • Создать непривилегированного пользователя. От его имени будет исполняться ненадёжный код. Ограничить ему всё, что можно.

8. Инфраструктура исполнения

  • Обеспечить быстрый (пере-)запуск песочниц. Если это Kubernetes, то настроить Image Pull Policy и никогда не использовать тег latest.

  • Установить разумный лимит на количество PIDs. Лимит на PIDs задаётся для пользователя, а не для запускаемого процесса! По этой причине он должен быть разумно большим. Важно: в Kubernetes этот лимит разделяется между всеми контейнерами узла.

  • Для каждой песочницы устанавливаем лимиты на ресурсы. Как минимум, лимиты на использование CPU, память и I/O.

Общие выводы

  • Задача исполнения ненадёжного кода решается в комплексе: разработка + DevOps.

  • Вынуждены разменивать безопасность на скорость исполнения или наоборот.

  • Ограничиваем всё, что можно и нужно.

Дополнительные материалы по данной тематике (и не только) доступны в моём Telegram-канале "Архитектоника в ИТ" (@arch_and_dev).