Управление пользователями и доступом в Kubernetes: RBAC, ServiceAccount и kubeconfig | AdminWiki

Управление пользователями и доступом в Kubernetes: RBAC, ServiceAccount и kubeconfig

18 сентября 2026 13 мин. чтения
Содержание статьи

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

КритерийUserServiceAccount
Хранение в 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.

СвойствоRoleClusterRole
Область действияОдин namespaceВесь кластер или cluster-scoped ресурсы
Тип ресурсовТолько namespace-ресурсыNamespace- и cluster-scoped ресурсы
ПривязкаRoleBindingRoleBinding (в одном 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 и как их исправить

  1. RoleBinding в другом namespace. Привязка действует только в своём namespace. Проверьте metadata.namespace и суффикс -n в командах.
  2. Несовпадение kind в subject. Для пода и CI нужен kind: ServiceAccount, для сотрудника kind: User. Смешение даёт запрет без явной ошибки в логах.
  3. Опечатка в roleRef.name. Привязка ссылается на несуществующую Role. Проверьте: kubectl get role deployer -n ci-cd.
  4. Не хватает verb. Частая ситуация: есть get и list, но нет watch, и контроллер не видит изменений. Добавьте нужное действие в rules.
  5. 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).

Поделиться:
Сохранить гайд? В закладки браузера