Безопасность Docker-контейнеров: non-root, сканирование Trivy и др.
Практика4 минРедакция Innovialabs
Коротко
Запускайте контейнер от non-root пользователя на минимальном образе, делайте файловую систему read-only, сбрасывайте все capabilities, не монтируйте Docker-сокет и останавливайте CI, если Trivy нашёл исправимые HIGH и CRITICAL уязвимости.
Коротко
Контейнер сам по себе не является надёжной границей безопасности. Он использует ядро хоста, а типичный образ запускается от root со стандартным набором capabilities. Большая часть защиты сводится к пяти привычкам:
- процесс работает от non-root пользователя;
- образ собирается на минимальной базе;
- корневая файловая система только для чтения;
- ненужные Linux capabilities сброшены (обычно все);
- Docker-сокет никогда не монтируется в контейнер приложения.
Поверх этого каждый образ проверяется на известные уязвимости (CVE) в CI, чтобы уязвимая зависимость не доехала до продакшена.
Non-root пользователь и минимальный образ
Если злоумышленник проэксплуатирует приложение, он получит права его процесса. Root внутри контейнера ближе к root на хосте, чем принято думать, особенно в сочетании с ошибкой конфигурации.
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
Что здесь важно:
- USER переключает на непривилегированную учётную запись. В официальных образах Node есть пользователь
node; в других создайте его черезuseraddили укажите числовой UID. - Файлы приложения принадлежат root и доступны пользователю приложения только на чтение, поэтому процесс не может переписать собственный код.
- Слушайте порт выше 1024, тогда дополнительные привилегии для его открытия не нужны.
- Выбирайте slim, Alpine или distroless базы и multi-stage сборку: компиляторы, пакетные менеджеры и shell остаются на этапе сборки. Меньше пакетов — меньше CVE и меньше инструментов у атакующего.
Read-only файловая система и сброс capabilities
Большинству сервисов нужно писать только во временную папку и, возможно, в один том с данными. Всё остальное можно сделать read-only.
docker run -d \
--read-only \
--tmpfs /tmp \
--cap-drop ALL \
--security-opt no-new-privileges \
myapp:1.4.2
- --read-only не даёт подложить бинарник или изменить конфиги.
- --tmpfs /tmp даёт приложению место для временных файлов в памяти.
- --cap-drop ALL убирает возможности ядра вроде смены владельца файлов или raw-доступа к сети. Возвращайте только то, что можете обосновать.
- no-new-privileges запрещает повышение прав через setuid-бинарники.
В Kubernetes то же самое задаётся в securityContext контейнера:
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
Никогда не монтируйте Docker-сокет
Монтирование /var/run/docker.sock даёт контейнеру полный контроль над Docker-демоном. А кто управляет демоном, тот может запустить привилегированный контейнер с файловой системой хоста — по сути это root на хосте. Относитесь к сокету как к паролю root.
Если инструменту действительно нужен Docker (CI-раннеры, сборщики логов), вынесите его на отдельный хост, используйте фильтрующий прокси с узким allowlist или переходите на сборку образов без демона. Та же логика касается --privileged и --pid=host: в контейнерах приложений их быть не должно.
Сканирование образов Trivy в CI
Trivy — open-source сканер, который сверяет системные пакеты и зависимости языков в образе с базами уязвимостей. Ещё он умеет проверять Dockerfile и манифесты Kubernetes на ошибки конфигурации.
# остановить пайплайн при исправимых HIGH/CRITICAL уязвимостях
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 myapp:1.4.2
# проверить Dockerfile и манифесты на ошибки конфигурации
trivy config .
Как сделать сканирование полезным, а не шумным:
- Порог по severity: блокируйте merge на HIGH и CRITICAL, остальное показывайте в отчёте.
- --ignore-unfixed пропускает CVE без патча — с ними всё равно ничего не сделать сейчас.
- Принятые риски храните в .trivyignore с комментарием и датой пересмотра для каждой записи.
- Пересканируйте регулярно, а не только при сборке: новые CVE появляются и для образов, которые не менялись.
- Фиксируйте версию или commit сторонних CI-actions: сканер тоже часть вашей цепочки поставки.
Частые ошибки
| Ошибка | Как лучше |
|---|---|
Теги latest в продакшене | Фиксировать версии или digest |
| Секреты, зашитые в слои образа | Передавать секреты при запуске или через build secrets |
| Один большой образ с инструментами сборки | Multi-stage сборка, slim runtime |
| Просканировали один раз и забыли | Плановые пересканы задеплоенных образов |
--privileged, «чтобы заработало» | Найти и добавить одну нужную capability |