Container Escape через Docker Socket Exposure: как не превратить контейнер в «админку» всего сервера

Container Escape через Docker Socket Exposure: как не превратить контейнер в «админку» всего сервера

Проброс /var/run/docker.sock внутрь контейнера делает приложение полноценным клиентом Docker API на хосте. На практике это означает возможность запускать и останавливать контейнеры, монтировать директории хоста и управлять сетевой конфигурацией — то есть получать доступ, эквивалентный административному по оценке самой Docker. Если такой контейнер скомпрометирован, «побег» на уровень узла возможен без эксплуатации уязвимостей ядра.

Почему организации до сих пор идут на это? Чаще всего — ради скорости сборок в CI, удобства инструментов управления или «временной» диагностики. Эти сценарии понятны, но цена компромисса высока: контейнер получает возможность выполнять действия, которые обычно доступы только на уровне узла, а удалённый API без корректной защиты превращается в прямую точку входа — его разрешают только с TLS и строгой аутентификацией.

В этой статье разбираем механизм риска при экспонировании сокета, типовые конфигурации, из-за которых он появляется, признаки в Compose и Kubernetes, а также практические способы закрыть проблему: прокси над Docker API с минимально достаточными правами, rootless-режим и user namespaces, политики на уровне платформы и безопасные альтернативы в цепочке сборки.

Цель — дать практичные шаги, которые можно внедрить без радикальной перестройки процессов: от быстрой проверки инфраструктуры до устойчивых политик и мониторинга, помогающих предупредить инцидент ещё на этапе конфигурации.

Что такое Docker socket и почему он критичен

Docker-демон принимает команды по локальному Unix-сокету /var/run/docker.sock. Любой процесс, имеющий доступ к этому сокету, может управлять жизненным циклом контейнеров, сетями и томами, а также запускать новые контейнеры с произвольными параметрами. По сути, членство пользователя в группе docker приравнивается к администраторским правам на узле — это прямо обозначено в документации.

Отдельно стоит отметить риск удалённого API Docker по TCP: его можно включать только с шифрованием и аутентификацией клиентов, иначе это эквивалент «административного» порта наружу см. рекомендации по защите доступа.

Почему проброс docker.sock опасен

Монтируя сокет хоста внутрь контейнера, вы передаёте приложению возможность управлять Docker-демоном. Если злоумышленник получает доступ в этот контейнер (RCE, скомпрометированный токен, уязвимая библиотека), то следующий шаг тривиален: запустить новый контейнер с нужными привилегиями, смонтировать директории хоста, изменить сеть. Никаких специальных эксплойтов — достаточно штатного функционала API.

  • Контроль контейнеров на узле. Создание, остановка, пересоздание — как для штатного оператора.
  • Доступ к файловой системе. Запуск контейнера с монтированием / или нужных директорий хоста.
  • Эскалация прав. Старт привилегированных контейнеров, манипуляции с capabilities, сетевые изменения.
  • Сокрытие следов. Большинство действий выглядят как легитимные операции Docker API и не всегда сразу бросаются в глаза в журналах.

Типовые источники риска

Проброс сокета редко появляется «сам по себе». Чаще всего это побочный эффект удобства и стремления к скорости.

  • CI/CD с «Docker внутри Docker». Раннеру для сборки образов монтируют сокет — и это остаётся в конфигурации надолго.
  • Инструменты управления и автообновления. Portainer, Watchtower и аналоги требуют доступ к демону и нередко получают его шире, чем необходимо.
  • Диагностика и временные решения. Временный доступ для отладки со временем превращается в постоянную настройку.
  • Kubernetes с hostPath. Манифесты, где в Pod подмешан сокет Docker/Containerd (/var/run/docker.sock, /run/containerd/containerd.sock), что нарушает изоляцию и противоречит Pod Security Standards.

Последовательность атаки при наличии доступа к Docker API

Последовательность минималистична: проникновение в контейнер → обнаружение смонтированного сокета → обращение к Docker API → запуск новых контейнеров с нужными монтированиями/правами → доступ к узлу. С технической стороны это стандартные API-вызовы. Поэтому важно предотвращать саму возможность доступа, а не рассчитывать на то, что такие операции останутся незамеченными.

Как обнаружить проблему: проверочный план

Начните с инвентаризации и статического анализа конфигураций. Цель — оперативно найти, где и почему появился проброс.

