RBAC: кто что может

Кто угодно с доступом к кластеру может удалить всё — если вы не настроили RBAC.

RBAC (Role-Based Access Control) — модель доступа Kubernetes, где правила (что можно делать с какими ресурсами) собираются в роли, а роли привязываются к пользователям, группам и сервис-аккаунтам.

По умолчанию свежий кластер уже включает RBAC, но это не значит, что он настроен правильно. Очень частая ситуация: разработчику выдают cluster-admin, потому что «так быстрее», и CI-пайплайн ходит в кластер под тем же всемогущим токеном. Один скомпрометированный токен — и атакующий хозяйничает во всём кластере. RBAC нужен, чтобы каждый субъект (человек или процесс) имел ровно те права, что ему действительно нужны, и ни одним больше.

Зачем это на практике

Представьте команду из трёх групп: разработчики деплоят приложения в свой namespace, дежурные смотрят логи во всём кластере, а CI применяет манифесты только в staging. Без RBAC все они либо ничего не могут, либо могут всё. С RBAC вы описываете три набора прав и спокойно спите: даже если токен CI утечёт, атакующий не выйдет за пределы staging и не доберётся до секретов в prod.

Четыре кирпичика RBAC

Модель строится из четырёх объектов. Два описывают что можно, два — кому это можно:

ОбъектОбластьНазначение
Roleодин namespaceнабор разрешённых действий над ресурсами namespace
ClusterRoleвесь кластерправа на кластерные ресурсы (nodes, PV) или общий шаблон
RoleBindingодин namespaceпривязывает Role или ClusterRole к субъектам в namespace
ClusterRoleBindingвесь кластерпривязывает ClusterRole к субъектам во всём кластере

Role: права в одном namespace

Опишем роль, которая разрешает только читать поды и их логи в namespace dev — ни создавать, ни удалять:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: pod-reader
rules:
  - apiGroups: [""]            # "" — core API group (pods, services)
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]

Поле verbs — это и есть разрешённые глаголы: get, list, watch, create, update, patch, delete. Здесь нет create и delete — значит, обладатель роли физически не сможет менять поды.

RoleBinding: кому выдать роль

Роль без привязки не делает ничего. Свяжем её с сервис-аккаунтом ci-bot:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-can-read-pods
  namespace: dev
subjects:
  - kind: ServiceAccount
    name: ci-bot
    namespace: dev
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Применяем оба манифеста обычной командой:

kubectl apply -f pod-reader.yaml -f ci-can-read-pods.yaml

ServiceAccount: личность пода

Люди заходят в кластер по сертификатам или OIDC, а вот процессы внутри подов ходят в API-сервер от имени ServiceAccount. Если под не указал свой SA, он получает default в своём namespace. Создаём отдельный аккаунт под конкретную задачу:

kubectl create serviceaccount ci-bot -n dev

И указываем его в поде, чтобы приложение работало именно с нашими ограниченными правами:

apiVersion: v1
kind: Pod
metadata:
  name: deployer
  namespace: dev
spec:
  serviceAccountName: ci-bot
  automountServiceAccountToken: true   # токен монтируется в под
  containers:
    - name: app
      image: my-deployer:1.4

Если приложению вообще не нужен доступ к API Kubernetes, поставьте automountServiceAccountToken: false — тогда токен не попадёт внутрь контейнера, и красть будет нечего.

Принцип минимальных привилегий

Главное правило безопасности доступа: выдавайте минимум, расширяйте по необходимости. Практические следствия:

  • Начинайте с пустой роли и добавляйте глаголы, когда приложение реально упёрлось в отказ.
  • Предпочитайте Role в namespace, а не ClusterRole на весь кластер.
  • Никогда не выдавайте cluster-admin сервис-аккаунтам приложений и пайплайнов.
  • Избегайте wildcard: resources: ["*"] и verbs: ["*"] сводят весь смысл RBAC к нулю.

Как это работает под капотом

RBAC — это аддитивная модель: разрешено только то, что явно дано хотя бы одним binding. Запрещающих правил в RBAC нет вообще — нельзя «забрать» доступ отдельным deny. Если субъект подпадает под несколько ролей, его итоговые права — объединение всех. Поэтому одна лишняя широкая роль перечёркивает десяток узких.

Когда приходит запрос к API-серверу, он проходит цепочку: сначала аутентификация (кто ты — сертификат, токен SA, OIDC), затем авторизация (RBAC-модуль ищет хоть один разрешающий rule), и только потом admission-контроль. RBAC отвечает строго за второй шаг — «можно ли этому субъекту такой глагол над таким ресурсом».

Частые ошибки

  • cluster-admin «на время», который остаётся навсегда. Временный доступ почти всегда становится постоянным.
  • RoleBinding на встроенную ClusterRole вроде edit или admin без понимания, что именно она открывает (а она открывает и работу с секретами).
  • Привязка к группе system:authenticated — это буквально «любой, кто вошёл в кластер».
  • Забытый automountServiceAccountToken: токен default лежит в каждом поде и часто имеет больше прав, чем кажется.

Проверка прав: kubectl auth can-i

Не угадывайте, что разрешено, — спросите кластер напрямую. Команда kubectl auth can-i отвечает yes или no:

# Могу ли я (текущий пользователь) удалять поды в dev?
kubectl auth can-i delete pods -n dev

# А что может конкретный сервис-аккаунт? Подставляем его через --as
kubectl auth can-i list secrets -n dev \
  --as=system:serviceaccount:dev:ci-bot

# Полный аудит: все действия, доступные субъекту
kubectl auth can-i --list -n dev \
  --as=system:serviceaccount:dev:ci-bot

Вывод:

no
no
Resources   Non-Resource URLs   Resource Names   Verbs
pods        []                  []               [get list watch]
pods/log    []                  []               [get list watch]

Флаг --as (impersonation) позволяет проверить чужие права, не зная их токена. Делайте такой аудит частью ревью манифестов: если --list показывает что-то лишнее, правьте роль до выката.

Итоги

  • RBAC включён по умолчанию, но безопасность даёт только осознанная настройка.
  • Role/ClusterRole описывают права, RoleBinding/ClusterRoleBinding — кому их дать.
  • Процессы в подах ходят в API под ServiceAccount; давайте каждому свой, минимальный.
  • Модель аддитивная: лишняя широкая роль сводит на нет все узкие — избегайте wildcard и cluster-admin.
  • Проверяйте реальные права через kubectl auth can-i и --as ещё до выката.
Проверьте себя
1. Что произойдёт, если создать Role, но не создавать RoleBinding для неё?
ARole автоматически применится ко всем сервис-аккаунтам в namespace
BRole не даёт никаких прав — без RoleBinding она ни к кому не привязана
CKubernetes откажется применять манифест Role без RoleBinding
DRole станет действовать на весь кластер по умолчанию
2. Команда kubectl auth can-i list secrets -n dev --as=system:serviceaccount:dev:ci-bot отвечает на вопрос:
AМожет ли текущий пользователь читать секреты в dev
BМожет ли сервис-аккаунт ci-bot из namespace dev читать секреты в dev
CСоздаст ли она новый сервис-аккаунт ci-bot с правом list secrets
DВключён ли RBAC в кластере
3. Почему в RBAC одна лишняя широкая роль особенно опасна?
ARBAC берёт самую узкую из ролей субъекта
BRBAC аддитивен: итоговые права — объединение всех ролей, а deny-правил нет
CШирокая роль отключает все остальные RoleBinding
DШирокие роли применяются только к группе system:authenticated