Kubernetes не хранит пользователей: внутри кластера нет списка логинов и паролей. API-сервер доверяет внешнему подтверждению личности (сертификат, токен, OIDC), а затем решает, что этому субъекту разрешено. Первый шаг называют аутентификацией, второй авторизацией, и по умолчанию авторизацию в кластере закрывает RBAC.
Практический ответ на главный вопрос: чтобы выдать доступ человеку или системе, нужны объекты RBAC и артефакт для подключения. Role или ClusterRole описывают набор разрешений. RoleBinding или ClusterRoleBinding привязывают разрешения к субъекту: User, Group или ServiceAccount. Человеку дополнительно собирают kubeconfig, поду и CI/CD обычно достаточно ServiceAccount с токеном. Проверка результата выполняется командой kubectl auth can-i.
Дальше разобраны все шаги: модель доступа, создание ServiceAccount, настройка Role и ClusterRole, привязка прав, генерация kubeconfig через CertificateSigningRequest и отладка ошибок Forbidden. Примеры рассчитаны на API Kubernetes 1.24 и новее, где токены ServiceAccount больше не создаются автоматически как Secret. Базовый доступ kubectl в кластере, поднятом по инструкции kubeadm на Ubuntu 22.04, настраивается копированием admin.conf в $HOME/.kube/config (руководство по развёртыванию кластера через kubeadm на Ubuntu 22.04). С устройством control plane и объектов API удобнее познакомиться заранее (архитектура кластера Kubernetes и ключевые объекты API).
Как Kubernetes разграничивает доступ: аутентификация, авторизация и RBAC
Аутентификация отвечает на вопрос «кто ты». Авторизация отвечает на вопрос «что тебе можно». API-сервер выполняет эти проверки последовательно для каждого запроса: сначала определяет субъект, затем сверяет действие с правилами.
Субъекты в Kubernetes делятся на три типа:
- User: внешний по отношению к кластеру пользователь. Объекта User в etcd нет, Kubernetes узнаёт его по данным аутентификации.
- Group: объединение пользователей, чаще всего задаётся полем Organization в сертификате или claim в OIDC.
- ServiceAccount: полноценный объект API внутри namespace, предназначенный для процессов в подах и внешних систем вроде CI/CD.
Пользователь появляется из одного из источников:
- клиентский сертификат X.509: Common Name становится именем пользователя, Organization группами;
- bearer-токен статического файла или токен ServiceAccount;
- внешний провайдер OIDC (Keycloak, Okta, Dex и другие);
- аутентифицирующий прокси перед API-сервером.
Авторизацию включают одним или несколькими режимами: Node, ABAC, RBAC, Webhook. RBAC остаётся основным встроенным механизмом и работает через четыре объекта API: Role, ClusterRole, RoleBinding, ClusterRoleBinding. Аналогия простая: Role и ClusterRole это список разрешённых действий, RoleBinding и ClusterRoleBinding это замок, который выдаёт ключ конкретному субъекту.
Чем отличаются User и ServiceAccount
| Критерий | User | ServiceAccount |
|---|---|---|
| Хранение в etcd | Нет, объект не существует | Да, объект API в namespace |
| Аутентификация | Сертификат, токен, OIDC | Токен через TokenRequest API или legacy Secret |
| Типичное использование | Люди с kubeconfig | Поды, CI/CD, операторы |
| Область имени | Задаётся в сертификате или провайдере | system:serviceaccount:имя_пространства:имя |
Разработчику выдавайте kubeconfig с клиентским сертификатом: так он проходит как User и работает от своего имени. CI-раннеру выдавайте ServiceAccount: его идентичность привязана к namespace, а токен можно ограничить по времени жизни.
Зачем RBAC нужен кроме безопасности: порядок и предсказуемость
Без RBAC команда быстро скатывается к выдаче cluster-admin всем подряд. Одна опечатка в скрипте CI с полными правами способна удалить namespace целиком. RBAC даёт три практических эффекта:
- ограничивает зону поражения: CI может менять только deployments в namespace приложения, но не удалять nodes;
- делает права декларативными: манифесты Role и RoleBinding лежат в Git, проходят ревью и версионируются вместе с приложением;
- упрощает аудит: список привязок показывает, кто и что может, без чтения кода.
Создание ServiceAccount и выдача прав для CI/CD
ServiceAccount создаётся в namespace, где работает процесс, которому вы даёте доступ. Для CI/CD это обычно отдельный namespace ci-cd, чтобы права раннера не пересекались с рабочими нагрузками приложений.
Как создать ServiceAccount и привязать к нему Role
kubectl create namespace ci-cd
apiVersion: v1 kind: ServiceAccount metadata: name: ci-runner namespace: ci-cd automountServiceAccountToken: false
kubectl apply -f ci-runner-sa.yaml
Поле automountServiceAccountToken: false означает, что токен не будет автоматически смонтирован в поды с этим ServiceAccount. Для внешнего раннера это правильно: токен выдаётся отдельно и не лежит внутри контейнеров.
Теперь Role с минимальным набором действий:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: ci-cd
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
RoleBinding связывает сервисный аккаунт с ролью:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-runner-deployer
namespace: ci-cd
subjects:
- kind: ServiceAccount
name: ci-runner
namespace: ci-cd
roleRef:
kind: Role
name: deployer
apiGroup: rbac.authorization.k8s.io
kubectl apply -f deployer-role.yaml kubectl apply -f ci-runner-binding.yaml kubectl auth can-i list deployments --as=system:serviceaccount:ci-cd:ci-runner -n ci-cd
Обратите внимание: без verb delete в Role CI не сможет удалить deployment. Если удаление действительно нужно, добавляйте его осознанно, а не на всякий случай. Cluster-wide доступ потребует ClusterRole и ClusterRoleBinding, и это заметно повышает риски, потому что правило начнёт действовать во всех namespace.
Как выдать ServiceAccount токен для CI/CD
С Kubernetes 1.24 Secret с токеном больше не генерируется автоматически при создании ServiceAccount. Токен запрашивают через TokenRequest API, и команда kubectl create token выдаёт его с заданным сроком жизни.
kubectl create token ci-runner -n ci-cd --duration=24h
Команда возвращает короткоживущий JWT. Для некоторых инструментов по-прежнему нужен долгоживущий Secret типа kubernetes.io/service-account-token с аннотацией kubernetes.io/service-account.name. Учтите разницу: такой токен не ротируется сам и хранится в etcd как обычный Secret. Для GitLab и Jenkins предпочтительнее короткий токен, который раннер выпускает перед каждой задачей.
Ограничивайте срок жизни: 24 часа для интерактивной отладки и минуты для одноразовых задач. Просроченный токен не обновляется автоматически, CI должен запрашивать новый при каждом запуске.
Role и ClusterRole: настройка правил доступа
Правила задаются списком rules. Каждое правило содержит apiGroups, resources, verbs и опционально resourceNames:
- apiGroups: группа API. Для pods, services, secrets это пустая строка. Для deployments, statefulsets это apps. Для jobs это batch.
- resources: имена ресурсов во множественном числе: pods, deployments, configmaps.
- verbs: действия get, list, watch, create, update, patch, delete, deletecollection. Подресурсы вроде pods/log и pods/portforward добавляются отдельной строкой с тем же verbs.
- resourceNames: ограничение конкретными объектами. Работает только с verbs, где объект уже известен (get, update, delete), и не работает с list, watch, create.
Разница между Role и ClusterRole на практике
Role действует строго в одном namespace. ClusterRole не привязан к namespace и нужен в двух случаях: для cluster-scoped ресурсов (nodes, persistentvolumes, namespaces) и для повторного использования одного набора прав в разных namespace.
| Свойство | Role | ClusterRole |
|---|---|---|
| Область действия | Один namespace | Весь кластер или cluster-scoped ресурсы |
| Тип ресурсов | Только namespace-ресурсы | Namespace- и cluster-scoped ресурсы |
| Привязка | RoleBinding | RoleBinding (в одном namespace) или ClusterRoleBinding (везде) |
Пример. Разработчику нужен доступ к pods и logs в dev, это Role в namespace dev. Администратору нужен доступ к nodes, это ClusterRole. ClusterRole можно привязать через RoleBinding, и тогда права будут действовать только в одном namespace.
Готовые ClusterRole: view, edit, admin, cluster-admin
Kubernetes поставляет набор встроенных ClusterRole, которые покрывают типовые сценарии.
| Роль | Что даёт | Кому подходит |
|---|---|---|
| view | Чтение большинства объектов namespace | Разработчики, аналитики, наблюдатели |
| edit | Чтение и изменение объектов, без управления RBAC | Разработчики в dev |
| admin | Полный доступ в namespace, включая управление Role и RoleBinding | Лид команды, ответственный за namespace |
| cluster-admin | Полный доступ ко всему кластеру | Только аварийный доступ |
Роль edit в ряде версий не даёт доступ к secrets, а роль view не даёт читать secrets вообще. Проверяйте фактический набор прав командой kubectl auth can-i --list, а не по названию роли. Для точного контроля пишите кастомные Role по принципу наименьших привилегий (полное руководство по созданию ролей в Kubernetes).
RoleBinding и ClusterRoleBinding: привязка прав к пользователям и сервисам
RoleBinding связывает Role или ClusterRole с субъектами и действует в пределах namespace. ClusterRoleBinding связывает ClusterRole с субъектами на весь кластер. В субъектах указывают один из трёх kind: User, Group, ServiceAccount.
Как привязать права к пользователю через RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-ivanov
namespace: dev
subjects:
- kind: User
name: ivanov@company.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
Для ServiceAccount в subject дополнительно указывают namespace:
subjects:
- kind: ServiceAccount
name: ci-runner
namespace: ci-cd
kubectl auth can-i get pods --as=ivanov@company.com -n dev kubectl auth can-i get pods --as=ivanov@company.com --as-group=developers -n dev
Привязку можно создать и без манифеста:
kubectl create rolebinding developer-ivanov --role=developer --user=ivanov@company.com -n dev kubectl create clusterrolebinding node-reader-group --clusterrole=node-reader --group=ops
Как привязать ClusterRole через RoleBinding для namespace-доступа
Практичный приём: взять ClusterRole и привязать её через RoleBinding. Тогда набор прав один, а действует он только в указанном namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-view
namespace: dev
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
Так вы не дублируете права в каждом namespace: достаточно создать одинаковые RoleBinding в dev, staging и prod. Расширенные шаблоны привязок для команд и CI собраны в практическом руководстве по RBAC (RBAC в Kubernetes 2026: Role, RoleBinding и ClusterRoleBinding).
Генерация kubeconfig для пользователя
kubeconfig это файл с тремя блоками: cluster (адрес API-сервера и CA), user (данные аутентификации), context (связка cluster плюс user плюс namespace). Переключение между кластерами и пользователями идёт через контексты.
Создание kubeconfig через CertificateSigningRequest
Порядок для пользователя developer@example.com начинается с ключа и запроса на сертификат:
openssl genrsa -out developer.key 2048 openssl req -new -key developer.key -out developer.csr -subj "/CN=developer@example.com/O=developers"
Поле O задаёт группу developers, поле CN имя пользователя. Затем CSR отправляется в кластер:
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: developer
spec:
request: BASE64_CSR
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 7776000
usages:
- client auth
Поле request заполняется содержимым developer.csr в base64: cat developer.csr | base64 | tr -d "\n". Поле expirationSeconds задаёт срок действия, 7776000 секунд это 90 дней. Дальше:
kubectl apply -f developer-csr.yaml
kubectl certificate approve developer
kubectl get csr developer -o jsonpath='{.status.certificate}' | base64 -d > developer.crt
Сборка kubeconfig:
kubectl config set-cluster my-cluster --server=https://API-SERVER:6443 --certificate-authority=/path/ca.crt --embed-certs=true --kubeconfig=developer.kubeconfig kubectl config set-credentials developer@example.com --client-certificate=developer.crt --client-key=developer.key --embed-certs=true --kubeconfig=developer.kubeconfig kubectl config set-context developer@my-cluster --cluster=my-cluster --user=developer@example.com --namespace=dev --kubeconfig=developer.kubeconfig kubectl config use-context developer@my-cluster --kubeconfig=developer.kubeconfig
Проверка нового доступа:
kubectl --kubeconfig=developer.kubeconfig auth can-i get pods -n dev
Флаг --embed-certs=true встраивает сертификаты в файл, поэтому передавать отдельные .crt и .key не потребуется. Передавайте kubeconfig по защищённому каналу: файл содержит приватный ключ пользователя.
Сборка kubeconfig для ServiceAccount
TOKEN=$(kubectl create token ci-runner -n ci-cd --duration=24h) kubectl config set-cluster my-cluster --server=https://API-SERVER:6443 --certificate-authority=/path/ca.crt --embed-certs=true --kubeconfig=ci.kubeconfig kubectl config set-credentials ci-runner --token="$TOKEN" --kubeconfig=ci.kubeconfig kubectl config set-context ci --cluster=my-cluster --user=ci-runner --namespace=ci-cd --kubeconfig=ci.kubeconfig kubectl config use-context ci --kubeconfig=ci.kubeconfig
kubeconfig с сертификатом живёт до истечения сертификата. kubeconfig с токеном ServiceAccount перестаёт работать вместе с токеном, поэтому CI запрашивает новый перед задачей. Не храните такие файлы в открытом виде и не коммитьте их в Git.
Проверка прав и отладка ошибок доступа
Основной инструмент проверки, kubectl auth can-i. Он не меняет кластер и показывает, разрешено ли действие конкретному субъекту.
Как использовать kubectl auth can-i
kubectl auth can-i get pods --as=ivanov@company.com -n dev kubectl auth can-i create deployments --as=system:serviceaccount:ci-cd:ci-runner -n ci-cd kubectl auth can-i "*" "*" --as=ivanov@company.com -n dev kubectl auth can-i --list --as=ivanov@company.com -n dev
Вывод yes или no. Флаг --list выводит все правила для субъекта в namespace с указанием источника, например конкретной привязки. Для групп добавьте --as-group=developers. Если проверяете ServiceAccount, используйте полную строку system:serviceaccount:имя_пространства:имя.
Команда kubectl auth reconcile применяет RBAC-манифесты и приводит существующие роли и привязки к нужному виду, добавляя недостающие правила и субъекты. Это предсказуемый способ накатить обновления RBAC из Git.
Типичные ошибки RBAC и как их исправить
- RoleBinding в другом namespace. Привязка действует только в своём namespace. Проверьте metadata.namespace и суффикс -n в командах.
- Несовпадение kind в subject. Для пода и CI нужен kind: ServiceAccount, для сотрудника kind: User. Смешение даёт запрет без явной ошибки в логах.
- Опечатка в roleRef.name. Привязка ссылается на несуществующую Role. Проверьте: kubectl get role deployer -n ci-cd.
- Не хватает verb. Частая ситуация: есть get и list, но нет watch, и контроллер не видит изменений. Добавьте нужное действие в rules.
- ClusterRole без привязки. Сама по себе ClusterRole ничего не разрешает, нужна привязка: RoleBinding для одного namespace или ClusterRoleBinding для всего кластера.
Порядок диагностики при Forbidden: проверьте subject и namespace в привязке, затем сам roleRef, затем verbs и resources в правилах, и только после этого ищите причину в клиенте. Полный список избыточных привязок и открытых точек входа ищется в рамках аудита (аудит безопасности Kubernetes-кластера).
Безопасность доступа: принцип наименьших привилегий и ротация
Принцип наименьших привилегий означает: выдавайте минимальный набор verbs и resources, достаточный для задачи. На практике это отдельный ServiceAccount под каждую задачу, отдельная Role под каждую команду и срок жизни токена по необходимости.
Как ограничить cluster-admin и избежать избыточных прав
cluster-admin даёт полный доступ ко всему кластеру, включая secrets и узлы. Проверьте, кому он сейчас выдан:
kubectl get clusterrolebinding -o yaml | grep -B5 -A10 "cluster-admin"
Типовые меры: сузить привязки до namespace через RoleBinding с ClusterRole, заменить привязки разработчиков на view или кастомную Role, для CI использовать только namespace-scoped права. cluster-admin стоит оставить для аварийного доступа и выдавать его по запросу, а не постоянно.
RBAC разграничивает действия через API, но не мешает скомпрометированному поду использовать смонтированный токен для чтения секретов. Отключайте автоподстановку токена там, где он не нужен (automountServiceAccountToken: false), и дополняйте RBAC сетевыми политиками и ограничениями подов. Безопасное хранение учётных данных разобрано отдельно (хранение паролей в Kubernetes: Secrets, External Secrets и Vault).
Ротация токенов и сертификатов
Сроки жизни артефактов доступа:
- токены ServiceAccount через TokenRequest API: задавайте --duration, для CI разумно 24 часа или меньше, для одноразовых задач минуты;
- долгоживущие Secret-токены ServiceAccount: не ротируются автоматически, при компрометации удаляйте Secret и создавайте новый;
- клиентские сертификаты пользователей: срок задаётся expirationSeconds в CSR, ориентир 90 дней;
- join-токены kubeadm для добавления нод: живут 24 часа, при необходимости генерируйте новый командой kubeadm token create --print-join-command (руководство по кластеру Kubernetes на Ubuntu 22.04).
Просроченный сертификат пользователя не обновляется сам, его перевыпускают через новый CSR. Для доступа людей удобнее внешний OIDC-провайдер: сессии короткие, отзыв доступа делается в одном месте.
Шпаргалка: команды и манифесты для управления доступом
# ServiceAccount kubectl create serviceaccount ci-runner -n ci-cd # Роли и привязки kubectl create role deployer --verb=get,list,watch,create,update,patch --resource=deployments -n ci-cd kubectl create rolebinding ci-runner-deployer --role=deployer --serviceaccount=ci-cd:ci-runner -n ci-cd kubectl create clusterrole node-reader --verb=get,list,watch --resource=nodes kubectl create clusterrolebinding node-reader-group --clusterrole=node-reader --group=ops # Токен ServiceAccount kubectl create token ci-runner -n ci-cd --duration=24h # Проверка прав kubectl auth can-i get pods --as=ivanov@company.com -n dev kubectl auth can-i --list --as=system:serviceaccount:ci-cd:ci-runner -n ci-cd # Контексты kubeconfig kubectl config set-cluster my-cluster --server=https://API-SERVER:6443 --certificate-authority=/path/ca.crt --embed-certs=true kubectl config set-credentials developer@example.com --token=TOKEN kubectl config set-context developer@my-cluster --cluster=my-cluster --user=developer@example.com --namespace=dev kubectl config use-context developer@my-cluster
apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-runner
namespace: ci-cd
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: ci-cd
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-runner-deployer
namespace: ci-cd
subjects:
- kind: ServiceAccount
name: ci-runner
namespace: ci-cd
roleRef:
kind: Role
name: deployer
apiGroup: rbac.authorization.k8s.io
Команды опираются на API Kubernetes 1.24 и новее. В версиях до 1.24 токен ServiceAccount создаётся автоматически как Secret, и команда kubectl create token может отсутствовать, поэтому обновляйте kubectl до версии кластера. Для кластера, собранного через kubeadm на Ubuntu 22.04, базовые шаги подключения описаны в отдельном руководстве (развёртывание кластера Kubernetes через kubeadm).