Безопасный запуск Docker: non-root, rootless, capabilities и seccomp
Автор: Казачкин Даниил Михайлович · Обновлено
Контейнерная упаковка не отменяет контроль полномочий. Разные механизмы ограничивают разные стороны выполнения: пользователя процесса, привилегии daemon, доступные операции ядра…
Контейнерная упаковка не отменяет контроль полномочий. Разные механизмы ограничивают разные стороны выполнения: пользователя процесса, привилегии daemon, доступные операции ядра и видимые файлы. Составим понятный минимальный профиль для учебного процесса и проверим его наблюдаемое поведение.
Четыре независимых вопроса
Non-root означает, что приложение работает с непривилегированным UID внутри контейнера. Rootless относится к запуску самого daemon и контейнеров без root на хосте. Capabilities делят некоторые привилегированные операции Linux на отдельные права, а seccomp фильтрует системные вызовы. Ни один из этих пунктов в одиночку не гарантирует отсутствие уязвимостей. Rootless и seccomp решают разные задачи.
Если приложение умеет работать без записи в корневую файловую систему и без специальных capabilities, это удобно выразить явно. Не включайте такой профиль вслепую для любой базы: сначала узнайте её необходимые каталоги, пользователя и процедуру инициализации.
Проверяемый ограниченный процесс
Следующий одноразовый запуск пишет только во временную память:
docker run --rm --user 10001:10001 --read-only --tmpfs /tmp:rw,nosuid,nodev,size=16m --cap-drop ALL --security-opt no-new-privileges:true alpine:3.22 sh -c 'id; printf "temporary\n" > /tmp/note; cat /tmp/note; if touch /forbidden; then exit 1; else echo "root filesystem is not writable"; fi'Ожидайте UID 10001, строку temporary и сообщение о невозможности записи в корень. Это подтверждает конкретные ограничения данного запуска, а не проверку всей безопасности образа. После выхода tmpfs исчезает; постоянные данные потребовали бы отдельного mount и продуманного владельца.
По умолчанию Docker использует свой профиль seccomp там, где механизм поддерживается. --security-opt seccomp=unconfined снимает этот слой, поэтому не добавляйте его как универсальный ответ на любую ошибку. Если легитимный syscall блокируется, найдите точную операцию и выберите минимальное обоснованное изменение.
Почему privileged слишком широк
--privileged меняет сразу несколько ограничений и доступов. Исчезновение ошибки после его добавления показывает лишь то, что вы ослабили ограничения; оно не выявляет минимально необходимое право. Сравните требуемую операцию с capabilities, устройствами и политикой безопасности, затем добавьте только нужное. Обзор границ Docker.
Mount всего хоста или Docker socket внутрь приложения даёт ему сильные возможности за пределами собственного rootfs. Параметр :ro у socket-файла не превращает Docker API в API только для чтения: запросы всё равно могут создавать ресурсы. Не используйте такую схему для недоверенного приложения, считая её защищённой read-only mount.
Образ и запуск проверяются отдельно
Маленький образ может содержать уязвимую библиотеку; большой не становится автоматически недопустимым. Поддерживайте обновления, узнавайте происхождение, фиксируйте проверенный digest и анализируйте состав. Секреты передавайте при запуске подходящим механизмом, не записывайте в Dockerfile, слои или build args.
Доступ к daemon разрешайте только доверенным субъектам. Для удалённого управления используйте поддерживаемый защищённый канал, например SSH context, или настроенный TLS с проверкой сторон. Защита daemon socket относится к управлению всем Engine, а не к одному HTTP-порту приложения.
Задание на минимальность
Сначала выполните ограниченный пример, затем попробуйте сохранить файл вне /tmp. Объясните, какое ограничение сработало и зачем временный каталог выделен отдельно. Для собственного сервиса составьте список путей записи и необходимых привилегий; каждое расширение профиля должно соответствовать проверенному требованию. Разбор исследования Confine дополнит практику вопросом о том, как автоматически строить syscall-политики и где заканчивается доказательность такой оценки.