← Весь справочник

Kubernetes: ServiceAccount, Role, RoleBinding

Права в Kubernetes — ServiceAccount, Role и ClusterRole, правила apiGroups/resources/verbs, привязки и токен пода

RBAC (role-based access control) решает, кому что можно делать в API Kubernetes. Схема из трёх частей:

ЧастьОбъектыЧто описывает
КтоServiceAccount (для программ в подах), User и Group (люди; в Kubernetes объектами не являются — приходят из сертификата или внешней аутентификации)субъект
Что можноRole (в одном неймспейсе), ClusterRole (во всём кластере или как шаблон)набор правил «ресурсы × действия»
СвязкаRoleBinding (в неймспейсе), ClusterRoleBinding (во всём кластере)выдаёт роль субъектам

Правила только разрешают: запрещающих правил нет, по умолчанию запрещено всё. Права субъекта — объединение всех привязанных к нему ролей.

Синтаксис

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app
  namespace: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: dev
rules:
  - apiGroups: [""]               # "" — базовая группа: pods, services, configmaps…
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-pod-reader
  namespace: dev
subjects:
  - kind: ServiceAccount
    name: app
    namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-reader

Без кластера генерируются ServiceAccount, RoleBinding и ClusterRoleBinding (kubectl create serviceaccount|rolebinding|clusterrolebinding … --dry-run=client -o yaml). kubectl create role и create clusterrole обращаются к серверу даже с --dry-run=client — роли пиши руками или собирай через yq.

ServiceAccount

ПолеЧто значитПо умолчанию / заметки
metadata.name, metadata.namespaceимя; ServiceAccount всегда живёт в неймспейсев каждом неймспейсе есть ServiceAccount default
automountServiceAccountTokenмонтировать ли токен в поды с этим аккаунтомtrue
imagePullSecrets[{name: секрет}] — добавляются ко всем подам с этим аккаунтом

В поде:

Поле spec подаЧто значитПо умолчанию
serviceAccountNameот имени какого ServiceAccount под обращается к APIdefault
automountServiceAccountTokenмонтировать ли токен; перекрывает настройку ServiceAccountкак у ServiceAccount

Токен монтируется в /var/run/secrets/kubernetes.io/serviceaccount/: файлы token (JWT), ca.crt (сертификат API-сервера), namespace. Токен короткоживущий, kubelet обновляет его сам. В API ServiceAccount выглядит как пользователь system:serviceaccount:<неймспейс>:<имя> и входит в группы system:serviceaccounts и system:serviceaccounts:<неймспейс>.

Role и ClusterRole

RoleClusterRole
Где действуеттолько в своём неймспейсе (metadata.namespace)во всём кластере — или в одном неймспейсе через RoleBinding
Ресурсынеймспейсные: pods, deployments, secrets…ещё и кластерные: nodes, namespaces, persistentvolumes, clusterroles
Не-ресурсные URLнетnonResourceURLs: ["/healthz", "/metrics"]

Поля элемента rules[]:

ПолеЧто значитПримеры
apiGroupsгруппа API — часть apiVersion до /"" (v1: pods, services, configmaps, secrets, serviceaccounts), apps (deployments, statefulsets, daemonsets, replicasets), batch (jobs, cronjobs), networking.k8s.io (ingresses, networkpolicies), autoscaling, rbac.authorization.k8s.io, "*" — все
resourcesресурсы во множественном числе, строчнымиpods, deployments; подресурсы: pods/log, pods/exec, deployments/scale; "*" — все
verbsдействиясм. таблицу ниже; "*" — все
resourceNamesтолько объекты с этими именами["app-config"]; с create и deletecollection не работает
nonResourceURLsURL вне ресурсов (только ClusterRole)["/healthz", "/version"] с verbs: ["get"]

Действия (verbs) и чему они соответствуют:

VerbЗапросПример команды
getпрочитать один объектkubectl get pod web-1, describe
listсписокkubectl get pods
watchследить за изменениямиkubectl get pods -w
createсоздатьkubectl create, apply нового объекта; create на pods/exec — kubectl exec
updateзаменить целикомkubectl replace
patchизменить частичноkubectl apply существующего, scale, label, edit
deleteудалить одинkubectl delete pod web-1
deletecollectionудалить многиеkubectl delete pods --all

kubectl apply нужны сразу get, create и patch.

У ClusterRole есть ещё aggregationRule: {clusterRoleSelectors: [{matchLabels: …}]} — роль автоматически собирает правила других ClusterRole с этими метками. Так устроены встроенные admin, edit, view.

RoleBinding и ClusterRoleBinding

ПолеЧто значитЗаметки
subjects[].kindServiceAccount / User / Group
subjects[].nameимя субъектадля групп ServiceAccount: system:serviceaccounts:dev
subjects[].namespaceнеймспейс ServiceAccountобязательно для ServiceAccount
subjects[].apiGrouprbac.authorization.k8s.io для User и Group; для ServiceAccount — не указывать ("")
roleRef.kindRole или ClusterRole
roleRef.nameимя роли
roleRef.apiGroupвсегда rbac.authorization.k8s.io

Какие сочетания возможны:

