RBAC и аутентификация глубже

Рейтинг: 64.6% · 12 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

RBAC и аутентификация глубже

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже (вы здесь)
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
Боль: кто ты и что тебе можно

Представь, что у тебя в кластере живут 30 человек, 200 подов и десяток CI-роботов, и каждый из них стучится в один и тот же API-сервер. Сервер обязан на каждый запрос ответить на два вопроса. Первый: кто ты - это аутентификация. Второй: что конкретно тебе сейчас можно сделать - это авторизация, в Kubernetes реализованная через RBAC. Если ты этого не контролируешь, у тебя один протёкший токен превращается в полный захват кластера. Поэтому kubernetes rbac - не бюрократия для галочки, а основная стена между "сломали один под" и "сломали весь прод".

Ключевая вещь, которую надо принять сразу: в Kubernetes нет объекта User. Ты не найдёшь в etcd запись про человека Ивана. Пользователь - это просто строка, которую API-сервер вытащил из предъявленного удостоверения (сертификата, токена, заголовка). Аутентификация устанавливает identity (имя плюс набор групп), а дальше RBAC решает, что этой строке разрешено. Объекты в кластере есть только у ServiceAccount - машинных идентичностей. Эта асимметрия и есть корень половины граблей, которые мы разберём.

Изображение

Аутентификация: четыре способа доказать, кто ты

Запрос проходит цепочку authenticator-ов по порядку, первый сработавший побеждает. kubernetes аутентификация на практике сводится к четырём механизмам.

1. Клиентские сертификаты x509. Это то, чем по умолчанию ходит сам admin. В сертификате поле CN (Common Name) становится именем пользователя, а поля O (Organization) - его группами. API-сервер проверяет, что сертификат подписан тем CA, которому он доверяет (флаг --client-ca-file). Разберём, что внутри твоего kubeconfig:

Код: Выделить всё

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJ...   # CA кластера в base64
    server: https://10.0.0.1:6443
  name: prod
users:
- name: ivan
  user:
    client-certificate-data: LS0tLS1CRUdJ...        # твой сертификат
    client-key-data: LS0tLS1CRUdJ...                # твой приватный ключ
Главная засада сертификатов: их нельзя отозвать. В Kubernetes нет работающего CRL/OCSP для клиентских сертов. Если сертификат админа утёк, единственный способ его погасить - перевыпустить CA кластера, а это боль на весь кластер. Поэтому сертификаты хороши для bootstrap и аварийного доступа, но не для повседневного входа людей.

2. OIDC - правильный способ пускать людей. kubernetes oidc - это интеграция с внешним провайдером идентичности (Keycloak, Dex, Okta, Azure AD, в RU-реалиях - Keycloak поверх Yandex Cloud Organization). Человек логинится в SSO, получает JWT (id_token), kubectl кладёт его в заголовок Authorization. API-сервер проверяет подпись токена по публичным ключам провайдера (JWKS), достаёт из claim-ов имя и группы. Никаких секретов на стороне кластера, токены короткоживущие, увольнение человека в SSO мгновенно закрывает ему доступ.

С Kubernetes 1.30 (бета) старые флаги --oidc-* устарели в пользу структурированной конфигурации аутентификации - отдельного файла, который передают через --authentication-config. Он позволяет подключить несколько JWT-провайдеров сразу и валидировать токены на CEL-выражениях, причём менять конфиг можно без рестарта kube-apiserver:

Код: Выделить всё

apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
    url: https://keycloak.cyberlake.ru/realms/k8s
    audiences:
    - kubernetes
  claimMappings:
    username:
      claim: email
      prefix: "oidc:"
    groups:
      claim: groups
      prefix: "oidc:"
  claimValidationRules:
  - expression: 'claims.email_verified == true'
    message: "email must be verified"
Префикс oidc: - это не косметика. Он не даёт внешнему пользователю случайно совпасть по имени с внутренним system:-аккаунтом и получить лишние права. В RBAC-биндингах ты потом ссылаешься именно на oidc:ivan@cyberlake.ru.

3. ServiceAccount-токены - для подов и роботов. Это JWT, выпущенный самим кластером. Современный механизм - projected token: kubelet через TokenRequest API получает короткоживущий токен (по умолчанию час), привязанный (bound) к конкретному поду и ServiceAccount, и монтирует его в под. Истёк под - истёк токен. Подробнее ниже, это отдельная большая тема.

