Как взламывают контейнеры: разбор цепочки инцидента
Контейнер не изолирует приложение автоматически. Разбираем типовую цепочку инцидента: уязвимый образ, лишние права в Kubernetes, доступ к реестру и последствия для узла.

Один уязвимый образ редко становится причиной инцидента сам по себе. Опасной его делает цепочка: разработчик собирает контейнер из неподписанного базового слоя, приложение запускается от root, Kubernetes выдаёт поду лишние права, а из сети разрешён исходящий трафик. Ниже — реконструкция такого сценария для Docker Engine 26.x и Kubernetes 1.29+, с точками, где атаку можно было остановить.
Как начинается инцидент
Рассмотрим не одну публичную атаку и не отчёт конкретной компании, а собирательный сценарий из распространённых ошибок. В продакшене работает веб-сервис в Kubernetes. Образ собирается в CI и публикуется в реестр. В Dockerfile используется тег базового образа вроде node:latest, зависимости ставятся без фиксации полного дерева, а контейнер запускается с правами root.
В приложении появляется уязвимый маршрут. Через него атакующий выполняет команды внутри контейнера или получает доступ к переменным окружения. Там находятся адрес внутреннего сервиса, служебные параметры и токен доступа к облачному хранилищу. Сам контейнер пока не равен узлу кластера, но его возможности уже шире, чем нужны приложению.
Контейнерная безопасность ломается не в одной точке. Образ, рантайм, права пода, сеть и секреты усиливают друг друга — или совместно превращают локальную ошибку в инцидент кластера.
Цепочка атаки по шагам
- Компрометация приложения. Атакующий находит уязвимый endpoint или использует украденные учётные данные. Признаки: необычные параметры запросов, всплеск ответов 4xx и 5xx, появление процессов, которых нет в обычном профиле сервиса.
- Закрепление внутри контейнера. Процесс проверяет окружение: имя пользователя, доступные capabilities, смонтированные каталоги, переменные окружения и токены. Если контейнер работает от root, а файловая система доступна на запись, закрепиться проще.
- Выход за границы приложения. Опасность возникает, когда под получил hostPath, доступ к Docker socket, привилегированный режим или capability, не нужную приложению. В таком случае атакующий может воздействовать уже не только на процесс сервиса.
- Доступ к Kubernetes API. В под по умолчанию может быть смонтирован токен service account. Если RBAC разрешает больше, чем нужно, скомпрометированный процесс получает возможность читать секреты, создавать поды или менять рабочие ресурсы.
- Расширение присутствия. Через доступный API или украденные данные из секрета атакующий ищет соседние namespace, реестр образов, CI и облачные роли. Исходящий трафик используется для связи с внешней инфраструктурой или вывода данных.
- Нагрузка и ущерб. На узле запускается посторонний процесс, майнер или прокси. Приложение начинает медленнее отвечать, растёт потребление CPU, а пользователи видят тайм-ауты. Если затронуты секреты, одной остановкой пода инцидент не заканчивается.
Каждый шаг здесь зависит от предыдущего. Уязвимость приложения не должна давать доступ к узлу. Токен пода не должен позволять читать секреты. Секрет не должен жить в образе. Защита работает только как набор ограничителей.
Что искать в образе
Проверка образа должна начинаться не с вопроса «есть ли CVE», а с вопроса «что реально попадёт в продакшен». Сканер показывает известные уязвимости, но не определяет, нужен ли пакет приложению и может ли процесс использовать найденную библиотеку.
- Плавающие теги.
latestи другие изменяемые теги затрудняют повторную сборку и расследование. Для релиза фиксируйте образ по digest и сохраняйте связь между digest, коммитом и сборкой. - Лишние пакеты. Компилятор, shell, отладчики и пакетные менеджеры увеличивают поверхность атаки. В финальный слой должен попадать только runtime и файлы, необходимые приложению.
- Секреты в слоях. Удалить файл в следующем слое недостаточно: он может остаться в истории образа. Ключи, пароли и токены нельзя помещать в Dockerfile, аргументы сборки и репозиторий.
- Root по умолчанию. Если приложение не требует привилегий, задайте непривилегированного пользователя и проверьте права на каталоги, где сервис пишет данные.
- Непрозрачное происхождение. У образа должны быть владелец, исходный репозиторий, версия базового слоя и результат проверки. Без этого нельзя надёжно ответить, что именно запущено в кластере.
Сноска. Digest — криптографический идентификатор содержимого образа. В отличие от тега он меняется, если меняется сам образ, поэтому подходит для воспроизводимой ссылки на конкретный артефакт.
Где ломается Kubernetes
После публикации безопасного образа риск переезжает в манифесты. Наиболее опасны не отдельные настройки, а их сочетание.
| Настройка | Риск | Безопасная проверка |
|---|---|---|
| Привилегированный контейнер | Процесс получает расширенные возможности ядра и может взаимодействовать с устройствами узла | Включать только для явно обоснованных системных задач |
| hostPath | В контейнер попадают файлы узла, сокеты или конфигурация | Заменять на том с узким назначением и режимом только для чтения |
| Монтирование service account | Скомпрометированный процесс получает токен Kubernetes API | Отключать автоматическое монтирование там, где API не нужен |
| Широкий RBAC | Один взломанный под может читать секреты или менять ресурсы namespace | Выдавать только конкретные действия и ресурсы |
| Отсутствие ограничений ресурсов | Посторонний процесс вытесняет рабочие сервисы с узла | Задавать requests и limits, затем проверять их по фактическому потреблению |
Pod Security Standards в режиме Restricted — полезная базовая граница, но не замена ревью манифестов. Даже при запрете привилегированного режима остаются ошибки RBAC, слишком широкая сеть и секреты, доступные приложению.
Что измерять во время расследования
Не начинайте с удаления подозрительного пода. Сначала сохраните данные, иначе вы уничтожите часть следов.
- Зафиксируйте время первого подозрительного события, namespace, имя пода, узел и digest образа.
- Сохраните audit-логи Kubernetes, логи ingress, CI и реестра за период до и после события.
- Сравните фактический образ с тем, что прошёл сборку: слои, entrypoint, пользователя, зависимости и дату публикации.
- Проверьте события API: чтение секретов, создание подов, изменение ролей, service account и привязок ролей.
- Проверьте исходящие соединения, новые процессы на узлах и скачки CPU. Нагрузка сама по себе не доказывает майнинг, но помогает определить время начала активности.
- После сбора артефактов отзовите скомпрометированные токены, пересоберите образ из проверенного источника и замените зависимости. Просто перезапустить под недостаточно.
Для замеров пригодятся метрики CPU и памяти подов, число запросов к API, сетевые потоки и скорость публикации образов. Фиксированных порогов нет: нормальный профиль зависит от сервиса. Сравнивайте инцидент с базовой линией за обычный рабочий период, а не с универсальным числом.
Типичные ошибочные реакции
«Мы пересканировали образ — значит, всё исправлено»
Сканирование не отзовёт токен и не изменит права уже запущенного пода. Оно также может не обнаружить логическую уязвимость приложения или вредоносную команду, добавленную в pipeline.
«Удалили под — инцидент закрыт»
Если атакующий прочитал секрет, его нужно заменить. Если изменён образ в реестре, необходимо проверить все workload, которые ссылались на тот же тег. Если затронут узел, его следует изолировать и расследовать отдельно.
«Запретим весь исходящий трафик»
Жёсткое правило быстро ломает обновления, внешние API и доставку телеметрии. Надёжнее начать с default deny, затем явно разрешить нужные направления и контролировать DNS, прокси и журналы соединений.
«Достаточно запускать всё от non-root»
Это снижает последствия, но не закрывает уязвимость. Слабый RBAC, доступный секрет или небезопасный hostPath по-прежнему дают атакующему полезные возможности.
Где этот подход ломается
Слоистая защита не спасает, если команда не может восстановить происхождение релиза. Образ без digest, ручные изменения в кластере и отсутствие audit-логов делают расследование догадкой. Проблема также возникает при смешении доверительных зон: один namespace получает доступ к секретам другого, а общий service account используется несколькими сервисами.
Отдельная граница — безопасность самого узла. Контроль манифестов не заменяет обновление ядра, ограничение доступа к container runtime и защиту учётных данных облака. Если у приложения действительно есть потребность в hostNetwork, hostPID или системных capabilities, эту зависимость нужно документировать и изолировать, а не прятать за исключением в политике.
Что делать дальше
Начните с инвентаризации: сопоставьте каждый рабочий под с digest образа, владельцем, namespace, service account и набором разрешений. Затем включите проверку образов в CI, запретите плавающие теги для продакшена, уберите автоматическое монтирование токенов там, где оно не нужно, и задайте минимальные RBAC-права.
Следующий контрольный показатель — не число найденных CVE, а доля workload, для которых известны происхождение образа, владелец, разрешения и сетевые зависимости. Именно по этой карте можно быстро понять, что отключать при инциденте и какие секреты менять в первую очередь.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: Docker Engine security, Pod Security Standards — Kubernetes Documentation
Частые вопросы
Достаточно ли сканировать Docker-образы перед публикацией?
Что делать, если подозрительный контейнер уже работал в продакшене?
Может ли контейнер получить доступ к хосту?
Нужно ли отключать service account у каждого пода?
Что делать с образом, если в нём нашли секрет?
Как проверить защиту при другой версии Kubernetes или Docker?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.