ПривязкаРольРезультат
RoleBindingRole из того же неймспейсаправа в этом неймспейсе
RoleBindingClusterRoleправа ClusterRole, но только в неймспейсе привязки — удобно описать роль один раз и выдавать по неймспейсам
ClusterRoleBindingClusterRoleправа во всех неймспейсах и на кластерные ресурсы
ClusterRoleBindingRoleневозможно

Встроенные ClusterRole: view (чтение без секретов), edit (изменение большинства объектов, без ролей), admin (полный доступ в неймспейсе, включая роли), cluster-admin (всё).

Примеры

# ServiceAccount и привязки — генераторы работают офлайн
kubectl create serviceaccount ci -n dev --dry-run=client -o yaml > sa.yaml
kubectl create rolebinding ci-edit -n dev --clusterrole=edit \
  --serviceaccount=dev:ci --dry-run=client -o yaml > rb.yaml
kubectl create rolebinding devs-view -n dev --clusterrole=view \
  --user=alice --group=developers --dry-run=client -o yaml
kubectl create clusterrolebinding ci-nodes --clusterrole=node-reader \
  --serviceaccount=dev:ci --dry-run=client -o yaml > crb.yaml

# Role: `kubectl create role` требует кластер — собираем через yq
yq -n '
  .apiVersion = "rbac.authorization.k8s.io/v1" |
  .kind = "Role" |
  .metadata = {"name": "deployer", "namespace": "dev"} |
  .rules = [
    {"apiGroups": ["apps"], "resources": ["deployments"], "verbs": ["get", "list", "create", "patch"]},
    {"apiGroups": [""], "resources": ["pods", "pods/log"], "verbs": ["get", "list"]}
  ]' > role.yaml

# привязать её вместо ClusterRole — roleRef правим так же
yq '.roleRef.kind = "Role" | .roleRef.name = "deployer" | .metadata.name = "ci-deployer"' rb.yaml > rb-role.yaml

# запретить монтирование токена всем подам этого аккаунта
yq -i '.automountServiceAccountToken = false' sa.yaml

kubeconform -strict -summary sa.yaml rb.yaml crb.yaml role.yaml rb-role.yaml

# в кластере (здесь не сработают):
# kubectl auth can-i create deployments -n dev --as=system:serviceaccount:dev:ci
# kubectl auth can-i --list -n dev --as=system:serviceaccount:dev:ci
# kubectl create token ci -n dev --duration=1h

ClusterRole для чтения узлов и неймспейсов и привязка на весь кластер:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader               # у кластерных объектов нет namespace
rules:
  - apiGroups: [""]
    resources: ["nodes", "namespaces"]
    verbs: ["get", "list", "watch"]
  - nonResourceURLs: ["/healthz", "/version"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: monitoring-node-reader
subjects:
  - kind: ServiceAccount
    name: monitoring
    namespace: observability
  - kind: Group
    name: sre
    apiGroup: rbac.authorization.k8s.io
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: node-reader

Доступ к одному конкретному ConfigMap и под без токена:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: read-app-config
  namespace: dev
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config"]
    verbs: ["get", "watch"]
---
apiVersion: v1
kind: Pod
metadata:
  name: static-site
  namespace: dev
spec:
  serviceAccountName: default
  automountServiceAccountToken: false   # приложению API не нужен — токен не монтируем
  containers:
    - name: web
      image: nginx:1.27

Подводные камни

  • Неверный apiGroups — самая частая ошибка. Deployment живёт в apps, а не в ""; Ingress — в networking.k8s.io; CronJob — в batch. Группа — это часть apiVersion до /, а у apiVersion: v1 группа пустая: "".
  • Имя ресурса — во множественном числе и строчными. Pod, pod, Deployment в resources ничего не дадут: нужно pods, deployments. Схема это не проверяет.
  • Подресурсы — отдельно. Право на pods не даёт читать логи и делать exec: нужны pods/log (get) и pods/exec (create).
  • list открывает содержимое. list на secrets возвращает секреты целиком, не только имена. Права на секреты выдавай как можно уже.
  • roleRef нельзя изменить у существующей привязки: только удалить и создать заново. subjects менять можно.
  • namespace у ServiceAccount в subjects обязателен, иначе API отвергнет привязку. Схема это не ловит.
  • --serviceaccount в kubectl create rolebinding — в виде неймспейс:имя. Просто --serviceaccount=ci — ошибка.
  • Role из другого неймспейса RoleBinding не найдёт: roleRef ищет Role только в своём неймспейсе.
  • ClusterRoleBinding на edit или admin выдаёт права во всех неймспейсах. Если нужен один — RoleBinding на ту же ClusterRole.
  • Нельзя выдать больше, чем есть у тебя. Создать Role или привязку с правами, которых у тебя самого нет, API не даст (без особого verb escalate/bind).
  • Токен смонтирован, даже если программе API не нужен. Любой, кто получил shell в контейнере, может использовать его права. Где API не нужен — automountServiceAccountToken: false.
  • default ServiceAccount используют все поды без serviceAccountName. Выдал права default — их получили все такие поды неймспейса. Для приложения заводи свой ServiceAccount.
  • Ошибки доступа видны только в кластере: Forbidden: User "system:serviceaccount:dev:ci" cannot list resource "pods" … in the namespace "dev". Из сообщения сразу видно, какого verb, ресурса и неймспейса не хватает.

См. также

kubectl · kubeconform · yq · k8s-workloads · k8s-config · sudo · chmod