Введение: почему безопасность Kubernetes требует особого внимания
Kubernetes управляет контейнерами на тысячах узлов, и каждая ошибка в настройке доступа открывает путь к компрометации всего кластера. По данным CNCF, число production-кластеров растет на 28% ежегодно, а вместе с ними растет и число атак на cloud-native инфраструктуру. Злоумышленники ищут открытые API, избыточные права и секреты в открытом виде.
Безопасность кластера держится на трех опорах: контроль доступа, сетевая сегментация и защита чувствительных данных. Если RBAC настроен с избыточными правами, NetworkPolicies отсутствуют, а секреты лежат в base64, кластер уязвим. В этом руководстве вы настроите каждую из этих областей на практике: создадите роли с минимальными привилегиями, изолируете сетевой трафик между сервисами и выстроите безопасное хранение секретов. В конце получите готовый чек-лист для аудита.
Материал адресован администраторам и DevOps-инженерам, которые отвечают за безопасность контейнерных платформ. Все команды и YAML-манифесты проверены на Kubernetes 1.30 и совместимы с актуальными версиями.
Что будет настроено и в каком порядке
Сначала ограничьте действия пользователей и ServiceAccount через RBAC Kubernetes, затем проверьте, что разрешения соответствуют рабочим задачам. После этого включите сетевую изоляцию в production-namespace: начните с наблюдения и разрешающих правил, а default deny применяйте только после инвентаризации потоков. На последнем этапе защитите secrets Kubernetes: включите encryption at rest, выберите Sealed Secrets или внешнее хранилище и проверьте ротацию. Такой порядок снижает риск потерять административный или межсервисный доступ при изменении политик.
RBAC: детальное разделение прав доступа
RBAC (Role-Based Access Control) определяет, кто и что может делать в кластере. Без него любой пользователь с доступом к API получает права администратора. Настройка RBAC снижает риск несанкционированных действий и ограничивает ущерб при компрометации учетной записи.
Основные понятия RBAC: Role, ClusterRole, Binding
Role действует в пределах одного namespace. ClusterRole работает на уровне всего кластера. Разница принципиальна: Role не может выдать права на узлы, PersistentVolumes или другие cluster-scoped ресурсы.
Binding связывает субъектов с ролями. RoleBinding применяет Role к пользователям, группам или ServiceAccount в конкретном namespace. ClusterRoleBinding применяет ClusterRole ко всему кластеру. Субъектами могут быть пользователи, группы и ServiceAccount.
Пример Role, которая разрешает чтение подов в namespace development:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]Связывание этой роли с ServiceAccount команды разработки:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-pod-reader
namespace: development
subjects:
- kind: ServiceAccount
name: dev-team
namespace: development
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.ioПошаговая настройка RBAC для команды разработки
Сценарий: команда разработки должна управлять подами в своем namespace development, но не иметь доступа к другим namespace и cluster-scoped ресурсам.
Шаг 1. Создайте namespace и ServiceAccount:
kubectl create namespace development
kubectl create serviceaccount dev-team -n developmentШаг 2. Создайте Role с правами на управление подами, деплойментами и сервисами:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: dev-full-access
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]Для production лучше разделять read-only и изменяющие права. Роль для просмотра может содержать только get, list и watch, а права create, update, patch и delete следует выдавать отдельной группе или ServiceAccount, используемому CI/CD. Не добавляйте resources: ["*"] и verbs: ["*"] без отдельного обоснования: такая запись часто незаметно открывает доступ к secrets и другим критичным объектам.
Шаг 3. Создайте RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-full-access-binding
namespace: development
subjects:
- kind: ServiceAccount
name: dev-team
namespace: development
roleRef:
kind: Role
name: dev-full-access
apiGroup: rbac.authorization.k8s.ioШаг 4. Проверьте доступ до и после применения манифестов:
kubectl auth can-i create pods --as=system:serviceaccount:development:dev-team -n development
# yes
kubectl auth can-i create pods --as=system:serviceaccount:development:dev-team -n production
# no
kubectl auth can-i get secrets --as=system:serviceaccount:development:dev-team -n development
# noКоманда kubectl auth can-i показывает, какие действия разрешены для конкретного субъекта. Проверяйте не только ожидаемый yes, но и отрицательные сценарии: доступ к production, secrets, узлам и cluster-scoped ресурсам должен быть no, если он не нужен по задаче.
Типовые ошибки RBAC и как их проверить
Наиболее частая ошибка - привязка ClusterRole через ClusterRoleBinding, когда требовался доступ только к одному namespace. Отдельно проверьте RoleBinding: он может ссылаться на ClusterRole, но при этом сохранять область действия namespace. Выполните:
kubectl get rolebindings,clusterrolebindings --all-namespaces -o yaml
kubectl auth can-i --list --as=system:serviceaccount:development:dev-team -n development
kubectl auth can-i get secrets --as=system:serviceaccount:development:dev-team -n productionЕсли субъект неожиданно получает доступ, ищите все его группы и косвенные привязки, включая привязки к встроенным ролям view, edit и admin. Для аудита полезно сравнивать фактические права с перечнем операций, которые нужны приложению, а не только проверять наличие YAML в репозитории.
Лучшие практики RBAC
Используйте группы для управления доступом, а не отдельные привязки для каждого пользователя. Привязывайте роли к группам через ClusterRoleBinding с указанием группы в subjects. Это упрощает управление при росте команды.
Избегайте использования cluster-admin для повседневных задач. Выдавайте cluster-admin только при начальной настройке кластера и для аварийного восстановления. Для регулярной работы создавайте роли с минимальными правами.
Регулярно аудируйте роли. Команда kubectl get rolebindings --all-namespaces показывает все привязки. Ищите субъектов с правами на delete, update для критичных ресурсов. Подробнее об аудите RBAC читайте в руководстве по аудиту безопасности Kubernetes.
Принцип наименьших привилегий работает так: если команде не нужны права на удаление, не выдавайте их. Лучше добавить права позже, чем удалять после инцидента.
NetworkPolicies: сегментация сетевого трафика
По умолчанию все поды в кластере могут общаться друг с другом. Это удобно для разработки, но опасно для production: если атакующий получит доступ к одному поду, он сможет атаковать все остальные. NetworkPolicies ограничивают сетевые взаимодействия на уровне подов.
Как работают NetworkPolicies: селекторы и правила
NetworkPolicy использует podSelector для выбора подов, к которым применяется политика. policyTypes указывает, какие направления трафика контролируются: Ingress, Egress или оба. Правила ingress и egress определяют, от кого и куда разрешен трафик.
NetworkPolicy Kubernetes не является межсетевым экраном для всех узлов и не контролирует трафик, который не проходит через поддерживаемый CNI. Политика действует только для выбранных подов и только в направлениях, указанных в policyTypes. Если egress ограничен, отдельно разрешите DNS и необходимые внешние зависимости.
Пример политики, которая разрешает входящий трафик только от подов с меткой app=frontend:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Проверьте метки фактических подов до применения политики: kubectl get pods -n production --show-labels. Важно учитывать, что podSelector без namespaceSelector выбирает поды только в том же namespace.
Если в namespace нет ни одной NetworkPolicy, весь трафик разрешен. Первая политика с podSelector, который выбирает все поды (пустой matchLabels), запускает режим default deny.
Практические примеры: изоляция приложений
Пример 1. Default deny для всего namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressЭта политика блокирует весь входящий и исходящий трафик для всех подов в namespace production. После ее применения нужно явно разрешать каждый легитимный поток.
Чтобы не сломать production, сначала составьте список потоков: ingress controller к web, frontend к backend, backend к базе, DNS и необходимые внешние API. Применяйте разрешающие политики по одному потоку, проверяйте соединение и только затем включайте default deny. Изменения выполняйте в отдельном namespace или в окно работ, сохраняя возможность отката манифестов.
Пример 2. Разрешение трафика от ingress controller:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443Проверьте фактический label namespace ingress-nginx: kubectl get namespace ingress-nginx --show-labels. В разных установках ingress controller может работать в другом namespace или использовать другой label. Ошибка в namespaceSelector приведет к блокировке легитимного трафика.
Пример 3. Разрешение egress только к определенному внешнему ресурсу:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5432Если приложение разрешает имена, а не только IP-адрес базы, добавьте egress к DNS-сервису кластера на TCP и UDP порт 53. Для базы за пределами кластера проверьте, что ipBlock соответствует фактическому адресу, маршрутизации и правилам облачной сети.
Мини-диагностика NetworkPolicies
До применения политики зафиксируйте рабочее состояние:
kubectl get networkpolicies -A
kubectl get pods -n production --show-labels
kubectl exec -n production deploy/backend -- sh -c 'nc -vz db.example 5432'После применения проверьте разрешенный и запрещенный сценарии из пода с нужными labels:
kubectl describe networkpolicy -n production allow-egress-to-db
kubectl exec -n production deploy/backend -- sh -c 'nc -vz db.example 5432'
kubectl exec -n production deploy/frontend -- sh -c 'nc -vz backend 8080'
kubectl exec -n production deploy/frontend -- sh -c 'nc -vz web 80'Ожидаемый результат зависит от модели доступа: разрешенные соединения должны проходить, а не предусмотренные политикой - завершаться ошибкой или тайм-аутом. Если запрещенный трафик продолжает проходить, проверьте labels, namespace, направление policyTypes и поддержку CNI. kubectl describe networkpolicy показывает объект, но не доказывает, что dataplane его применяет; для диагностики нужны логи и инструменты конкретного CNI.
Выбор CNI-плагина с поддержкой NetworkPolicies
NetworkPolicy - это API-объект, но его применяет CNI-плагин. Не все CNI поддерживают политики. Calico, Cilium и Weave Net реализуют полную поддержку NetworkPolicy. Flannel не поддерживает политики без дополнительных компонентов.
Проверьте CNI до внедрения: определите его по pod'ам в kube-system, документации дистрибутива и конфигурации кластера. Уточните поддерживаемую версию Kubernetes и режим dataplane: возможности, логирование и поддержка функций могут отличаться между версиями. Также проверьте, не используется ли отдельный компонент для kube-proxy replacement, hostNetwork или внешнего трафика: NetworkPolicy не всегда покрывает такие исключения.
Calico - проверенное решение для production-кластеров, поддерживает NetworkPolicy и расширенные сетевые политики. Cilium использует eBPF и предлагает более высокую производительность и наблюдаемость. Weave Net проще в настройке, но менее распространен в крупных кластерах. Выбор зависит от требований к производительности и функциональности. Для большинства сценариев Calico или Cilium - оптимальный выбор.
Управление секретами: от нативных Secrets до внешних систем
Секреты содержат пароли, токены, ключи API. Их компрометация дает атакующему доступ к базам данных, внешним сервисам и внутренним системам. Управление секретами требует отдельного подхода.
Проблемы нативных Kubernetes Secrets
Нативные Secrets хранятся в etcd в base64. Base64 - это кодирование, а не шифрование. Любой, кто получит доступ к etcd или к объекту Secret через API, сможет декодировать данные. По умолчанию etcd не шифрует данные на диске.
Доступ к Secret через API означает доступ к данным. Если у пользователя есть права get на secrets в namespace, он видит все пароли этого namespace. Это создает риск при избыточных RBAC-правах. Подробнее о работе с нативными Secrets читайте в полном руководстве по Kubernetes Secrets.
Как выбрать защиту: encryption at rest, Sealed Secrets или Vault
Encryption at rest защищает данные Secret в etcd на диске и является базовым требованием для production, но не заменяет RBAC: пользователь с разрешением get все равно получит расшифрованное значение через API. Sealed Secrets подходят для GitOps, когда зашифрованный манифест должен храниться в Git и расшифровываться только в кластере. Vault или External Secrets Operator целесообразны, когда нужны централизованная ротация, аудит, единая политика для нескольких кластеров и внешних систем. Выбирайте один основной жизненный цикл секретов и отдельно документируйте доступ аварийного восстановления.
Sealed Secrets: шифрование секретов для GitOps
Sealed Secrets решает проблему хранения секретов в Git. Вы шифруете секрет с помощью kubeseal, получаете манифест SealedSecret, который можно безопасно коммитить в репозиторий. Расшифровать его может только контроллер Sealed Secrets в кластере.
Установка контроллера:
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.1/controller.yamlШифрование секрета:
kubectl create secret generic db-password --from-literal=password=MySecret123 -n production --dry-run=client -o yaml | kubeseal --format yaml > sealed-secret.yamlПолученный sealed-secret.yaml можно коммитить в Git. Контроллер в кластере расшифрует его и создаст обычный Secret. Приватный ключ хранится только в кластере, что делает манифест бесполезным для посторонних. Резервную копию ключа контроллера храните отдельно и защищайте теми же правилами, что и другие критичные ключи: без нее восстановление SealedSecret после потери кластера может быть невозможно.
Интеграция с HashiCorp Vault
HashiCorp Vault централизует управление секретами: хранение, ротацию, аудит и доступ. Интеграция с Kubernetes позволяет синхронизировать секреты из Vault в Kubernetes Secrets или монтировать их напрямую через CSI driver.
Практичный способ - External Secrets Operator. Он создает Kubernetes Secrets из секретов Vault по расписанию. Установка:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespaceНастройка SecretStore для подключения к Vault:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
auth:
kubernetes:
mountPath: "kubernetes"
role: "k8s-role"Перед применением проверьте, что роль Vault разрешает только нужный путь secret и что ServiceAccount имеет доступ к этой роли. Сам SecretStore не проверяет бизнес-логику прав: ошибки в policy Vault или в namespace ServiceAccount проявятся как ошибки синхронизации.
Создание ExternalSecret для синхронизации:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: "1h"
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: db/password
property: valueПроверьте результат синхронизации:
kubectl get externalsecret db-credentials -n production
kubectl describe externalsecret db-credentials -n production
kubectl get secret db-credentials -n productionПреимущества Vault: автоматическая ротация, детальный аудит, единое хранилище для всех кластеров. Сравнение нативных Secrets и внешних систем читайте в статье о выборе хранилища секретов.
Шифрование Secrets at rest
Даже при использовании внешних систем нативные Secrets остаются в etcd. Включите шифрование at rest, чтобы защитить их на диске.
Создайте файл EncryptionConfiguration:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}Передайте файл в kube-apiserver через флаг --encryption-provider-config. После включения шифрования новые записи будут сохраняться с выбранным провайдером, но существующие секреты необходимо перезаписать, чтобы выполнить их перешифрование. Команду пересоздания выполняйте по процедуре, принятой для вашей версии и конфигурации API server, предварительно сделав резервную копию etcd и проверив восстановление.
Проверяйте настройку по конфигурации kube-apiserver и результатам штатного аудита, а не поиском строки aescbc в содержимом Secret: API Kubernetes возвращает объект уже расшифрованным. Сохраните резервную копию ключа шифрования. Потеря ключа означает невозможность расшифровать секреты.
Чек-лист аудита безопасности кластера
Этот чек-лист поможет проверить соответствие требованиям и выявить уязвимости. Выполняйте его регулярно, минимум раз в квартал или после значительных изменений в кластере.
Проверка RBAC
- Выполните kubectl get clusterrolebindings и найдите всех субъектов с cluster-admin. Их должно быть минимальное количество.
- Выполните kubectl auth can-i --list --as=system:serviceaccount:default:default для проверки прав сервисного аккаунта по умолчанию.
- Проверьте, что нет RoleBinding, которые выдают права на secrets для широкого круга пользователей.
- Используйте kubectl get rolebindings --all-namespaces -o yaml и ищите подозрительные привязки.
- Отдельно проверьте доступ к secrets, exec, portforward, nodes и cluster-scoped ресурсам.
Проверка NetworkPolicies
- Убедитесь, что в каждом production-namespace есть default deny политика.
- Проверьте, что CNI-плагин поддерживает NetworkPolicy: kubectl get pods -n kube-system | grep calico или cilium.
- Протестируйте разрешенный и запрещенный доступ между подами с учетом labels и namespaceSelector.
- Проверьте DNS после включения Egress-политик и убедитесь, что egress-трафик ограничен там, где это необходимо.
- Сверьте версию Kubernetes и CNI, режим dataplane и ограничения для hostNetwork, внешних адресов и сервисов.
Проверка управления секретами
- Проверьте конфигурацию kube-apiserver и наличие EncryptionConfiguration для secrets. Проверка содержимого Secret через API не показывает, зашифрован ли он в etcd.
- Убедитесь, что секреты не хранятся в образах: сканируйте образы на наличие паролей и ключей.
- Проверьте, что секреты не закоммичены в Git в открытом виде.
- Если используются Sealed Secrets, проверьте резервное копирование ключа контроллера и процедуру восстановления.
- Если используются внешние системы, проверьте настройки ротации, аудита, синхронизации и RBAC-доступа.
Дополнительные проверки
- Примените Pod Security Standards: kubectl label namespace production pod-security.kubernetes.io/enforce=restricted.
- Проверьте версии компонентов: kubectl version. Обновляйте Kubernetes минимум раз в 6 месяцев.
- Настройте аудит API server и проверяйте логи на подозрительные действия.
- Сканируйте образы на уязвимости с помощью Trivy или Grype.
- Запустите kube-bench для проверки соответствия CIS Benchmark.
Полный план аудита безопасности с готовыми командами читайте в практическом плане аудита IT-инфраструктуры.
Заключение
Безопасность Kubernetes - это три уровня: RBAC ограничивает доступ, NetworkPolicies сегментируют сеть, управление секретами защищает чувствительные данные. Каждый уровень требует настройки и регулярной проверки.
Начните с RBAC: создайте роли с минимальными привилегиями, избегайте cluster-admin, используйте группы. Затем примените NetworkPolicies: сначала инвентаризируйте потоки, разрешите DNS и рабочие зависимости, после этого включите default deny в production-namespace и проверьте доступ. Защитите секреты: включите шифрование at rest, используйте Sealed Secrets для GitOps или интегрируйтесь с Vault через External Secrets Operator.
Безопасность - непрерывный процесс. Запускайте чек-лист аудита регулярно, обновляйте компоненты, сканируйте образы. Каждая проверка снижает риск компрометации. Для углубленного изучения настройки RBAC читайте практическое руководство по RBAC, а для настройки аудита API server - руководство по аудиту безопасности.