харденинг безопасности Kubernetes для production-кластеров

Харденинг безопасности Kubernetes: практическое руководство для production-кластеров

14 минут

Кластеры «как есть» уязвимы из-за RBAC, открытого API и etcd без шифрования. В материале — флаги control plane, Pod Security Standards, default-deny в сети, sysctl на узлах, секреты через Vault и поэтапный план внедрения.

Почему стандартные конфигурации Kubernetes не подходят для production

Kubernetes — стандарт де-факто для оркестрации контейнеров, но настройки «из коробки» редко годятся для production-нагрузок. Ошибки конфигурации безопасности стабильно входят в число самых распространённых и болезненных уязвимостей в cloud-native средах. По руководству NSA и CISA по харденингу, большинство успешных атак на кластеры опирается на несколько известных паттернов: избыточно широкий RBAC, открытые endpoint API server, повышение привилегий контейнеров, слишком свободная сеть между подами и секреты без шифрования at rest. Проблема не в отсутствии инструментов — экосистема CNCF богата решениями — а в том, как ужесточить кластер, не сломав приложения. Команды либо откладывают харденинг на потом, либо вводят контроли настолько жёстко, что страдают продуктивность разработчиков и CI/CD. Третий сценарий сбоя — разные правила в окружениях: staging остаётся с настройками, которые не пройдут production-ревью безопасности. Последствия серьёзны: ransomware-группы целенаправленно ищут API server, открытый в интернет; утечки из-за container escape ведут к штрафам по GDPR, PCI-DSS или SOC 2. Даже без прямой атаки уязвимости уровня ядра — например CVE-2020-8554 (MITM через маршрутизацию NodePort) или последствия Log4Shell для контейнеров — требуют системной митигации, а не разовых патчей. В материале — пять опор: харденинг control plane, безопасность worker nodes, сетевые политики, RBAC и управление секретами.

Харденинг control plane

API server — главная поверхность атаки. Отключите анонимную аутентификацию, включите аудит с ротацией логов, задайте TLS 1.2+ и стойкие шифры, подключите шифрование секретов at rest. Ниже — ориентир флагов kubeadm; пути и набор admission plugins подстройте под дистрибутив. Включите шифрование etcd: без него секреты лежат в открытом виде, и компрометация узла с доступом к etcd раскрывает весь кластер. Ротируйте сертификаты etcd до истечения срока: kubeadm certs check-expiration и kubeadm certs renew all в rolling-режиме.

kubeadm · флаги API server
# kube-apiserver flags (kubeadm configuration snippet)
apiServer:
  extraArgs:
    anonymous-auth: "false"
    enable-admission-plugins: "NodeRestriction,PodSecurity,ServiceAccount"
    profiling: "false"
    audit-log-maxage: "30"
    audit-log-maxbackup: "10"
    audit-log-maxsize: "100"
    audit-log-path: "/var/log/kubernetes/audit.log"
    audit-policy-file: "/etc/kubernetes/audit-policy.yaml"
    encryption-provider-config: "/etc/kubernetes/encryption-config.yaml"
    tls-min-version: "VersionTLS12"
    tls-cipher-suites: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
    client-ca-file: "/etc/kubernetes/pki/ca.crt"
    request-timeout: "10s"
EncryptionConfiguration
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}

RBAC: принцип наименьших привилегий обязателен

Слишком широкие ClusterRole — частая болезнь. Встроенную роль cluster-admin нельзя привязывать к пользователям и service account в production. Собирайте узкие admin-роли через role aggregation, перед выдачей прав проверяйте kubectl auth can-i --list --as=<user>, а непрерывный контроль отдайте OPA или Kyverno.

ClusterRole и binding
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-reader
  labels:
    rbac.example.com/aggregation-role: "true"
rules:
  - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dev-team-namespace-reader
subjects:
  - kind: Group
    name: "engineering-dev"
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-reader
  apiGroup: rbac.authorization.k8s.io

Pod Security Standards

Во всех namespace применяйте уровень restricted Pod Security Standard: он запрещает privileged-контейнеры, рискованные hostPath, лишние capabilities и слабые проекции секретов. Нагрузкам, которым действительно нужны повышенные привилегии (агенты узлов, CNI), выделите отдельные namespace с явными исключениями и change control.

Namespace · метки Pod Security
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: "restricted"
    pod-security.kubernetes.io/enforce-version: "v1.29"
    pod-security.kubernetes.io/warn: "restricted"
    pod-security.kubernetes.io/warn-version: "v1.29"

Сетевые политики: zero trust внутри кластера

По умолчанию любой pod достигает любого другого. Введите default-deny и разрешайте только нужные потоки. Набор манифестов ниже запрещает весь трафик, открывает DNS к kube-dns и разрешает frontend→backend на порту 8080. Нужен CNI с поддержкой NetworkPolicy (Calico, Cilium, Weave Net); перед production проверяйте enforcement в staging.

Набор NetworkPolicy
# Default deny all ingress and egress in the namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# Explicitly allow DNS resolution (required for all pods)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - port: 53
          protocol: UDP
---
# Allow frontend to backend communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - port: 8080

Харденинг узлов

