Logo Craft Homelab Docs Контакты Telegram
Трендовые github проекты в нашем телеграм канале. Подпишись →
5 марта 2026 г.

Kubernetes security: RBAC, PodSecurity

Безопасный Kubernetes-кластер строится на четырёх слоях: RBAC ограничивает доступ к API, Pod Security Standards запрещают привилегированные контейнеры, шифрование защищает Secrets в etcd, а runtime-мониторинг через Falco обнаруживает атаки после прорыва первых трёх линий.

По умолчанию Kubernetes достаточно открытый: поды читают секреты всего namespace, приложения работают от root, сервис-аккаунты имеют широкие права. В продакшене это создаёт существенные риски безопасности.

Key Takeaways

  • RBAC: минимальные права — Role в namespace, не ClusterRole; kubectl auth can-i для проверки
  • automountServiceAccountToken: false для подов, которые не обращаются к Kubernetes API
  • PodSecurityPolicy удалена в 1.25; замена — Pod Security Admission с уровнями baseline/restricted
  • Kubernetes Secrets в base64 в etcd — не шифрование; нужен Encryption at Rest или External Secrets Operator
  • Falco обнаруживает runtime-атаки: pod запускает curl, читает /etc/shadow, запускает shell

RBAC: Role-Based Access Control

RBAC контролирует, кто и что может делать с ресурсами кластера. Три ключевых объекта:

  • Role — набор прав в рамках namespace
  • ClusterRole — набор прав на уровне кластера (все namespace или cluster-scoped ресурсы)
  • RoleBinding / ClusterRoleBinding — привязка роли к субъекту (пользователь, группа, ServiceAccount)
# Роль для CI/CD: только деплой в namespace staging
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: staging
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "update", "patch"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer
  namespace: staging
subjects:
  - kind: ServiceAccount
    name: ci-service-account
    namespace: staging
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io

Проверить права:

kubectl auth can-i update deployments \
  --namespace staging \
  --as system:serviceaccount:staging:ci-service-account
# yes

kubectl auth can-i delete pods \
  --namespace staging \
  --as system:serviceaccount:staging:ci-service-account
# no

Никогда не давайте CI/CD cluster-admin. Это нарушает принцип минимальных привилегий и означает, что скомпрометированный CI имеет полный доступ к кластеру.

Роли для разных команд

# Read-only для разработчиков: смотреть, не менять
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer-readonly
  namespace: production
rules:
  - apiGroups: ["", "apps", "batch"]
    resources: ["pods", "deployments", "jobs", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  # НЕ включаем: secrets, exec

ServiceAccount и IRSA

Каждый под использует ServiceAccount. По умолчанию токен автоматически монтируется в /var/run/secrets/kubernetes.io/serviceaccount/token. Для подов, которые не обращаются к Kubernetes API, это лишний вектор атаки:

spec:
  serviceAccountName: my-app
  automountServiceAccountToken: false   # отключить для большинства приложений

В облаках (AWS EKS, GCP GKE) вместо хранения облачных ключей в Secrets используйте IRSA (IAM Roles for Service Accounts) или Workload Identity:

# AWS IRSA: ServiceAccount привязан к IAM-роли
apiVersion: v1
kind: ServiceAccount
metadata:
  name: s3-reader
  namespace: production
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/s3-read-role
# GCP Workload Identity
apiVersion: v1
kind: ServiceAccount
metadata:
  name: storage-reader
  namespace: production
  annotations:
    iam.gke.io/gcp-service-account: storage-reader@project.iam.gserviceaccount.com

Под с таким ServiceAccount получает временные облачные credentials через OIDC-токен — без хранения ключей в кластере.

Pod Security Standards

PodSecurityPolicy устарела и удалена в Kubernetes 1.25. Замена — Pod Security Admission с тремя уровнями, применяемыми на уровне namespace:

  • privileged — без ограничений (для system namespace)
  • baseline — базовые защиты: нет privileged контейнеров, нет host network/PID
  • restricted — строгий: non-root, read-only filesystem, seccomp profile
# Применяем restricted ко всем подам в production namespace
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

При уровне restricted поды должны явно указывать securityContext:

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]

Если сервис пишет временные файлы — монтируйте tmpfs вместо разрешения записи в filesystem:

      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /app/cache
  volumes:
    - name: tmp
      emptyDir: {}
    - name: cache
      emptyDir:
        medium: Memory
        sizeLimit: 100Mi

Secrets и шифрование

По умолчанию Kubernetes Secrets хранятся в etcd в base64 — это кодирование, не шифрование. Кто имеет доступ к etcd, читает все секреты.

Encryption at Rest шифрует Secrets в etcd через kube-apiserver:

# kube-apiserver --encryption-provider-config=/etc/kubernetes/encryption.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: {}   # fallback для незашифрованных данных

После включения шифрования нужно пересоздать все существующие Secrets:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

External Secrets Operator — лучшая альтернатива: хранить секреты в Vault, AWS Secrets Manager или GCP Secret Manager, а в кластере иметь только ссылки:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/db
        property: password
    - secretKey: DB_USERNAME
      remoteRef:
        key: production/db
        property: username

Сканирование и runtime monitoring

# kube-bench: проверка на соответствие CIS Kubernetes Benchmark
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs -l app=kube-bench | grep FAIL

# Trivy Operator: непрерывное сканирование образов на CVE
helm install trivy-operator aquasecurity/trivy-operator \
  --namespace trivy-system \
  --create-namespace

# Посмотреть результаты сканирования
kubectl get vulnerabilityreports -n production

Falco обнаруживает runtime-атаки через системные вызовы:

helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set falco.grpc.enabled=true

Falco генерирует алерты при:

  • Запуске shell в контейнере (kubectl exec ... bash)
  • Чтении sensitive файлов (/etc/shadow, /root/.ssh/id_rsa)
  • Сетевых соединениях из неожиданных контейнеров
  • Повышении привилегий через sudo

Пример custom правила Falco:

- rule: Unexpected outbound connection from api container
  desc: API container making unexpected outbound connection
  condition: >
    outbound and container.name = "api"
    and not fd.sip in (db_allowed_ips)
  output: "Unexpected outbound connection (container=%container.name dest=%fd.rip:%fd.rport)"
  priority: WARNING

Аудит Kubernetes API

Включите audit log для отслеживания всех обращений к API:

# kube-apiserver --audit-policy-file
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: Metadata          # логировать метаданные (кто, что, когда)
    resources:
      - group: ""
        resources: ["secrets"]
  - level: Request
    verbs: ["create", "update", "patch", "delete"]
  - level: None              # не логировать healthcheck
    users: ["system:serviceaccount:kube-system:*"]
    verbs: ["get"]
    resources:
      - group: ""
        resources: ["endpoints", "services"]

Итог

Безопасность Kubernetes — это слои. RBAC с минимальными правами, automountServiceAccountToken: false, Pod Security Standards restricted, шифрование Secrets, регулярное сканирование образов и runtime-мониторинг через Falco. Ни один уровень не защищает от всего, но вместе они делают атаку значительно сложнее.

Следующий шаг — управление конфигурацией Kubernetes через Helm Charts.