4. Webhook token authentication. API-сервер отдаёт предъявленный токен внешнему сервису по HTTP и спрашивает "это валидно, кто это". Так подключают облачные IAM: в managed-кластерах (Yandex Managed Kubernetes, VK Cloud) именно webhook связывает токен облачного аккаунта с identity в кластере.

Авторизация: как устроен RBAC под капотом

Теперь k8s authorization. RBAC живёт в apiGroup rbac.authorization.k8s.io/v1 и состоит ровно из четырёх типов объектов, которые бьются на две пары.

Носители прав:
  • Role - набор разрешений в пределах одного namespace.
  • ClusterRole - набор разрешений на весь кластер либо шаблон, применимый в любом namespace. Только ClusterRole может давать права на cluster-scoped ресурсы (nodes, namespaces, PV) и на нересурсные пути (/healthz, /metrics).
Привязки (кому выдать):
  • RoleBinding - связывает Role или ClusterRole с субъектами внутри namespace.
  • ClusterRoleBinding - связывает ClusterRole с субъектами во всём кластере.
RBAC чисто аддитивный: нет ни одного правила deny. Ты только разрешаешь. Если ни одно правило не дало verb на ресурс - доступ запрещён по умолчанию. Это упрощает рассуждения: чтобы понять, что юзеру можно, надо собрать объединение всех его биндингов, и всё.

Само правило (rule) описывается тремя осями:

Код: Выделить всё

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: payments
  name: pod-reader
rules:
- apiGroups: [""]              # "" - это core-группа (pods, services, configmaps)
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
  resourceNames: ["api-7f9c"] # необязательно: сузить до конкретных имён
Разберём оси:
  • apiGroups - к какой группе API относится ресурс. Пустая строка "" - это core-группа. apps - для Deployment/StatefulSet. networking.k8s.io - для Ingress. Промахнулся группой - правило молча не сработает.
  • resources - тип ресурса во множественном числе (pods, не Pod). Бывают subresource через слэш: pods/log, pods/exec, deployments/scale. Дать get на pods и забыть pods/log - и человек не увидит логи.
  • verbs - что делать: get, list, watch, create, update, patch, delete, deletecollection. Отдельно стоит list против get: list возвращает все объекты разом, get - один по имени. Дашь list на secrets "чтобы видел список" - человек прочитает содержимое всех секретов, потому что в RBAC list отдаёт полные объекты, а не только имена.
resourceNames - тонкий инструмент: сузить право до конкретных объектов по имени. Важная ловушка: resourceNames не работает с verb list, watch, create и deletecollection - только с get, update, patch, delete. Логика в том, что list по своей природе перечисляет всё, имя ему взять неоткуда.

Агрегация ClusterRole: роли, которые собираются сами

Когда у тебя 15 операторов (cert-manager, Argo CD, Prometheus и т.д.), каждый хочет дописать свои права к роли admin. Чтобы не переписывать гигантскую роль руками, есть агрегация. Базовая ClusterRole оставляет rules пустым и объявляет aggregationRule с селектором по меткам:

Код: Выделить всё

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.cyberlake.ru/aggregate-to-monitoring: "true"
rules: [] # control plane заполнит это сам
Контроллер берёт все ClusterRole с меткой rbac.cyberlake.ru/aggregate-to-monitoring=true и сливает их rules в эту роль. Важно: поле rules у агрегирующей роли control plane перезаписывает - руками туда писать бесполезно, изменения вноси в исходные роли с меткой. Именно так устроены встроенные view, edit, admin: новый CRD-оператор просто вешает метку aggregate-to-edit на свою ClusterRole, и его ресурсы автоматически появляются у всех, кому выдан edit.

ServiceAccount и bound-токены подов

serviceaccount - это identity для процессов внутри кластера. У каждого пода он есть, даже если ты его не указал - подставится default из namespace. И вот тут историческая мина: раньше под получал вечный токен из Secret, который лежал в etcd и не протухал никогда. Утёк такой токен с логами в Grafana - и он валиден годами.

