Контейнер изолирует процесс, но ядро у него общее с хостом. Поэтому безопасность контейнеров это набор слоёв: что внутри образа, с какими правами процесс запущен и что ему позволено делать с ядром.
Не запускать от root
По умолчанию процесс в контейнере работает от root (UID 0). Без user namespaces это тот же UID 0, что и на хосте, просто ограниченный capabilities и namespaces. При побеге из контейнера (уязвимость рантайма или ядра, неудачный mount) атакующий окажется root на хосте.
RUN useradd --system --uid 10001 app
USER 10001
Числовой UID лучше имени: Kubernetes с runAsNonRoot: true может проверить его, не заглядывая в /etc/passwd. Порт ниже 1024 без root не открыть: слушай 8080 и публикуй как нужно, или дай одну capability.
Capabilities
Права root в Linux разбиты на ~40 capabilities. Docker по умолчанию оставляет контейнеру урезанный набор (например CHOWN, NET_BIND_SERVICE, SETUID, KILL), а опасные вроде SYS_ADMIN убирает.
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
Правило: убрать всё и добавить только нужное.
--privileged и docker.sock
--privilegedвыдаёт все capabilities, доступ ко всем устройствам хоста и отключает часть защит (seccomp, AppArmor). Из такого контейнера выйти на хост тривиально. Использовать только когда без этого никак (некоторые системные агенты) и понимать цену.- Монтирование
/var/run/docker.sockв контейнер даёт ему полный контроль над Docker, а значит над хостом: можно запустить привилегированный контейнер с корнем хоста. Это фактически root на сервере. - Аналогично опасны
--pid=host,--network=host, bind mount/или/etc.
Файловая система только для чтения
docker run --read-only --tmpfs /tmp app
Атакующий не сможет дописать бинарник или изменить код. Каталоги, куда приложению действительно нужно писать, монтируются как tmpfs или тома.
Seccomp, AppArmor, SELinux, no-new-privileges
- seccomp: фильтр системных вызовов. Профиль Docker по умолчанию блокирует несколько десятков опасных вызовов.
- AppArmor/SELinux: мандатный контроль доступа к файлам и ресурсам.
--security-opt no-new-privileges: процесс и его потомки не смогут получить больше прав через SUID-бинарники.
Rootless и user namespaces
User namespaces сопоставляют root в контейнере обычному непривилегированному UID на хосте. Rootless Docker (и Podman по умолчанию) запускает сам демон без root. Даже при побеге атакующий получает права обычного пользователя.
Минимальный образ
Чем меньше в образе, тем меньше уязвимостей и инструментов для атакующего: distroless или slim вместо полного дистрибутива, multi-stage без компиляторов, без curl, wget, shell, если они не нужны. Обновлять базовые образы регулярно: патчи безопасности приходят в новых сборках того же тега.
Секреты
Секреты нельзя:
- класть в образ (
COPY .env,ENV TOKEN=...,ARG): они остаются в слоях и истории, а образ уходит в реестр; - коммитить в репозиторий с compose-файлом.
Правильно: при сборке RUN --mount=type=secret, в рантайме секреты оркестратора (Docker secrets, Kubernetes Secret, внешнее хранилище вроде Vault), файл с правами 600 вне репозитория. Переменные окружения удобны, но видны в docker inspect и /proc/<pid>/environ, поэтому файлы предпочтительнее.
Сканирование и цепочка поставки
- Сканеры уязвимостей образов: Trivy, Grype, встроенный Docker Scout. Запускаются в CI, сборка падает на критичных CVE с исправлением.
- Подпись образов (cosign, Sigstore) и проверка подписи при деплое.
- SBOM: список компонентов образа для аудита.
- Свой реестр и закрепление базовых образов по digest.
Ресурсы
Лимиты памяти, CPU и числа процессов (--memory, --cpus, --pids-limit) защищают хост от контейнера, который съест всё, будь то утечка или атака. fork-бомба без --pids-limit положит весь сервер.
Частые ошибки
- Процесс от root «потому что так работает».
docker.sockв контейнере CI или мониторинга без понимания последствий.--privilegedдля решения проблем с правами.- Секреты в
ENVили в слоях образа. - Образы годами без пересборки.
Что спросят на собесе
- Почему root в контейнере опасен, если есть изоляция.
- Что даёт
--privilegedи монтированиеdocker.sock. - Как уменьшить поверхность атаки образа.
- Где хранить секреты и почему не в образе.
- Какие механизмы ядра ограничивают контейнер: capabilities, seccomp, AppArmor, user namespaces.