Ужесточайте worker nodes на уровне ОС: отключите swap, заблокируйте неиспользуемые модули ядра, примените sysctl против redirect, фильтрации bridge и ptrace. Доставляйте настройки через Ansible, cloud-init или Talos MachineConfigs. Неизменяемые образы вроде Bottlerocket или Flatcar дополнительно сужают поверхность атаки.

Харденинг ОС
# Disable swap (Kubernetes requires swap to be off or accounted for)
swapoff -a
# Edit /etc/fstab to make swap permanent
sed -i '/swap/d' /etc/fstab

# Disable unused kernel modules
cat > /etc/modprobe.d/k8s.conf << 'EOF'
install br_netfilter /bin/true
install ceph /bin/true
install rbd /bin/true
EOF

# Kernel hardening parameters
cat > /etc/sysctl.d/99-kubernetes-hardening.conf << 'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
kernel.yama.ptrace_scope = 2
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 1
EOF

sysctl --system

Управление секретами

Не храните секреты в ConfigMap в base64 — их декодируют за секунды. Сочетайте шифрование etcd с выделенным хранилищем секретов. HashiCorp Vault и Agent Injector — проверенный способ подставлять краткоживущие значения в runtime. Ротируйте секреты заранее; для учётных данных БД используйте dynamic secrets Vault, где это возможно.

Vault Agent Injector
# Vault Agent Injector annotation pattern on a pod
apiVersion: v1
kind: Pod
metadata:
  name: myapp
  namespace: production
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "myapp-readonly"
    vault.hashicorp.com/agent-inject-secret-db: "secret/data/production/database"
    vault.hashicorp.com/agent-inject-template-db: |
      {{- with secret "secret/data/production/database" -}}
      DATABASE_HOST={{ .Data.data.host }}
      DATABASE_PORT={{ .Data.data.port }}
      DATABASE_USER={{ .Data.data.username }}
      DATABASE_PASSWORD={{ .Data.data.password }}
      {{- end }}
spec:
  containers:
    - name: myapp
      image: myapp:latest
      volumeMounts:
        - name: vault-secret
          mountPath: /vault/secrets
          readOnly: true
  volumes:
    - name: vault-secret
      emptyDir:
        medium: Memory

Пример поэтапной миграции

Платформенная команда ведёт multi-tenant кластер для трёх продуктовых команд. Ревью показывает: поды под root, hostPath повсюду, нет network policies, default service account с cluster-admin, шифрование etcd выключено. Этап 1 — срочные настройки API server (без простоя приложений): anonymous-auth=false, ужесточение admission webhooks, включение шифрования etcd в maintenance window; убедитесь, что workloads стартуют только с service account tokens. Этап 2 — RBAC: аудит ClusterRoleBindings через kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin")', scoped-роли по командам, constraints OPA Gatekeeper против избыточных привязок. Этап 3 — Pod Security Standards: сначала restricted в режиме warn, сбор нарушений через kubectl, затем non-root образы и отказ от hostPath. Этап 4 — сетевые политики: default-deny по namespace, начиная с production; мониторинг разрешённых потоков; проверка cilium-cli connectivity test. Этап 5 — секреты: перенос статических Secret в Vault, injection через Agent, dynamic credentials для БД с TTL около часа, отзыв старых статических учётных данных после grace period. Итог — примерно на 80% меньше покрываемой поверхности по матрице MITRE ATT&CK для Kubernetes при сохранении CI/CD и привычных workflow разработчиков.

Лучшие практики и непрерывный харденинг

1. Автоматизируйте харденинг — не правьте контроли вручную. Кодируйте настройки в Terraform, Ansible или Talos MachineConfigs; в CI падайте на kube-bench при превышении порога severity. 2. Control plane — самый критичный актив: шифруйте etcd, требуйте клиентские сертификаты, ограничьте доступ к etcd узлами API server, ежеквартально проверяйте восстановление из бэкапов. 3. Храните NetworkPolicy в Git рядом с приложением; новые allow-правила утверждают владельцы сети; используйте topology-aware routing, где доступно. 4. Слой за слоем защищайте секреты: шифрование etcd, интеграция с Vault, chmod 600 на PKI kubelet; предпочитайте dynamic secrets статическим. 5. Мониторьте неуспешную аутентификацию в API, изменения RBAC, новые admission webhooks, ослабление securityContext подов и неожиданный DNS из system namespace — в kube-prometheus-stack есть готовые правила. 6. Сканируйте образы в CI (Trivy, Grype, Snyk); в production фиксируйте digest, а не плавающий latest. 7. Держите runbook инцидентов для Kubernetes: изоляция узла, массовый отзыв service account tokens, ротация учётных данных, пересборка namespace из GitOps; отрабатывайте в staging через Litmus или Chaos Mesh. 8. Staging и development должны быть сопоставимы с production по контролям, с осознанными послаблениями только для отладки; каждое исключение — с обоснованием и тикетом на устранение. Харденинг не заканчивается — новые CVE и меняющиеся нагрузки требуют IaC, аудитов, бенчмарков и chaos-экспериментов как части операционного ритма.

Харденинг кластера всё равно упирается в то, как секреты попадают из Git в pod — разобрать подход можно в материале про управление секретами в DevOps и CI/CD.

Декларативные выкаты и policy hooks закрепляют контроли; сопоставьте базовую линию с практиками GitOps с Argo CD и Flux в Kubernetes.