Docker Compose и одиночные хосты

  • Просмотрите файлы Compose на наличие volumes с путём /var/run/docker.sock.
  • Проверьте вспомогательные контейнеры (Portainer, Watchtower, раннеры): действительно ли им необходим прямой сокет, в какой области и под чьим пользователем.

Kubernetes

  • Ищите hostPath на сокеты рантайма: Docker/Containerd/CRI-O.
  • Проверьте, включены ли Pod Security уровня baseline/restricted, и есть ли политика, блокирующая подобные монтирования.

Статический анализ IaC

  • Используйте сканеры конфигураций: Trivy Config, Checkov, KICS. В их правилах есть сигнатуры на опасные hostPath и доступ к сокетам.

Журналы и телеметрия

  • Включите аудит Docker/Kubernetes: фиксируйте создание контейнеров, добавление монтирований, изменение сетей. Это позволяет заметить злоупотребления.
  • Отслеживайте обращения к удалённым портам 2375/2376 и внутренним Unix-сокетам через средства наблюдения.

Меры предотвращения: как снизить риск без радикальной перестройки процессов

Полный отказ от проброса — идеальная цель. Но в реальной инфраструктуре часто нужны компромиссы. Ниже — практики, которые снижают риск до приемлемого уровня.

Избегайте прямого доступа к демону

Для сборок используйте инструменты, которые не требуют docker.sock:

  • Kaniko — сборка образов без доступа к демону, публикация напрямую в реестр.
  • Docker BuildKit в изолированном или rootless-режиме: кэш, параллелизм, без прямого доступа к хосту.
  • Podman и Buildah — альтернативы для ряда сценариев сборки и управления контейнерами.

Если доступ всё же нужен — ограничьте его прокси и RBAC

Не выдавайте контейнеру «голый» сокет. Поставьте прокси над Docker API и включите только необходимые эндпоинты. Популярный вариант — docker-socket-proxy, где права включаются по переменным окружения (подход «белых списков»).

Rootless Docker и user namespaces

Запуск демона без прав суперпользователя снижает последствия компрометации. Совместите с user namespaces, чтобы UID внутри контейнера маппился на непривилегированный UID на хосте Rootless Docker, userns-remap.

Политики на уровне платформы

Запретите опасные монтирования централизованно: даже если кто-то попытается — платформа отклонит запрос.

Kyverno (пример политики)

{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "deny-runtime-sockets" }, "spec": { "validationFailureAction": "enforce", "rules": [{ "name": "block-runtime-sockets", "match": { "resources": { "kinds": ["Pod"] } }, "validate": { "message": "Монтирование сокетов рантайма запрещено", "deny": { "conditions": { "any": [{ "key": "{{ request.object.spec.volumes[].hostPath.path || '' }}", "operator": "AnyIn", "value": ["/var/run/docker.sock","/run/containerd/containerd.sock","/var/run/crio/crio.sock"] }] } } } }] } } 

OPA Gatekeeper (пример constraint)

apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sDenyHostPath metadata: name: deny-runtime-sockets spec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: blockedPaths: - "/var/run/docker.sock" - "/run/containerd/containerd.sock" - "/var/run/crio/crio.sock" 

Усиление песочницы

  • Включите профили seccomp, AppArmor/SELinux и флаг no-new-privileges по умолчанию см. рекомендации Docker.
  • Минимизируйте Linux capabilities: оставляйте только необходимые для работы приложения.
  • В Kubernetes используйте Pod Security уровня restricted и SecurityContext с запретом привилегированных контейнеров.

Удалённый Docker API — только с TLS и mTLS

Если по архитектуре нужен удалённый доступ к демону, применяйте TLS, проверку клиентских сертификатов и сетевую изоляцию (ACL, VPN, частные сегменты) официальный гайд.

Специальные инструменты (Portainer, Watchtower)

Для таких сервисов выделяйте отдельный узел/namespace, включайте TLS, ведите подробный аудит действий и ограничивайте эндпоинты API через прокси. Это не «рядовые» приложения и к ним нужен отдельный контроль.

CI/CD без проброса сокета

Сборка образов — главный источник соблазна «смонтировать сокет». Сегодня есть зрелые альтернативы.

  • Kaniko — сборка в контейнере, артефакт сразу уходит в реестр; поддерживается кэширование слоёв.
  • BuildKit — предоставляет производительность и кэш без необходимости управлять демоном на хосте; поддерживает rootless.
  • Эфемерные раннеры — если нужен полноценный Docker, выполняйте сборку на отдельной VM и удаляйте её по завершении.
  • Целостность цепочки поставки — подпись образов (Cosign/Sigstore), аттестации (SLSA) и политика приёма только подписанных артефактов.

