Глава 14. Kubernetes: глубокое погружение
«Kubernetes — не безопасен по умолчанию. Но он предоставляет все примитивы, чтобы построить платформу с нулевым доверием.»
В предыдущих главах мы рассматривали отдельные аспекты Zero Trust в Kubernetes: SPIFFE/SPIRE (глава 7), микросегментацию (глава 10), service mesh (глава 12), admission control (глава 13). Эта глава объединяет все элементы в целостный стек и углубляется в три темы, которые ещё не были раскрыты: RBAC, управление секретами и мультитенантность.
14.1. Зачем: Kubernetes как поверхность атаки
Kubernetes-кластер содержит десятки компонентов с собственными attack vectors:
| Компонент | Риск | Механизм защиты |
|---|---|---|
| kube-apiserver | Несанкционированный доступ к API | RBAC + admission control + аудит |
| etcd | Чтение секретов в открытом виде | Encryption at rest + mTLS |
| kubelet | Выполнение произвольных команд | Node authorization + certificate rotation |
| Pods | Lateral movement между нагрузками | NetworkPolicy + PSS + SPIFFE |
| Secrets | Утечка credentials | Vault CSI + External Secrets |
| Container runtime | Container escape | User namespaces + seccomp + AppArmor |
Kubernetes 1.33–1.35 (2025) принесли ключевые улучшения безопасности:
- K8s 1.33: User namespaces включены по умолчанию — изоляция UID/GID контейнеров от хоста.
- K8s 1.35: Pod certificates для mTLS (KEP-4317, beta) — нативная поддержка workload identity без внешних контроллеров. Constrained impersonation (KEP-5284, alpha) — защита от spoofing нод.
14.2. RBAC: минимальные привилегии
14.2.1. Модель RBAC
Kubernetes RBAC (rbac.authorization.k8s.io/v1) определяет четыре ресурса:
| Ресурс | Назначение |
|---|---|
| Role / ClusterRole | Определяют набор разрешений (verbs + resources) |
| RoleBinding / ClusterRoleBinding | Привязывают роли к субъектам (users, groups, SA) |
- Role / RoleBinding — область видимости: один namespace.
- ClusterRole / ClusterRoleBinding — область видимости: весь кластер.
14.2.2. Best practices для Zero Trust
1. Отключите автоматическое монтирование токенов:
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
automountServiceAccountToken: falseПо умолчанию каждый Pod получает токен ServiceAccount, смонтированный в /var/run/secrets/kubernetes.io/serviceaccount/token. Если приложение не обращается к Kubernetes API, этот токен — лишняя поверхность атаки.
2. Один ServiceAccount на приложение:
Не используйте default ServiceAccount. Создайте отдельный SA для каждого Deployment с минимально необходимыми разрешениями.
3. Ограничьте глаголы и ресурсы:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-reader
namespace: production
rules:
# Только чтение ConfigMap и Secret — ничего больше
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
# Доступ к конкретным секретам по имени
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["app-config", "db-credentials"]
verbs: ["get"]4. Запретите wildcards (*):
# ПЛОХО: даёт доступ ко всему
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# ХОРОШО: явное перечисление
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]5. Используйте resourceNames для точечного доступа:
Если приложению нужен один конкретный Secret, ограничьте доступ именем, а не выдавайте доступ ко всем Secret в namespace.
14.2.3. Аудит RBAC
Инструменты для выявления избыточных прав:
| Инструмент | Назначение | Установка |
|---|---|---|
rbac-tool (Alcide/Rapid7) | Визуализация RBAC, поиск who-can, генерация политик из аудит-логов | kubectl krew install rbac-tool |
kubectl auth can-i | Проверка разрешений конкретного субъекта | Встроенный в kubectl |
kubectl auth whoami | Информация о текущем пользователе (K8s 1.27+) | Встроенный в kubectl |
# Кто может удалять pods в namespace production?
kubectl auth can-i delete pods -n production --list
# Визуализация RBAC графа (rbac-tool)
kubectl rbac-tool viz --outformat dot | dot -Tpng -o rbac.png
# Генерация минимальных ролей из аудит-логов
kubectl rbac-tool auditgen --audit-log /var/log/kubernetes/audit.log14.2.4. RBAC в управляемых кластерах
| Платформа | Интеграция RBAC | Ключевые изменения |
|---|---|---|
| EKS | Access Entries API (замена aws-auth ConfigMap, deprecated) | IAM → K8s RBAC через API, не через ConfigMap |
| GKE | Google Groups for RBAC, Workload Identity | IAM roles → K8s ClusterRole автоматически |
| AKS | Azure RBAC for K8s (GA) | Entra ID groups → K8s roles, Azure Policy integration |
EKS Access Entries — рекомендуемый метод с 2024:
# Создание access entry для IAM-роли
aws eks create-access-entry \
--cluster-name production \
--principal-arn arn:aws:iam::123456789012:role/AppDeployRole \
--type STANDARD
# Привязка к K8s-группе
aws eks associate-access-policy \
--cluster-name production \
--principal-arn arn:aws:iam::123456789012:role/AppDeployRole \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=namespace,namespaces=production14.3. Управление секретами
14.3.1. Проблема нативных Secrets
Kubernetes Secrets по умолчанию хранятся в etcd в base64 (не зашифрованными). Даже с включённым encryption at rest (EncryptionConfiguration) секреты доступны любому, кто имеет RBAC-доступ к ресурсу secrets.
В модели Zero Trust секреты должны:
- Храниться во внешнем хранилище (Vault, AWS Secrets Manager, Azure Key Vault).
- Доставляться через проверенные каналы (CSI driver, оператор).
- Ротироваться автоматически.
- Быть доступны только авторизованным workloads.
14.3.2. Vault CSI Provider
Secrets Store CSI Driver (secrets-store.csi.x-k8s.io/v1) — стандартный Kubernetes-механизм для монтирования секретов из внешних хранилищ. Vault CSI Provider — реализация для HashiCorp Vault.
Архитектура:
Аутентификация: Pod предоставляет ServiceAccount JWT. Vault проверяет его через Kubernetes TokenReview API и выдаёт секреты согласно политике, привязанной к роли.
# SecretProviderClass: маппинг Vault-секретов
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-db-creds
namespace: production
spec:
provider: vault
parameters:
roleName: "app-role"
vaultAddress: "https://vault.vault-system:8200"
objects: |
- objectName: "db-username"
secretPath: "database/creds/app-db"
secretKey: "username"
- objectName: "db-password"
secretPath: "database/creds/app-db"
secretKey: "password"
# Опционально: синхронизация в K8s Secret
secretObjects:
- secretName: app-db-creds
type: Opaque
data:
- objectName: db-username
key: username
- objectName: db-password
key: password# Deployment: монтирование секретов
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: production
spec:
template:
spec:
serviceAccountName: app-sa
containers:
- name: app
image: ghcr.io/my-org/app:v1.2.3
volumeMounts:
- name: secrets
mountPath: "/mnt/secrets"
readOnly: true
env:
# Из синхронизированного K8s Secret
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: app-db-creds
key: username
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: vault-db-creds14.3.3. Vault Agent Injector vs CSI Provider
| Критерий | Vault Agent Injector | Vault CSI Provider |
|---|---|---|
| Механизм | Mutating webhook + sidecar | CSI volume driver |
| Sidecar | Да (vault-agent контейнер) | Нет |
| Ресурсы | Дополнительные CPU/RAM на Pod | Минимальные (DaemonSet) |
| Ротация | Автоматическая (agent обновляет файлы) | При перемонтировании тома |
| Sync в K8s Secret | Нет | Да (через secretObjects) |
| Шаблоны | Go templates (гибкие) | Только key-value |
Рекомендация: CSI Provider — для простых сценариев (key-value секреты). Agent Injector — когда нужны шаблоны, автоматическая ротация или динамические credentials.
14.3.4. External Secrets Operator (ESO)
External Secrets Operator (ESO) — альтернативный подход: оператор синхронизирует секреты из внешних хранилищ в нативные Kubernetes Secrets.
Примечание: ESO активно развивается (v1.x в 2025–2026), но проект зависит от ограниченной команды мейнтейнеров. Перед внедрением проверьте актуальный статус поддержки на external-secrets.io.
Поддерживаемые провайдеры: AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, 1Password, GitLab Variables, Doppler и другие.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: db-password
remoteRef:
key: secret/data/app
property: db-password14.4. Мультитенантность
14.4.1. Уровни изоляции
| Уровень | Механизм | Применение |
|---|---|---|
| Namespace | RBAC + ResourceQuota + LimitRange + NetworkPolicy | Базовая изоляция команд |
| Capsule | Tenant CRD: агрегация namespaces с наследованием политик | Soft multi-tenancy для внутренних команд |
| vCluster | Виртуальный control plane (API server + controller manager) | Hard multi-tenancy для внешних пользователей |
| Multi-cluster | Отдельные кластеры (Cluster API v1.12) | Максимальная изоляция |
14.4.2. Namespace-based tenancy
Минимальный набор для изоляции tenant:
# 1. Namespace с PSS
apiVersion: v1
kind: Namespace
metadata:
name: tenant-a
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
tenant: team-alpha
---
# 2. ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-quota
namespace: tenant-a
spec:
hard:
requests.cpu: "10"
requests.memory: "20Gi"
limits.cpu: "20"
limits.memory: "40Gi"
pods: "50"
---
# 3. Default deny NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: tenant-a
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress14.4.3. Capsule: soft multi-tenancy
Capsule (v0.12.4, декабрь 2025) реализует мультитенантность через CRD Tenant, агрегирующий namespaces с автоматическим наследованием политик:
apiVersion: capsule.clastix.io/v1beta2
kind: Tenant
metadata:
name: team-alpha
spec:
owners:
- name: alice
kind: User
- name: team-alpha-admins
kind: Group
namespaceOptions:
quota: 5 # Максимум 5 namespaces
networkPolicies:
items:
- policyTypes:
- Ingress
- Egress
podSelector: {}
# Все namespaces tenant'а получат deny-all по умолчанию
resourceQuotas:
items:
- hard:
requests.cpu: "20"
requests.memory: "40Gi"
pods: "100"
limitRanges:
items:
- limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"Пользователь alice может самостоятельно создавать namespaces в рамках tenant'а, и все политики применяются автоматически.
14.4.4. vCluster: hard multi-tenancy
vCluster (v0.31.0, январь 2026) создаёт полноценный виртуальный кластер внутри namespace хост-кластера:
# Создание виртуального кластера
vcluster create tenant-external \
--namespace vc-tenant-external \
--set networking.advanced.isolateNetworkPolicy=true
# Подключение к виртуальному кластеру
vcluster connect tenant-external --namespace vc-tenant-external
# Внутри — полноценный K8s
kubectl get ns # tenant видит только свои ресурсыКаждый vCluster имеет собственный API server, controller manager и хранилище (SQLite по умолчанию). Tenant может устанавливать свои CRD, использовать любую версию Kubernetes и не влиять на другие tenant'ы.
Внимание: HNC (Hierarchical Namespace Controller) — архивирован в апреле 2025. Не используйте для новых проектов. Мигрируйте на Capsule или vCluster.
14.4.5. Выбор подхода
| Вопрос | Namespace + RBAC | Capsule | vCluster |
|---|---|---|---|
| Tenant'ы доверяют друг другу? | Да | Да | Не обязательно |
| Нужны собственные CRD? | Нет | Нет | Да |
| Нужен свой API server? | Нет | Нет | Да |
| Overhead на tenant | Минимальный | Низкий | Средний-высокий |
| Самообслуживание | Ограниченное | Да (в рамках Tenant) | Полное |
14.5. Полная архитектура Zero Trust для Kubernetes
Слои Zero Trust в Kubernetes:
| Слой | Механизм | Глава |
|---|---|---|
| 1. Идентичность рабочей нагрузки | SPIFFE/SPIRE SVID | Гл. 7 |
| 2. Admission control | PSS + Kyverno/Gatekeeper | Гл. 13 |
| 3. RBAC | Минимальные привилегии для SA | Эта глава |
| 4. Сетевая изоляция | NetworkPolicy (Cilium/Calico) | Гл. 10 |
| 5. mTLS | Service mesh (Istio/Linkerd) | Гл. 12 |
| 6. Секреты | Vault CSI / ESO | Эта глава |
| 7. Мультитенантность | Capsule / vCluster | Эта глава |
14.6. Lab: Zero Trust Kubernetes stack
Предварительные требования
- Docker, kind, kubectl, Helm v3
Шаг 1: Создание кластера
kind create cluster --name zt-k8s-lab --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOFШаг 2: Namespace с PSS
kubectl create namespace production
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restrictedШаг 3: RBAC — минимальные привилегии
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-minimal
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-minimal-binding
namespace: production
subjects:
- kind: ServiceAccount
name: app-sa
namespace: production
roleRef:
kind: Role
name: app-minimal
apiGroup: rbac.authorization.k8s.io
EOFШаг 4: ResourceQuota
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
pods: "20"
EOFШаг 5: Default deny NetworkPolicy
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Разрешить DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
EOFШаг 6: Тестовый workload
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
EOFШаг 7: Проверка
# Pod запущен с restricted PSS
kubectl get pods -n production
# RBAC: SA может читать ConfigMaps
kubectl auth can-i get configmaps \
--as=system:serviceaccount:production:app-sa -n production
# yes
# RBAC: SA НЕ может читать Secrets
kubectl auth can-i get secrets \
--as=system:serviceaccount:production:app-sa -n production
# no
# NetworkPolicy: default deny активна
kubectl get networkpolicy -n production
# NAME POD-SELECTOR AGE
# default-deny-all <none> ...
# allow-dns <none> ...
# ResourceQuota
kubectl describe resourcequota production-quota -n productionОчистка
kind delete cluster --name zt-k8s-lab14.7. Итоги
| Аспект | Инструмент | Принцип |
|---|---|---|
| Идентичность | RBAC + SPIFFE | Каждый workload — уникальная идентичность |
| Авторизация | RBAC + OPA/Kyverno | Минимальные привилегии, deny-by-default |
| Сеть | NetworkPolicy + mesh | Микросегментация, mTLS |
| Секреты | Vault CSI / ESO | Внешнее хранение, динамическая доставка |
| Изоляция | PSS + namespaces/vCluster | Defense-in-depth |
Zero Trust в Kubernetes — это не один инструмент, а слоёная архитектура, где каждый слой компенсирует ограничения других. В следующей главе мы рассмотрим безопасность CI/CD: OIDC-федерация, SLSA и Sigstore (глава 15).
Источники:
- Kubernetes RBAC docs: kubernetes.io/docs/reference/access-authn-authz/rbac/
- Kubernetes Multi-tenancy: kubernetes.io/docs/concepts/security/multi-tenancy/
- Kubernetes PSS: kubernetes.io/docs/concepts/security/pod-security-standards/
- EKS Access Entries: docs.aws.amazon.com/eks/latest/userguide/access-entries.html
- Vault CSI Provider: developer.hashicorp.com/vault/docs/deploy/kubernetes/csi
- Secrets Store CSI Driver: secrets-store-csi-driver.sigs.k8s.io
- Capsule v0.12.4: capsule.clastix.io
- vCluster v0.31.0: vcluster.com
- Cluster API: github.com/kubernetes-sigs/cluster-api
- K8s 1.35 security: cncf.io/blog/2025/12/15/kubernetes-security-2025-stable-features-and-2026-preview/
- External Secrets Operator: external-secrets.io