Перейти к содержимому

kubectl whoami и проверка прав сервис-аккаунта

При деплое приложения в Kubernetes самая частая ошибка — сервис-аккаунт не может сделать то, что должен. Permission denied при создании секрета, запрет на list pods, отказ на update. kubectl whoami и kubectl auth can-i позволяют быстро понять, кто именно и что именно не может.

Утилита kubectl whoami

Примечание

kubectl whoami — это не встроенная команда kubectl. Это плагин из krew или отдельный бинарь. Установка: kubectl krew install whoami или скачать с GitHub.

После установки команда показывает текущий контекст и ассоциированный сервис-аккаунт:

kubectl whoami

Вывод:

system:serviceaccount:default:myapp

Без плагина ту же информацию даёт:

kubectl auth can-i --list

В начале вывода видно User: system:serviceaccount:default:myapp — это и есть whoami.

kubectl auth can-i: проверка прав произвольного субъекта

Встроенная команда kubectl auth can-i проверяет права без подключения от имени проверяемого субъекта. Синтаксис:

kubectl auth can-i <verb> <resource> --as=<subject>

Формат --as для сервис-аккаунта:

# В пределах namespace
kubectl auth can-i list pods \
  --as=system:serviceaccount:default:myapp

# Кросс-неймспейс
kubectl auth can-i list pods \
  --as=system:serviceaccount:production:myapp \
  -n production

Ответы: yes или no.

Чтобы проверить весь набор прав SA:

kubectl auth can-i --list --as=system:serviceaccount:default:myapp
Подсказка

Комбинация --as с --list — быстрый аудит прав сервис-аккаунта. Вывод содержит таблицу с ресурсами, версиями и списком разрешённых действий.

Флаги kubectl auth can-i

ФлагНазначение
--as <subject>Субъект для проверки
-n <namespace>Namespace субъекта (для SA)
--listВсе права субъекта
--quiet / -qТолько exit code, без вывода
--no-headersБез заголовков таблицы

Exit code: 0 при yes, 1 при no. Это удобно для скриптов:

kubectl auth can-i delete secrets --as=system:serviceaccount:default:myapp
if [ $? -eq 0 ]; then
  echo "SA может удалять секреты — проверь политику"
fi

Привязка ClusterRole к сервис-аккаунту: RoleBinding vs ClusterRoleBinding

Сервис-аккаунт привязывается к роли через два ресурса. Разница — в области действия.

RoleBinding — привязывает Role или ClusterRole к SA в рамках одного namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: myapp-pod-reader
  namespace: default
subjects:
  - kind: ServiceAccount
    name: myapp
    namespace: default
roleRef:
  kind: ClusterRole        # ссылка на ClusterRole
  name: pod-reader        # имя ClusterRole
  apiGroup: rbac.authorization.k8s.io
Примечание

RoleBinding может ссылаться и на Role, и на ClusterRole. В первом случае права ограничены неймспейсом, во втором — работают в пределах неймспейса RoleBinding, но наследуют все правила ClusterRole.

ClusterRoleBinding — привязывает ClusterRole ко всему кластеру:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: myapp-cluster-reader
subjects:
  - kind: ServiceAccount
    name: myapp
    namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-reader
  apiGroup: rbac.authorization.k8s.io
СценарийРесурс
SA нужны права только в одном namespaceRoleBinding → ClusterRole
SA нужны права cluster-wideClusterRoleBinding
Общая роль для разных SA в разных неймспейсахClusterRoleBinding с несколькими subjects

Subject в CSR: как читать и проверять

CSR (Certificate Signing Request) содержит поле spec.username и spec.groups. Для SA это выглядит так:

kubectl get csr <csr-name> -o jsonpath='{.spec}' | jq .

Вывод:

{
  "request": "base64-encoded-certificate-request",
  "username": "system:serviceaccount:default:myapp",
  "groups": ["system:serviceaccount", "system:serviceaccounts", "system:serviceaccounts:default"],
  "uid": "...",
  "extra": {
    "authentication.kubernetes.io/credential-id": ["..."],
    "authentication.kubernetes.io/node-name": ["..."]
  }
}

Три компонента в username через двоеточие: system:serviceaccount:<namespace>:<name>.

Для проверки прав конкретного SA из CSR:

# Извлечь SA из CSR
SUBJECT=$(kubectl get csr <csr-name> -o jsonpath='{.spec.username}')
kubectl auth can-i list pods --as="$SUBJECT"
Предупреждение

CSR создаётся kubelet при подключении node или через ServiceAccount admission. Если SA работает через TokenRequest API (современный способ), CSR не генерируется — токен выдаётся напрямую.

Быстрая проверка в CI/CD

В пайплайне нужно убедиться, что деплой-сервис-аккаунт имеет достаточные права до запуска манифестов:

#!/bin/bash
SA="system:serviceaccount:ci-runner:deployer"
RESOURCES=("pods" "services" "configmaps" "secrets")

for RES in "${RESOURCES[@]}"; do
  kubectl auth can-i create "$RES" --as="$SA" -n "$NAMESPACE" || {
    echo "ERROR: SA не может создавать $RES"
    exit 1
  }
done

echo "Права подтверждены"
kubectl auth can-i get pods --as="$SA" -n "$NAMESPACE"
Подсказка

Добавьте эту проверку после kubectl apply или helm template — ранний exit дешевле, чем упавший pod с ImagePullBackOff из-за отсутствия imagePullSecrets.

Полезный вывод для диагностики в логах CI:

kubectl auth can-i --list --as="system:serviceaccount:default:myapp" --no-headers

Строки без заголовков легко заgrepать на предмет подозрительных wildcards типа */* или */delete.