Мониторинг и оповещения

Даже при строгих политиках стоит контролировать события, связанные с управлением контейнерами.

  • Аномалии Docker/K8s: создание контейнеров вне релизных процедур, запуск с --privileged, появление hostPath на сокеты.
  • Сетевые признаки: обращения к 2375/2376, неожиданный трафик к демону с нетипичных хостов.
  • Аудит Kubernetes: фильтрация по verbs create/patch для Pod/DaemonSet/Deployment с проверкой volumes/hostPath.

Что делать, если «сокет уже внутри»

Контейнер с доступом к Docker API следует рассматривать как потенциальный мост на уровень узла. План реагирования:

  1. Изоляция. Ограничьте сеть для Pod/контейнера, зафиксируйте состояние (снимки, логи).
  2. Аудит действий API. Проверьте историю создания контейнеров, монтирования, изменения сетей и подключённые образы.
  3. Проверка узла. Поиск неизвестных процессов/служб, новых пользователей, изменённых конфигураций, таймеров.
  4. Ротация секретов. Ключи, токены, пароли — всё, до чего мог быть доступ, меняется в приоритетном порядке.
  5. Восстановление из чистого состояния. Пересборка образов и переразвёртывание; «ремонт на месте» возможен, но рискован.
  6. Постмортем. Почему проброс появился, почему его не поймали проверки, как предотвратить повторение.

Краткий чек-лист для продакшена

  • Не монтируйте docker.sock и сокеты рантайма в приложения, которым это не критично.
  • Если доступ необходим — только через прокси с белыми списками методов и учётной записью с минимальными правами.
  • Используйте rootless Docker и/или userns-remap там, где возможно.
  • В Kubernetes применяйте Pod Security (restricted), Kyverno/Gatekeeper, NetworkPolicy.
  • Собирайте образы через Kaniko/BuildKit; для исключений — эфемерные раннеры.
  • Включите аудит Docker/K8s и оповещения на опасные паттерны.
  • Регулярно сканируйте IaC (Trivy/Checkov/KICS) и блокируйте PR с пробросом сокетов.

Частые вопросы

«У нас закрытый контур. Это действительно проблема?»

Да. Внутренний периметр снижает вероятность внешней атаки, но не исключает компрометацию учётных данных или уязвимость в приложении. Доступ к Docker API предоставляет слишком много полномочий, чтобы рассчитывать на «закрытую» сеть как основной барьер.

«Portainer/Watchtower без сокета не работают. Что делать?»

Выделить отдельный узел/namespace, ограничить эндпоинты API через прокси, включить TLS и детальный аудит. Это допустимая конфигурация при условии минимизации прав и постоянного контроля.

«SELinux/AppArmor решают проблему?»

Эти механизмы полезны и обязательны как часть слоёной защиты. Однако, имея доступ к Docker API, злоумышленник действует в рамках разрешённых интерфейсов демона. Политики на уровне платформы и ограничение API всё равно необходимы.

«Нужна разовая диагностика. Можно ненадолго?»

Только в изолированной среде и под запись, с временными правами и последующим удалением ресурса. Предпочтительно использовать прокси с разрешением только необходимых методов и на отдельном хосте.

Итоги

Проброс docker.sock — это не «удобный компромисс», а канал прямого управления узлом. Хорошая новость в том, что существуют зрелые альтернативы: инструменты сборки без сокета, прокси над API с минимально достаточными правами, rootless-режим, user namespaces и политики на уровне платформы. Комбинация этих мер позволяет отказаться от опасной конфигурации или сделать её строго контролируемой в исключительных случаях. Это снижает вероятность побега из контейнера и упрощает расследование, если что-то идёт не по плану.

API container DevSecOps Docker Kubernetes безопасность контейнеров
Alt text
Обращаем внимание, что все материалы в этом блоге представляют личное мнение их авторов. Редакция SecurityLab.ru не несет ответственности за точность, полноту и достоверность опубликованных данных. Вся информация предоставлена «как есть» и может не соответствовать официальной позиции компании.

МЫ — ГЛАВНЫЙ БАГ ЭВОЛЮЦИИ.

Думали, мы "венец творения"? Ха! Оказалось — просто системная ошибка. Наш хвалёный "супер-мозг" — это не фича, а баг, который убивает планету.