ADG Оставить заявку
Блог DevOps 5 мин чтения

Kubernetes 1.23 и PSP: начинаем миграцию на Pod Security Standards

Kubernetes 1.23 GA: PodSecurityPolicy deprecated, Pod Security Admission в beta. Разбираем разницу подходов и что нашли в клиентских кластерах при первичном аудите.

Контекст момента

Kubernetes 1.23 GA (декабрь 2021): PodSecurityPolicy официально deprecated, Pod Security Admission переходит в beta

Kubernetes 1.23 вышел в декабре, и главная новость с точки зрения безопасности - не новая фича, а таймер. PodSecurityPolicy теперь официально deprecated. Pod Security Admission перешёл из alpha в beta и включён по умолчанию. Срок жизни PSP ограничен: по плану её уберут в 1.25.

Мы начали проходить по клиентским кластерам, которые ведём в рамках managed-сервиса, и смотреть что там с PSP. Картина оказалась предсказуемой, но от этого не менее неприятной.

Что нашли при аудите

В нескольких кластерах PSP живёт в одном из трёх состояний. Первое - политики настроены и реально работают: есть продуманные ограничения, матрица RBAC, команда понимает что там. Таких меньшинство. Второе - есть одна permissive-политика на весь кластер, привязанная ко всем ServiceAccount через system:authenticated. Де-факто PSP включена, де-факто ничего не ограничивает. Третье - PSP-контроллер активирован в admission-плагинах, политики есть, но никто уже не помнит зачем именно такие.

Все три варианта требуют разной работы, но одинаково требуют работы.

Отдельный привет - кластеры, которые обновляли последовательно начиная с давних версий. В них встречаются политики, написанные ещё когда PSP только появилась в alpha (Kubernetes 1.3-1.4), с полями, которые уже устарели, и комментариями вида # TODO: разобраться с этим. TODO не разобрались.

Чем PSP плоха и почему её убирают

PSP - концептуально правильная идея: ограничить что pod может делать на уровне хоста. Запрет privileged-контейнеров, hostPID, hostNetwork, опасных capabilities, монтирование hostPath. Всё это нужно.

Но реализация запутана. Политика выбирается через RBAC - admission-контроллер проверяет, какие PSP доступны ServiceAccount пода или пользователю, сделавшему запрос. Если доступных несколько - берётся первая по алфавиту, которая валидирует pod. Это поведение неочевидно и регулярно порождает сюрпризы: pod неожиданно получает менее строгую политику, чем предполагалось, просто потому что имя другой политики начинается раньше по алфавиту.

Ещё одна проблема - отсутствие нейтральных режимов. PSP либо блокирует, либо нет. Нет нативного способа сказать «посмотри что нарушает эта политика, но не блокируй». Это делает тестирование сложным: в staging всё работает, в production чего-то нет - и ищи разницу в RBAC.

Как устроены Pod Security Standards

Pod Security Admission работает иначе. Вместо произвольных политик - три встроенных профиля:

  • privileged - без ограничений. Для системных namespace-ов: kube-system, мониторинг, CNI.
  • baseline - базовые ограничения. Запрещает privileged-контейнеры, hostPID/hostNetwork/hostIPC, небезопасные capabilities, небезопасные volume-типы.
  • restricted - строгий профиль. Всё из baseline плюс: запуск от root запрещён, seccomp обязателен (RuntimeDefault или Localhost), capabilities ограничены до минимума.

Профиль назначается через лейблы на namespace, и здесь ключевое отличие от PSP - три режима применения:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

enforce - pod отклоняется. warn - pod создаётся, но в ответе API приходит предупреждение (его видно в kubectl-выводе). audit - событие пишется в audit log, pod проходит. Эти режимы независимы и могут быть с разными профилями - например, enforce: baseline и audit: restricted.

Это меняет процесс миграции принципиально: можно поставить warn: restricted и просто деплоить как обычно, собирая предупреждения. Никакого риска что что-то сломается.

Как мы делаем миграцию

Схема, которую мы сейчас применяем по кластерам:

Первое - инвентаризация PSP и привязок. kubectl get psp даёт список политик. Но важно понять кто их использует: kubectl get clusterrolebinding,rolebinding -A -o json и ищем ссылки на PSP. Строим матрицу: политика - namespace - ServiceAccount.

Второе - включить warn для всех production-namespace-ов. Профиль restricted как warn даёт картину что нарушает строгий профиль. Для менее критичных пространств можно начать с baseline. Режим warn работает в 1.23 без дополнительных настроек - PSA в beta, включён по умолчанию.

Третье - разбор нарушений. Типичное что мы находим: контейнеры без явного securityContext, запускающиеся от root. Поля runAsNonRoot: true, runAsUser, allowPrivilegeEscalation: false, capabilities.drop: [ALL] - их просто не добавляли, потому что и так работало. Для restricted ещё нужен seccompProfile. Всё это правится в манифестах деплоев.

Четвёртое - отдельная история с system-компонентами. Для kube-system, CNI (Cilium, Calico), мониторинга (node-exporter, Prometheus agent) - профиль privileged. Это не лень, это реальность: node-exporter монтирует hostPath и нужен доступ к сетевому стеку хоста. Никакой другой профиль там не пройдёт.

Пятое - переключение на enforce. После того как warn не показывает новых нарушений в течение нескольких дней реальных деплоев - переключаем на enforce.

Где PSA не дотягивает до PSP

Честно: не везде PSA - прямая замена. PSP позволяла гибко разрешать конкретные вещи конкретным ServiceAccount. Например, разрешить монтировать определённый hostPath-путь только для одного DaemonSet. В PSA такого нет - профиль применяется к namespace целиком.

Если нужна тонкая гранулярность - в бой идут внешние инструменты: OPA/Gatekeeper или Kyverno. Они умеют писать произвольные политики с доступом к полному контексту запроса. PSA и сторонний движок политик не конкурируют - они дополняют друг друга: PSA закрывает базовые профили, Kyverno/Gatekeeper - специфику.

Несколько кластеров с нетривиальными PSP-политиками мы пока оставили как есть и разбираем отдельно. Срок до 1.25 есть, но он не бесконечный - и наш опыт с Kubernetes 1.22 показал, что PSP-конфигурации оказываются сложнее, чем выглядят снаружи.

Где сейчас

Миграция идёт, не завершена. Простые кластеры - те, где была permissive-политика «лишь бы работало» - переводятся быстро: PSP там была чисто номинальной, реальных ограничений не добавляла. Кластеры с реально настроенными политиками требуют больше времени: нужно разобраться с каждым исключением и принять осознанное решение.

Полезный побочный эффект: аудит PSP вскрыл деплои, у которых вообще нет securityContext. Это были бы проблемы и без всякой миграции.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.