Безопасность контейнеров и Kubernetes
Харденинг Docker и Kubernetes — container escape, риск root/privileged, минимальные образы, RBAC, NetworkPolicy, Pod Security Standards, секреты, service-account-токены, supply chain, runtime-детект и сканирование образов.
13 вопросов
MiddleДизайнОчень частоПлатформа держит 40 сервисов в одном namespace на окружение, CNI — Calico с поддержкой NetworkPolicy. Трафик между подами сейчас ничем не ограничен — любой под достаёт любой другой под и внутрикластерную базу, и ревью отметило, что один скомпрометированный фронтенд-под может обратиться к платежам и к базе напрямую. Спроектируйте сегментацию east-west на NetworkPolicy при ограничениях — без политик действует allow-all; прод не должен сломаться во время выката; резолвинг DNS обязан продолжать работать; трём сервисам законно нужен кросс-namespace доступ к общему кэшу; платформенная команда хочет, чтобы результат ревьюился в Git. Опишите модель политик, порядок выката и способ проверки.
Платформа держит 40 сервисов в одном namespace на окружение, CNI — Calico с поддержкой NetworkPolicy. Трафик между подами сейчас ничем не ограничен — любой под достаёт любой другой под и внутрикластерную базу, и ревью отметило, что один скомпрометированный фронтенд-под может обратиться к платежам и к базе напрямую. Спроектируйте сегментацию east-west на NetworkPolicy при ограничениях — без политик действует allow-all; прод не должен сломаться во время выката; резолвинг DNS обязан продолжать работать; трём сервисам законно нужен кросс-namespace доступ к общему кэшу; платформенная команда хочет, чтобы результат ревьюился в Git. Опишите модель политик, порядок выката и способ проверки.
Задать в каждом namespace базовую политику deny-all на ingress и egress, затем разрешить только явные пары по меткам плюс egress к kube-dns. Выкатывать по одному namespace, наблюдая трафик, хранить политики в Git рядом с нагрузками и проверять связность тестами.
Типичные ошибки
- ✗Считать поды изолированными по умолчанию, когда политик нет вовсе
- ✗Писать только правила ingress, оставив egress и в том числе DNS без внимания
- ✗Включать deny-all везде сразу, не понаблюдав предварительно за трафиком
Уточняющие вопросы
- →Как выяснить реальные пары трафика до того, как что-то включать?
- →Что сломается первым, если egress к DNS кластера не разрешён явно?
MiddleТеорияОчень частоЧто задают Pod Security Standards и как admission control их применяет?
Что задают Pod Security Standards и как admission control их применяет?
Это три профиля — privileged, baseline и restricted, — задающие, какие поля безопасности пода допустимы. Pod Security Admission применяет выбранный профиль на namespace в режиме enforce, audit или warn, и нарушающий под отклоняется при создании.
Типичные ошибки
- ✗Считать стандарты отчётом, а не решением, принимаемым на стадии admission
- ✗Полагать, что профиль restricted включён по умолчанию на весь кластер
- ✗Пропускать режимы audit и warn и включать enforce сразу в живом namespace
Уточняющие вопросы
- →Как безопасно перевести существующий namespace с baseline на restricted?
- →Что использовать, если потребность в политике шире трёх готовых профилей?
JuniorТеорияЧастоЧто находит сканирование образов контейнеров и почему одного сканирования мало?
Что находит сканирование образов контейнеров и почему одного сканирования мало?
Сканер сопоставляет пакеты и библиотеки образа с базами уязвимостей и выдаёт известные CVE по severity. Базы пополняются уже после сборки, поэтому вчера чистый образ сегодня уязвим — нужно пересканировать хранимые образы и пересобирать на свежей базе.
Типичные ошибки
- ✗Считать зелёный отчёт на сборке вечным доказательством чистоты образа
- ✗Верить, что сканер находит вредоносную логику приложения, а не известные CVE
- ✗Сканировать только пакеты базовой ОС, игнорируя зависимости языка
Уточняющие вопросы
- →Почему пиннинг базового образа по digest меняет план пересканирования?
- →Как приоритизировать длинный список CVE, чтобы работа осталась выполнимой?
JuniorТеорияЧастоКак минимальные или distroless базовые образы уменьшают поверхность атаки контейнера?
Как минимальные или distroless базовые образы уменьшают поверхность атаки контейнера?
Каждый бинарь в образе — код, который атакующий может переиспользовать. Distroless или минимальная база не содержит shell, пакетного менеджера и отладочных утилит, поэтому инструментов для следующего шага нет, а патчить нужно кратно меньше.
Типичные ошибки
- ✗Думать, что маленькая база — только оптимизация размера и скорости выкачивания
- ✗Считать, что distroless снимает необходимость сканировать зависимости приложения
- ✗Оставлять shell и пакетный менеджер в продовых образах ради удобства
Уточняющие вопросы
- →Как отлаживать запущенный distroless-контейнер, не добавляя в него shell?
- →Почему меньшая база также сокращает очередь уязвимостей на разбор?
JuniorТеорияЧастоПочему контейнер должен работать не от root и с read-only корневой файловой системой?
Почему контейнер должен работать не от root и с read-only корневой файловой системой?
root в контейнере — это root хоста, отделённый лишь namespaces, поэтому взломанный процесс сразу получает больше прав, чем нужно. Непривилегированный UID и read-only корневая ФС блокируют установку пакетов, подмену бинарей и правку конфигов.
Типичные ошибки
- ✗Считать, что рантайм по умолчанию переотображает root контейнера на обычного пользователя
- ✗Воспринимать read-only корневую ФС как настройку хранения, а не как контроль
- ✗Полагать, что сетевая изоляция компенсирует процесс под root
Уточняющие вопросы
- →Куда выносить пути, в которые приложению действительно нужно писать?
- →Как находить образы, всё ещё объявляющие пользователя root?
MiddleТеорияЧастоКак устроен Kubernetes RBAC и чем опасны cluster-admin и wildcard-глаголы?
Как устроен Kubernetes RBAC и чем опасны cluster-admin и wildcard-глаголы?
Role или ClusterRole перечисляет глаголы над ресурсами, а binding привязывает её к пользователю, группе или ServiceAccount. Least privilege — Role в namespace с явными глаголами. Wildcard или cluster-admin даёт всё в кластере, включая чтение секретов.
Типичные ошибки
- ✗Выдавать cluster-admin рабочему ServiceAccount, чтобы разблокировать деплой
- ✗Думать, что ClusterRole не может открыть объекты namespace вроде Secret
- ✗Ставить wildcard в глаголах или ресурсах, потому что точный список утомителен
Уточняющие вопросы
- →Как проверить существующие привязки и найти переприлегированные ServiceAccount?
- →Почему список глаголов в Role важен не меньше списка ресурсов?
MiddleДизайнЧастоСервису нужны пароль к базе, ключ стороннего API и приватный ключ TLS. Сейчас пароль зашит в образ, ключ API лежит открытой переменной окружения в манифесте Deployment в Git, а ключ TLS хранится в Kubernetes Secret в кластере без шифрования at rest. Спроектируйте работу с секретами при ограничениях — управляемый кластер предоставляет KMS-провайдера; в организации уже есть внешнее хранилище секретов; разработчики не должны читать продовые секреты; ротация не должна требовать пересборки образа; манифесты остаются в Git. Опишите, где живёт каждый секрет, как он попадает в под и что сделать, чтобы утёкший манифест не означал утёкший секрет.
Сервису нужны пароль к базе, ключ стороннего API и приватный ключ TLS. Сейчас пароль зашит в образ, ключ API лежит открытой переменной окружения в манифесте Deployment в Git, а ключ TLS хранится в Kubernetes Secret в кластере без шифрования at rest. Спроектируйте работу с секретами при ограничениях — управляемый кластер предоставляет KMS-провайдера; в организации уже есть внешнее хранилище секретов; разработчики не должны читать продовые секреты; ротация не должна требовать пересборки образа; манифесты остаются в Git. Опишите, где живёт каждый секрет, как он попадает в под и что сделать, чтобы утёкший манифест не означал утёкший секрет.
Убрать значения из образа и из Git — Kubernetes Secret закодирован base64, а не зашифрован. Включить шифрование etcd at rest через KMS-провайдера, подтягивать значения из внешнего хранилища, отдавать их в под смонтированными файлами, а не переменными окружения, и закрыть чтение через RBAC.
Типичные ошибки
- ✗Считать, что кодирование base64 в Kubernetes Secret даёт шифрование
- ✗Оставлять секреты в слоях образа и ротировать их пересборкой
- ✗Отдавать секреты переменными окружения, утекающими в логи и дампы
Уточняющие вопросы
- →Почему шифрование etcd at rest не отменяет необходимость RBAC на секреты?
- →Что изменится в проекте, если тот же секрет нужен десяти namespace?
JuniorТеорияИногдаПочему kube-apiserver — центр безопасности Kubernetes и что его защищает?
Почему kube-apiserver — центр безопасности Kubernetes и что его защищает?
Всё состояние кластера доступно только через apiserver, который на каждый вызов выполняет аутентификацию, затем авторизацию RBAC, затем admission. Защита — отключить анонимный доступ, не публиковать его в интернет и шифровать etcd.
Типичные ошибки
- ✗Думать, что компоненты ходят в etcd напрямую, минуя apiserver
- ✗Считать, что анонимный доступ к apiserver выключен по умолчанию везде
- ✗Забывать, что etcd хранит все секреты кластера в открытом виде
Уточняющие вопросы
- →Почему прямой доступ к etcd на чтение равен чтению всех секретов?
- →Что даёт стадия admission такого, чего не может дать одна авторизация?
MiddleТеорияИногдаКак сброс capabilities, syscall-фильтр seccomp и LSM-профиль AppArmor усиливают контейнер?
Как сброс capabilities, syscall-фильтр seccomp и LSM-профиль AppArmor усиливают контейнер?
Контейнеры делят ядро с хостом, поэтому именно эта поверхность и есть граница любого побега. Сброс всех capabilities с возвратом только нужных убирает привилегированные операции, seccomp сужает набор доступных syscall, а AppArmor ограничивает доступ к файлам.
Типичные ошибки
- ✗Считать, что у каждого контейнера своё ядро, а не общее ядро хоста
- ✗Воспринимать capabilities, seccomp и AppArmor как взаимозаменяемые дубликаты
- ✗Возвращать capabilities скопом вместо перечисления действительно нужных
Уточняющие вопросы
- →Как получить профиль seccomp для приложения, которое вы не писали?
- →Какие capabilities может потребовать обычный HTTP-сервис и почему?
MiddleТеорияИногдаЧто находит runtime-безопасность контейнеров, недоступное сканированию образов на сборке?
Что находит runtime-безопасность контейнеров, недоступное сканированию образов на сборке?
Сканирование описывает образ до запуска, а runtime-инструменты наблюдают живые syscall и события процессов на узле. Они отмечают запуск shell в контейнере, неожиданный exec, запись в чувствительные пути хоста или новые исходящие соединения.
Типичные ошибки
- ✗Ждать от runtime-инструментов того же списка CVE, что уже выдал сканер
- ✗Считать активность syscall внутри контейнера невидимой с узла
- ✗Считать runtime-детект заменой гейтам на сборке и admission control
Уточняющие вопросы
- →Какое поведение контейнера вы считали бы уверенным алертом и почему?
- →Как удержать такие правила от потока ложных срабатываний для дежурного?
MiddleТеорияИногдаЗачем отключать automount токена ServiceAccount и что даёт утёкший токен?
Зачем отключать automount токена ServiceAccount и что даёт утёкший токен?
По умолчанию под получает токен своего ServiceAccount, поэтому исполнение кода внутри даёт аутентифицированный доступ к API с правами этой учётной записи. Отключайте automountServiceAccountToken, если под не ходит в API, и привязывайте ServiceAccount узко.
Типичные ошибки
- ✗Думать, что смонтированный токен привязан к поду и не сработает в другом месте
- ✗Оставлять automount включённым для нагрузок, никогда не ходящих в API Kubernetes
- ✗Считать, что токен только аутентифицирует и не несёт с собой прав
Уточняющие вопросы
- →Как связанные и ограниченные по времени projected-токены меняют ущерб от утечки?
- →Как найти поды, монтирующие токен, которым они никогда не пользуются?
SeniorТеорияИногдаПочему контейнер — не такая же граница безопасности, как виртуальная машина?
Почему контейнер — не такая же граница безопасности, как виртуальная машина?
Контейнер — процессы хоста, изолированные namespaces и cgroups на общем ядре, поэтому дефект ядра или послабление настроек достаёт до узла. Privileged, hostPath, hostPID и docker-сокет равны доступу к узлу, а недоверенным тенантам нужна изоляция на ВМ.
Типичные ошибки
- ✗Считать контейнер границей уровня гипервизора для недоверенных тенантов
- ✗Верить, что у каждого контейнера своё ядро, а не общее ядро хоста
- ✗Читать privileged или монтирование хоста как настройку ресурсов, а не доступа
Уточняющие вопросы
- →Какие нагрузки вы бы обязали запускать на изоляции уровня виртуальной машины?
- →Как найти в кластере поды, уже несущие настройки уровня узла?
SeniorДебаггингИногдаВ kube-аудите фронтенд-ServiceAccount перечисляет секреты по всему кластеру — найдите пробел в RBAC
В kube-аудите фронтенд-ServiceAccount перечисляет секреты по всему кластеру — найдите пробел в RBAC
ClusterRoleBinding на роль с wildcard в ресурсах позволяет фронтенду из одного namespace читать любые секреты кластера. Замените её на Role в пределах namespace с явными ресурсами и глаголами, привяжите через RoleBinding и отключите automount токена.
Открыть задачу →Типичные ошибки
- ✗Читать ClusterRoleBinding так, будто она ограничена одним namespace
- ✗Считать, что глаголы только на чтение делают широкую роль безобидной
- ✗Считать дефектом подробность аудита, а не саму выданную привилегию
Уточняющие вопросы
- →Как найти в кластере все остальные привязки такой же формы?
- →Какое правило детекта добавить, чтобы этот шаблон в следующий раз алертил?