Kubernetes 1.24 и CIS Benchmark v1.7: усиление безопасности в продакшне
После выхода CIS Kubernetes Benchmark 1.7 обновляем кластеры клиентов: новые требования к seccomp, AppArmor, Pod Security Standards и автоматизация проверок через kube-bench в CI.
CIS Kubernetes Benchmark v1.7 для Kubernetes 1.24: новые требования к seccomp, AppArmor и Pod Security Standards
CIS Benchmark для Kubernetes обновляется примерно синхронно с мажорными релизами платформы. Версия 1.7 бенчмарка вышла вместе с Kubernetes 1.24 - и принесла несколько изменений, которые для продакшн-кластеров означают конкретную работу руками, а не просто чтение release notes.
Мы ведём кластеры клиентов в рамках managed-сервиса и начали проходить по ним с обновлённым чеклистом. Вот что нашли и что делаем.
Что изменилось в CIS Benchmark v1.7
Основной акцент новой версии - ужесточение вокруг трёх областей.
Seccomp теперь явно обязателен. В предыдущих версиях бенчмарка seccomp был в разряде рекомендаций с пометкой «применяйте где возможно». В v1.7 это hard-требование: для подов в production-namespace-ах должен быть явно задан профиль RuntimeDefault или Localhost. Профиль Unconfined (он же отсутствие профиля вообще) отныне считается нарушением. Звучит просто, но в реальных кластерах это затрагивает все деплои, у которых нет явного seccompProfile в securityContext.
AppArmor - требование для узлов на Linux. Если дистрибутив поддерживает AppArmor (Ubuntu, Debian, AstraLinux), кластерная конфигурация должна это отражать. Конкретно - проверяется наличие аннотации container.apparmor.security.beta.kubernetes.io/<container-name>: runtime/default для подов там, где AppArmor доступен. Узлы на дистрибутивах без AppArmor (RHEL, AlmaLinux, Rocky - там SELinux) - отдельная история, бенчмарк учитывает это через conditional.
Pod Security Standards вместо PSP. Бенчмарк v1.7 переориентирован на PSA-лейблы. Для namespace-ов с production-нагрузкой рекомендован профиль restricted, для системных компонентов - явное указание privileged. Это согласуется с тем, что мы уже делали после 1.23, но теперь это часть формальных проверок, а не только наша практика.
Как мы автоматизируем проверку через kube-bench
Ручной аудит по бенчмарку - занятие полезное ровно один раз. Потом кластер меняется, нода пересоздаётся, кто-то добавляет новый namespace - и картина снова расходится с требованиями. Поэтому мы поставили kube-bench в CI-пайплайн.
kube-bench запускается как Job в кластере и проверяет конфигурации api-server, controller-manager, scheduler, etcd и узлов против актуальной версии CIS Benchmark. Для 1.24 используется профиль cis-1.7 - он идёт в поставке инструмента.
Пример манифеста для запуска:
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
namespace: kube-system
spec:
template:
spec:
hostPID: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench", "--benchmark", "cis-1.7"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
В CI мы парсим вывод kube-bench и падаем при наличии FAIL по разделам с весом WARN или выше. Секции INFO пропускаем - там обычно ручные организационные проверки типа «ведётся ли журнал изменений» - kube-bench их не может проверить автоматически.
Что нашли в кластерах
Проблемы предсказуемо кластеризуются вокруг двух вещей.
Отсутствие seccompProfile в манифестах. Это массово. Большинство деплоев написаны без явного securityContext.seccompProfile, потому что Kubernetes не требовал его явно. RuntimeDefault включается автоматически только если seccompDefault включён в конфигурации kubelet (feature gate, в 1.24 он ещё не дефолтный). В итоге поды запускаются с Unconfined по факту, хотя никто так явно не писал.
Фикс на уровне кластера: включить seccompDefault: true в KubeletConfiguration. Это переключает поведение по умолчанию без правки каждого манифеста. Но включать нужно осторожно - на кластерах с экзотическими системными вызовами в приложениях могут появиться блокировки. Тестируем в staging сначала.
Namespace-ы без PSA-лейблов. Особенно грешат namespace-ы, созданные операторами или Helm-чартами. При установке чарта namespace создаётся без лейблов вообще - а значит, PSA-профиль не применяется. Добавляем лейблы через Kyverno-политику ClusterPolicy, которая мутирует namespace при создании и проставляет как минимум warn: baseline. Потом смотрим что всплыло - и думаем дальше.
Про AppArmor в нашей реальности
Клиентские кластеры у нас работают на разных дистрибутивах. Ubuntu-узлы - AppArmor доступен. AlmaLinux-узлы - SELinux, AppArmor не применим. Это значит, что единого подхода нет: нужно смотреть на конкретный кластер.
На Ubuntu-узлах kube-bench корректно определяет наличие AppArmor и проверяет соответствующую секцию. Там реально нужно убедиться, что профиль runtime/default задан. На AlmaLinux kube-bench пропускает AppArmor-проверки - и это правильно, не надо форсировать то, чего нет в системе.
Где сейчас
По кластерам идём волнами. Первая волна - включение seccompDefault на control plane и worker-нодах. Вторая - расстановка PSA-лейблов через Kyverno. Третья - правка манифестов тех деплоев, которые в staging показали проблемы после включения seccomp.
kube-bench в CI уже работает на нескольких кластерах. Пока это только репорт - он не блокирует деплой, потому что хочется сначала собрать картину и убедиться что автоматика не ловит ложные срабатывания. Блокирующий режим планируем включить после того, как пройдём основной объём исправлений.
Нулевых нарушений по CIS Benchmark v1.7 нет ни на одном кластере - что честно, потому что стандарт стал строже, а кластеры создавались под старые требования. Прогресс есть, финал пока не близко.
- Kubernetes 1.24: dockershim удалён, миграция на containerd обязательна · 8 апреля 2022
- Kubernetes 1.23 и PSP: начинаем миграцию на Pod Security Standards · 14 января 2022