Современный Kubernetes (1.22+) перешёл на bound service account tokens через TokenRequest API и projected volume. Что меняется механически:
  • Токен выпускается на ограниченный срок (expirationSeconds, по умолчанию час) и kubelet прозрачно его ротирует.
  • Токен привязан (bound) к конкретному поду и ServiceAccount - в claim-ах зашит UID пода. Удалили под - токен мгновенно становится невалидным, даже если время ещё не вышло.
  • Токен имеет audience - для кого он предназначен. Это блокирует пересылку токена в чужой сервис.
Спецификация явного projected-токена в поде выглядит так:

Код: Выделить всё

spec:
  serviceAccountName: payments-api
  containers:
  - name: app
    image: registry.cyberlake.ru/payments:1.4
    volumeMounts:
    - name: token
      mountPath: /var/run/secrets/tokens
  volumes:
  - name: token
    projected:
      sources:
      - serviceAccountToken:
          path: api-token
          expirationSeconds: 3600
          audience: vault
Руками выпустить короткоживущий токен для отладки или CI:

Код: Выделить всё

$ kubectl create token payments-api -n payments --duration=10m
eyJhbGciOiJSUzI1NiIsImtpZCI6 Il9...

# что внутри - расшифруй payload (вторую часть JWT):
$ kubectl create token payments-api -n payments --duration=10m | \
    cut -d. -f2 | base64 -d 2>/dev/null
{"aud":["https://kubernetes.default.svc"],
 "exp":1739000000,
 "kubernetes.io":{"namespace":"payments",
   "serviceaccount":{"name":"payments-api","uid":"a1b2..."}},
 "sub":"system:serviceaccount:payments:payments-api"}
Обрати внимание на sub: имя ServiceAccount в RBAC всегда имеет вид system:serviceaccount:НЕЙМСПЕЙС:ИМЯ, а ещё каждый SA автоматически входит в группы system:serviceaccounts и system:serviceaccounts:НЕЙМСПЕЙС. На эти группы можно (осторожно) навешивать биндинги.

Практика: проверяем доступ через auth can-i

Главный инструмент диагностики - kubectl auth can-i. Он спрашивает у самого API-сервера через SubjectAccessReview, можно ли действие, и не выполняет его:

Код: Выделить всё

# а мне самому можно удалять поды в payments?
$ kubectl auth can-i delete pods -n payments
yes

# а вон тому ServiceAccount? (impersonation через --as)
$ kubectl auth can-i list secrets -n payments \
    --as=system:serviceaccount:payments:default
no

# полный аудит своих прав одним списком:
$ kubectl auth can-i --list -n payments
Resources       Non-Resource URLs   Resource Names   Verbs
pods            []                  []               [get list watch]
pods/log        []                  []               [get]
selfsubjectaccessreviews.authorization.k8s.io  []  []  [create]
Флаг --as - это impersonation: API-сервер разрешает тебе (если у тебя есть verb impersonate) выполнять запрос от лица другого пользователя, группы или SA. Используется ровно для двух вещей: отладить, почему у человека что-то не работает ("а ну-ка под его личиной"), и контролируемо повысить привилегии без раздачи cluster-admin. Сам verb impersonate надо выдавать крайне скупо - он эквивалентен праву стать кем угодно.

Когда что-то отказано непонятно почему, включают audit log. В audit-policy ставят уровень логирования, и API-сервер пишет каждое решение authorizer-а: кто, какой verb, какой ресурс, какой вердикт (allow/forbid) и по какому правилу RBAC (annotation authorization.k8s.io/decision и reason). Это единственный надёжный способ понять, какой именно биндинг дал или не дал доступ в проде.

Грабли и антипаттерны
  • Дефолтный ServiceAccount с широкими правами. Если ты в namespace навесил права на default SA, их получают все поды, которые явно не указали другой serviceAccountName. Утечка одного пода = доступ ко всему, что мог default. Правило: каждому workload - свой SA, default держать пустым.
  • Раздача cluster-admin "чтобы заработало". cluster-admin - это verb * на resource * во всех группах. Это бэкдор. Любой, кому он выдан, может выпустить себе новые токены, отредактировать RBAC и стереть аудит. Принцип наименьших привилегий: начинай с view, добавляй точечно по факту отказов из can-i.
  • automountServiceAccountToken: true там, где токен не нужен. Большинству подов API-сервер не нужен вообще. Ставь automountServiceAccountToken: false на уровне SA или пода - нет смонтированного токена, нечему утекать.
  • list/watch на secrets вместо get по имени. Классическая дыра: дали list secrets для UI, отдали все секреты целиком.
  • ClusterRoleBinding там, где хватило бы RoleBinding. Связал ClusterRole edit через ClusterRoleBinding - и человек может редактировать во всех namespace, а не в своём. Для ограничения namespace используй RoleBinding, ссылающийся на ClusterRole.
  • Сертификаты для людей. Невозможность отзыва превращает каждую утечку в перевыпуск CA. Люди - через OIDC, сертификаты - только break-glass.
