Аудит безопасности Docker-инфраструктуры проводят на трех уровнях: Docker-хост и Docker Engine, образы, запущенные контейнеры. Начинайте с read-only инвентаризации, затем проверяйте доступ к Docker API и docker.sock, права процессов, privileged-режим, capabilities, монтирования, сеть, опубликованные порты и секреты.
Наивысший приоритет у конфигураций, через которые контейнер может получить контроль над хостом или учетными данными: открытый Docker API, проброс /var/run/docker.sock, Privileged=true, доступ на запись к системным каталогам, запуск от root без ограничений, network_mode: host и секреты в образах или переменных окружения. Каждую находку фиксируйте с доказательством, владельцем и сроком исправления. После изменения перечитайте фактическую конфигурацию через docker inspect.
Что проверять в Docker-инфраструктуре в первую очередь
Первичная проверка должна дать ответ на четыре вопроса: кто управляет Docker, какие артефакты разрешено запускать, какие права получает каждый контейнер и к каким ресурсам хоста или сети он подключен. Такой порядок помогает быстро выделить риски с большим радиусом поражения.
Сначала соберите исходное состояние без изменения среды. Не включайте значения паролей, токенов и ключей в терминальные логи, тикеты или отчет. В отчете достаточно указать имя переменной, место обнаружения и факт наличия секрета.
Три уровня аудита: хост, образы и контейнеры
| Уровень | Основной вопрос | Приоритетные проверки |
|---|---|---|
| Docker-хост | Кто может управлять демоном и хостом? | Docker API, docker.sock, группа docker, версии Engine, containerd и runc, firewall, SSH, daemon.json |
| Образы | Что попадает в production-артефакт? | Registry, digest, Dockerfile, базовый образ, CVE, SBOM, секреты в слоях |
| Контейнеры | Какие полномочия реально получил процесс? | Пользователь, privileged, capabilities, seccomp, AppArmor или SELinux, mounts, сеть, лимиты |
Безопасный образ не компенсирует открытый Docker API. Ограниченный Docker-хост не спасает приложение, если контейнеру передали docker.sock, root-доступ и запись в /etc. Проверяйте все три уровня в одном цикле аудита.
Как подготовить инвентаризацию и зафиксировать исходное состояние
Создайте перечень хостов, окружений, владельцев сервисов и способов деплоя. Зафиксируйте версию ОС, Docker Engine, containerd, runc, количество контейнеров, образов, сетей, volumes и опубликованных портов. Отдельно отметьте ручные контейнеры, которые не создаются через основной CI/CD-процесс.
docker version
docker info
docker ps -a --no-trunc
docker image ls --digests
docker network ls
docker volume ls
ss -lntup
Для содержимого контейнеров используйте структурированный вывод. Перед сохранением результатов исключите или замаскируйте Config.Env. Удобный формат проверки: дата, имя хоста, идентификатор контейнера, команда, параметр, фактическое значение, риск, владелец, срок и статус повторной проверки.
Аудит безопасности Docker-хоста и Docker Engine
Компрометация Docker Engine часто дает атакующему возможность запускать процессы, подключать файловые системы и управлять сетями. Поэтому нарушения на хосте обычно получают критический приоритет, особенно в production-среде с несколькими сервисами или общими данными.
Версии, обновления и состав Docker-компонентов
Зафиксируйте версии Docker Engine, containerd, runc и ядра ОС. Сверьте их с внутренней политикой обновлений, статусом поддержки используемого дистрибутива и актуальными advisory вашей команды. Не помечайте компонент безопасным только по факту успешного запуска.
docker version --format '{{json .}}'
containerd --version
runc --version
uname -r
cat /etc/os-release
Отметьте компоненты с неизвестной датой обновления, локальные сборки и пакеты из нестандартных репозиториев. После обновления Docker Engine повторите проверку работающих контейнеров: меняются значения по умолчанию, поведение runtime и доступные возможности.
Доступ к Docker socket и удаленному API
Членство в группе docker рассматривайте как административный доступ к хосту. Пользователь с доступом к сокету может создать контейнер с монтированием файловой системы хоста или запустить контейнер с расширенными правами. Ограничение sudo без контроля группы docker не решает проблему.
stat -c '%A %U %G %n' /var/run/docker.sock
getent group docker
ps -ef | grep '[d]ockerd'
ss -lntp | grep -E '2375|2376|dockerd'
Найдите TCP-слушатели Docker. Порт 2375 без TLS нельзя оставлять доступным в сети. Для удаленного управления используйте защищенный канал, взаимную аутентификацию и ограничения firewall. Проброс /var/run/docker.sock в обычный прикладной контейнер считайте критической находкой, пока владелец сервиса не докажет необходимость и не предложит изолированную альтернативу.
Защита хоста и конфигурация демона
Проверьте правила firewall, SSH-доступ, учетные записи администраторов, журналы и конфигурацию демона. Конфигурация обычно находится в /etc/docker/daemon.json, но фактические аргументы запуска могут добавляться через systemd override.
cat /etc/docker/daemon.json
systemctl cat docker
systemctl show docker --property=ExecStart
journalctl -u docker --since '7 days ago'
Отдельно проверяйте insecure-registries, нестандартные hosts, отключенную проверку TLS и временные исключения. Rootless-режим снижает последствия части сценариев, но не отменяет аудит образов, монтирований, сети и секретов. Для размещения изолированных production-узлов пригодна облачная инфраструктура с отдельными серверами и сегментацией сети, например Timeweb Cloud; конфигурацию Docker и права доступа все равно нужно проверять на каждом узле.
Проверка безопасности Docker-образов
Образ проверяют до публикации и повторно после публикации. Новая CVE может появиться в уже используемой зависимости, даже если сборка раньше прошла контроль. Храните результаты сканирования вместе с digest образа, датой и правилами исключений.
Источник, тег и неизменяемый идентификатор образа
Тег удобен для чтения, но его можно переназначить на другой образ. Digest однозначно связывает деплой с конкретным набором слоев. В критичных окружениях фиксируйте образ по digest и контролируйте, кто имеет право публиковать артефакты в registry.
docker image inspect registry.example.local/app:1.8.2 \
--format '{{index .RepoDigests 0}}'
Тег latest не используйте как единственный идентификатор в production. Укажите версию, digest, источник сборки и ответственного за публикацию. Если digest отсутствует в записи релиза, восстановить точный состав развернутого образа будет сложнее.
Dockerfile и минимизация поверхности атаки
Проверьте базовый образ, список пакетов, копирование файлов и пользователя запуска. В runtime-образ не должны попадать компиляторы, менеджеры пакетов, тестовые данные, отладочные утилиты и ключи сборки, если приложение без них работает.
FROM golang:1.24 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app
FROM gcr.io/distroless/static-debian12
COPY --from=build /out/app /app
USER 65532:65532
ENTRYPOINT ["/app"]
Проверьте наличие USER, многостадийной сборки и корректного .dockerignore. Файлы .env, приватные ключи, каталоги .git, дампы баз данных и локальные конфигурации не должны попадать в build context. Не передавайте секреты через ARG, если команда сборки может сохранить их в истории слоя.
Уязвимости и секреты в слоях
Сканируйте ОС-пакеты, языковые зависимости и содержимое образа. Trivy и Grype находят известные уязвимости, Syft формирует SBOM. Результат сканера требует ручной оценки: важны доступность уязвимого кода, роль сервиса, наличие компенсирующих ограничений и срок исправления.
trivy image --severity HIGH,CRITICAL --ignore-unfixed app@sha256:example
grype app@sha256:example
syft app@sha256:example -o cyclonedx-json
Удаление файла с паролем в следующей инструкции Dockerfile не удаляет его из предыдущего слоя. Проверяйте историю образа и сборочные контексты. При обнаружении секретного значения удалите его из образа и registry, отзовите или ротируйте секрет, затем пересоберите артефакт.
Практические команды для сканирования и настройки запуска контейнеров собраны в чек-листе безопасности Docker-контейнеров.
Проверка безопасности запущенных контейнеров
Compose-файл или шаблон деплоя не всегда отражает фактическое состояние. Override-файлы, переменные CI/CD, ручной запуск и изменения после развертывания могут выдать контейнеру другие права. Источник истины для runtime-проверки: docker inspect.
docker inspect <container> --format '{{json .HostConfig}}'
docker inspect <container> --format '{{json .Config}}'
Пользователь, root и Linux capabilities
Контейнер по умолчанию должен запускать прикладной процесс от непривилегированного пользователя. Проверьте поле Config.User, фактический UID процесса и права на подключенные volumes. Пустое значение User обычно означает запуск от root внутри контейнера.
docker inspect <container> --format 'User={{.Config.User}}'
docker exec <container> id
docker inspect <container> --format 'CapAdd={{json .HostConfig.CapAdd}} CapDrop={{json .HostConfig.CapDrop}} SecurityOpt={{json .HostConfig.SecurityOpt}}'
Удаляйте capabilities по принципу минимальных привилегий. Для большинства веб-приложений не нужны SYS_ADMIN, SYS_PTRACE, NET_ADMIN и DAC_READ_SEARCH. Включайте no-new-privileges, используйте seccomp и профиль AppArmor или SELinux, если стек и ОС их поддерживают.
Privileged-режим и доступ к устройствам
Флаг Privileged=true почти снимает стандартные ограничения контейнера. В production он допустим только для узкого технического сценария с документированным обоснованием, изолированным узлом и дополнительным контролем. Проверьте подключенные устройства и правила DeviceCgroupRules.
docker inspect <container> --format 'Privileged={{.HostConfig.Privileged}} Devices={{json .HostConfig.Devices}} DeviceRules={{json .HostConfig.DeviceCgroupRules}}'
Особого внимания требуют доступ к /dev, GPU-устройствам, блочным устройствам, сокетам runtime и каталогам Docker. Комбинация privileged-контейнера с docker.sock или доступом на запись к хостовой файловой системе получает критический уровень риска.
Read-only filesystem и лимиты ресурсов
Для сервисов без необходимости записывать в корневую файловую систему включайте read_only: true. Каталоги для временных файлов добавляйте отдельными writable mounts или tmpfs. Это усложняет закрепление вредоносного кода внутри контейнера.
docker inspect <container> --format 'Readonly={{.HostConfig.ReadonlyRootfs}} Memory={{.HostConfig.Memory}} NanoCPUs={{.HostConfig.NanoCPUs}} Pids={{.HostConfig.PidsLimit}} Restart={{.HostConfig.RestartPolicy.Name}}'
Проверьте memory limit, CPU limit, pids-limit, restart policy и healthcheck. Контейнер без ограничений может исчерпать память, CPU или PID хоста и остановить соседние сервисы. Лимиты выбирают по фактическому профилю нагрузки, затем проверяют под нагрузочным тестом.
Опасные монтирования, сеть и секреты в продакшене
Монтирования, сеть и способ передачи секретов формируют фактические границы доступа сервиса. Здесь часто встречаются настройки, добавленные для отладки и оставленные в production после завершения работ.
Bind mounts, volumes и доступ к файловой системе хоста
Проверьте все mounts, тип, источник, назначение и режим доступа. Bind mount дает контейнеру доступ к пути хоста, поэтому любой каталог оценивайте как отдельное разрешение. Наиболее опасны /, /etc, /var/run, /var/lib/docker, docker.sock, домашние каталоги администраторов, SSH-ключи, облачные credentials и резервные копии.
docker inspect <container> --format '{{range .Mounts}}{{println .Type .Source "->" .Destination "RW=" .RW}}{{end}}'
Предпочитайте named volumes для данных приложения и монтирования ro для конфигураций, когда запись не нужна. Проброс docker.sock в CI-агент, панель управления или мониторинг требует отдельного анализа. Ограниченный proxy, выделенный builder и отдельный управляющий узел снижают число процессов с прямым доступом к Docker API.
Сетевые режимы, опубликованные порты и межсервисный доступ
Проверьте network_mode: host, опубликованные порты, общие bridge-сети и доступ к административным endpoints. Контейнер в host network использует сетевой стек хоста, поэтому стандартная сетевая изоляция Docker для него не работает.
docker inspect <container> --format 'NetworkMode={{.HostConfig.NetworkMode}} Ports={{json .NetworkSettings.Ports}}'
docker network inspect <network>
ss -lntup
Сопоставьте каждый порт с потребителем: публичный HTTP(S), внутренний API, мониторинг, база данных или административный интерфейс. Публикация на 0.0.0.0 открывает сервис на всех сетевых интерфейсах хоста. Для внутренних компонентов используйте закрытую сеть Docker, firewall и явные правила межсервисного доступа.
Подробные варианты настройки bridge-сетей, volumes и cgroups разобраны в практическом гайде по сетям и безопасности Docker.
Переменные окружения, Compose-файлы и Docker secrets
Проверьте Env, Compose-файлы, .env, переменные CI/CD, конфигурацию приложения и логи. Секрет в environment variable может быть виден через inspect, диагностику процесса, дампы и ошибки приложения. Для production используйте выделенное хранилище секретов или Docker secrets там, где это подходит архитектуре.
docker inspect <container> --format '{{range .Config.Env}}{{println .}}{{end}}' | sed -E 's/(=.*)/=***MASKED***/'
find . -maxdepth 3 \( -name '.env' -o -name 'docker-compose*.yml' -o -name 'compose*.yaml' \)
Значения параметров должны быть заданы явно и согласованно. Например, при развертывании Authentik заменяйте демонстрационные UID, GID, REDIS_PASSWORD и AUTHENTIK_SECRET_KEY на собственные. Пароль Redis должен совпадать во всех связанных настройках, иначе сервисы не подключатся, а команда может временно ослабить защиту для диагностики.
Не вставляйте значения секретов в команды, сообщения, скриншоты и отчет. Если ключ попал в Git, образ, CI-лог или registry, считайте его скомпрометированным: выполните ротацию, удалите утечку из актуальной конфигурации и проверьте историю артефактов.
Как быстро найти нарушения с помощью команд и инструментов
Автоматизация ускоряет первичный скрининг, но не принимает решение о приемлемости исключения. Сканер может отметить capability как риск, хотя она нужна узкому сервису. Аудитор должен проверить назначение контейнера, компенсирующие меры и область доступа.
Read-only инвентаризация контейнеров и образов
Начните с выборки объектов и точечной проверки опасных параметров. Команды ниже ничего не меняют.
docker ps -aq | xargs -r docker inspect \
--format '{{.Name}} privileged={{.HostConfig.Privileged}} user={{.Config.User}} network={{.HostConfig.NetworkMode}} readonly={{.HostConfig.ReadonlyRootfs}}'
docker ps -aq | xargs -r docker inspect \
--format '{{.Name}} {{range .Mounts}}{{.Source}}:{{.Destination}}:rw={{.RW}} {{end}}'
docker ps -aq | xargs -r docker inspect \
--format '{{.Name}} ports={{json .NetworkSettings.Ports}} caps={{json .HostConfig.CapAdd}}'
Сохраните результаты с идентификатором хоста и датой. Не отправляйте сырой docker inspect во внешний сервис: в нем могут быть переменные окружения, пути к конфигурациям и сведения о внутренней топологии.
Автоматический аудит хоста и конфигураций
Docker Bench for Security помогает проверить типовые настройки Docker-хоста и сверить их с контрольными рекомендациями. Линтеры Dockerfile и Compose-файлов полезны до деплоя: они выявляют запуск от root, проблемные инструкции и небезопасные параметры, пока изменение дешевле исправить.
Инструментальный отчет не закрывает находку автоматически. Сверяйте результат с реальным контейнером через docker inspect, проверьте владельца исключения и срок его пересмотра. Для регулярного контроля действий Docker Engine настройте журналирование и auditd по инструкциям из руководства по мониторингу и аудиту Docker.
Сканирование образов, зависимостей и SBOM
Добавьте сканирование в CI/CD до публикации образа. Блокируйте релиз при найденных Critical CVE, если для них есть исправление и уязвимый компонент реально попадает в runtime. Исключения храните рядом с кодом или в системе учета: CVE, причина, владелец, дата пересмотра.
- Соберите образ с версией и digest.
- Сформируйте SBOM через Syft.
- Запустите Trivy или Grype по образу и SBOM.
- Оцените находки, назначьте исправления и исключения.
- После публикации повторяйте сканирование образов в registry по расписанию.
Периодическое пересканирование нужно потому, что база уязвимостей меняется без пересборки образа. Для production полезно сравнивать результаты с предыдущим запуском и отдельно отслеживать появление новых Critical и High находок.
Чек-лист аудита Docker и план исправлений
Чек-лист превращает разовую проверку в повторяемый процесс. Для каждого пункта храните статус: пройдено, нарушение, допустимое исключение, требуется уточнение. Неподтвержденное предположение не должно становиться основанием для закрытия риска.
Как классифицировать находки по критичности
| Уровень | Примеры | Действие |
|---|---|---|
| Критический | Открытый Docker API без TLS, docker.sock в прикладном контейнере, privileged с чувствительным mount, секрет в публичном образе | Ограничить доступ или остановить рискованный путь, ротировать секрет, провести повторную проверку |
| Высокий | Root без обоснования, запись в системный каталог, host network, административный порт на 0.0.0.0, отсутствуют memory и pids limits | Исправить в ближайшем релизе, назначить владельца и срок |
| Средний | Mutable tag без digest, устаревший базовый образ, отсутствие healthcheck, лишняя сеть | Включить в план технических работ и контролировать срок |
| Низкий | Неполная документация исключения, устаревшая запись инвентаризации | Исправить в регламентном цикле |
Критичность зависит от эксплуатируемости и ущерба. Root-процесс в одноразовом изолированном тестовом контейнере и root-процесс в публичном production-сервисе имеют разный риск. Контекст не отменяет необходимость документировать исключение.
Минимальный формат отчета
| Поле | Что записать |
|---|---|
| Объект | Хост, контейнер, образ, сеть или volume; ID и имя |
| Фактическое состояние | Параметр и значение без раскрытия секрета |
| Доказательство | Команда, фрагмент конфигурации, дата и хеш артефакта |
| Риск | Вероятный путь эксплуатации и последствия |
| Исправление | Конкретное изменение конфигурации или процесса |
| Ответственный и срок | Владелец сервиса, дата устранения, дата повторной проверки |
Вместо пароля указывайте маску и источник: например, REDIS_PASSWORD присутствует в Config.Env, значение скрыто. Такой отчет помогает подтвердить риск без создания дополнительной утечки.
Повторная проверка после исправлений
После изменения перечитайте объект и сравните фактические параметры с исходным состоянием. Проверяйте не только конфигурацию, но и работоспособность: приложение должно запускаться под новым UID, записывать данные только в разрешенные каталоги и подключаться к нужной сети.
docker compose up -d
docker inspect <container> --format 'User={{.Config.User}} Privileged={{.HostConfig.Privileged}} Readonly={{.HostConfig.ReadonlyRootfs}}'
docker logs <container> --tail 100
Повторите сканирование образа после пересборки. После сетевых изменений проверьте, что публичные endpoints доступны только там, где это требуется, а внутренние сервисы сохраняют нужные связи.
Периодичность и события для внепланового аудита
Проводите полный аудит по внутреннему регламенту и запускайте внеплановую проверку после обновления Docker Engine, миграции на другой хост, смены базового образа, изменения сетевой схемы, добавления privileged-доступа, передачи docker.sock или инцидента. Критичные проверки образов и Dockerfile включайте в CI/CD при каждом релизе.
Для общей программы контроля, включая Docker, Kubernetes и Nginx, используйте практический план аудита IT-инфраструктуры. История отчетов помогает увидеть повторяющиеся исключения и сервисы, где риски возвращаются после обновлений.
Типичные ошибки при аудите безопасности Docker
Проверять только образы и игнорировать хост
Сканирование CVE не обнаружит открытый Docker API, слабые права на docker.sock, небезопасные firewall-правила или устаревшую конфигурацию демона. Образ без известных уязвимостей может работать на хосте, где любой член группы docker получает фактический административный доступ.
Включайте в каждый аудит проверку ОС, Docker Engine, прав пользователей, сокета и TCP endpoints. Это обязательная часть, а не дополнительная опция.
Считать Compose-файл источником истины
Compose-файл может отличаться от работающей конфигурации из-за override-файлов, переменных окружения, профилей и ручных действий администратора. Контейнер, созданный командой docker run, вообще не попадет в репозиторий с Compose-конфигурацией.
Сравнивайте декларацию с docker inspect, списками контейнеров, сетей, mounts и опубликованных портов. Отдельно разберите объекты без понятного владельца или pipeline-источника.
Вывести секреты ради удобства диагностики
Лог с паролем, токеном или приватным ключом превращает аудит в новый канал утечки. Маскируйте переменные в скриптах и отчетах. Не просите коллег присылать секретные значения в чат.
При обнаружении секрета в образе, Git, логах или Compose-файле выполните ротацию. Простое удаление строки не возвращает контроль над значением, которое уже могло попасть в кэш, историю Git или registry.
Вопросы и ответы по аудиту Docker
Нужно ли всегда запускать контейнеры не от root?
По умолчанию запускайте прикладной процесс от непривилегированного пользователя. Проверьте права на volumes и временные каталоги, затем задайте USER в Dockerfile или параметр user в Compose. Исключение документируйте с причиной, владельцем и компенсирующими ограничениями: read-only filesystem, удаленные capabilities, seccomp, AppArmor или SELinux, отсутствие опасных mounts.
Насколько опасен проброс docker.sock?
Доступ к docker.sock дает доступ к Docker API. Во многих сценариях это позволяет создать контейнер с доступом к ресурсам хоста, читать подключенные данные или управлять другими контейнерами. Не передавайте сокет обычному приложению. Используйте ограниченный proxy, отдельный builder или выделенный управляющий узел.
Достаточно ли Docker Bench и сканера образов?
Нет. Docker Bench и сканеры образов полезны для стандартизации типовых проверок, поиска CVE и ошибок конфигурации. Они не определяют бизнес-необходимость privileged-доступа, не знают допустимые сетевые потоки конкретного сервиса и не гарантируют совпадение Compose-файла с runtime-конфигурацией.
Как часто повторять аудит Docker-хоста?
Повторяйте автоматические проверки при каждом изменении Dockerfile, образа и deployment-конфигурации. Полный аудит хоста и работающих контейнеров проводите по регламенту команды и после событий высокого риска: обновления Engine, сетевых изменений, миграции, появления нового администратора, добавления docker.sock или privileged-контейнера. После каждого исправления обязательна точечная повторная проверка.
Рабочий порядок для production: инвентаризация, проверка Docker API и прав, анализ образов, проверка фактических параметров контейнеров, аудит mounts и сети, контроль секретов, устранение критичных находок, повторная проверка и фиксация результата в отчете.