Связь с admission. RBAC отвечает на вопрос "можно ли вообще тронуть этот ресурс", но не смотрит внутрь содержимого. Запретить "создавать привилегированные поды" или "тянуть образы только из нашего registry" RBAC не умеет - это работа admission-контроллеров (Kyverno, Gatekeeper, ValidatingAdmissionPolicy на CEL). Правильная схема: RBAC даёт грубое право create на pods, а admission-политика валидирует конкретный манифест. Это две независимые двери, и пройти надо обе.

Мини-лаба (повтори руками)
  • Создай namespace lab и ServiceAccount ci-bot в нём: kubectl create ns lab; kubectl create sa ci-bot -n lab.
  • Опиши Role pod-reader (get/list/watch на pods и pods/log) и RoleBinding, привязывающий её к ci-bot. Примени.
  • Проверь права именно у этого SA, не у себя: kubectl auth can-i list pods -n lab --as=system:serviceaccount:lab:ci-bot (ожидай yes) и kubectl auth can-i delete pods -n lab --as=system:serviceaccount:lab:ci-bot (ожидай no).
  • Выпусти короткий токен: kubectl create token ci-bot -n lab --duration=15m. Раскодируй его payload через cut -d. -f2 | base64 -d и найди поля sub, aud, exp.
  • Сломай и почини: добавь в Role verb list на secrets, примени, перепроверь can-i list secrets - убедись, что доступ появился, затем убери его. Прочувствуй аддитивность RBAC.
Контрольные вопросы
  • Почему дать verb list на secrets опаснее, чем get по конкретному resourceNames, и почему resourceNames вообще не работает с list?
  • Чем bound projected-токен принципиально безопаснее старого вечного токена из Secret, и что происходит с токеном при удалении пода?
  • В чём разница между RoleBinding на ClusterRole и ClusterRoleBinding на ту же ClusterRole - какой даёт права во всех namespace?
  • Где проходит граница ответственности между RBAC и admission-контроллером при создании пода?
Итог

Аутентификация устанавливает, кто ты (сертификат, OIDC, SA-токен, webhook), RBAC аддитивно решает, что тебе можно (apiGroups/resources/verbs, носители Role/ClusterRole, привязки Binding), а bound-токены и принцип наименьших привилегий держат поверхность атаки маленькой. Запомни три рычага на каждый день: kubectl auth can-i для проверки, --as для отладки под чужой личиной, audit log когда непонятно почему. cluster-admin - только в крайнем случае, дефолтный SA - пустой, люди - через SSO. Дальше RBAC передаёт эстафету admission, который смотрит уже внутрь манифестов.
👍3 ❤️2 🔥 😄 🤔1
Аватара пользователя
echabiron
Сообщения: 1
Зарегистрирован: 15 май 2026, 07:37

Re: RBAC и аутентификация глубже

Сообщение echabiron »

Меня этот sub в токене всегда сбивал, теперь дошло: system:serviceaccount:ns:name и плюс автоматом группа system:serviceaccounts:ns. Стало понятно, на что вешать RoleBinding.
👍 ❤️1 🔥 😄 🤔
Аватара пользователя
rlaw
Сообщения: 1
Зарегистрирован: 18 май 2026, 16:39

Re: RBAC и аутентификация глубже

Сообщение rlaw »

Проверил у себя - на default SA в одном неймспейсе реально висел старый ClusterRoleBinding с лишними правами. can-i --as спас, переехал на отдельный SA с automountServiceAccountToken false. Спасибо за раздел про грабли.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Kustomize и управление конфигурацией без шаблонов
Следующая глава →
Admission и Pod Security: контроль на входе

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: kubernetes rbac как настроить доступы и роли

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость