Ошибка авторизации для kubectl exec часто выглядит нелогично:
cannot create resource "pods/exec"
Pod уже существует. Команда не создаёт новый Pod и не изменяет его спецификацию. Поэтому ожидаемым разрешением кажется get или update, но Kubernetes проверяет create.
Причина в том, что RBAC описывает операции Kubernetes API, а не буквальный смысл команд kubectl.
exec — отдельный подресурс
kubectl exec обращается не к ресурсу pods напрямую. Команда вызывает подресурс pods/exec:
/api/v1/namespaces/<namespace>/pods/<pod>/exec
В RBAC ресурс и его подресурсы указываются отдельно:
resources:
- pods
- pods/log
- pods/exec
Разрешение get на pods не даёт доступ к pods/exec. Это разные точки API с разными операциями и рисками.
get pods позволяет получить описание Pod. get pods/log позволяет прочитать его логи. create pods/exec позволяет запустить новый сеанс выполнения команды внутри контейнера.
Откуда взялся create
Kubernetes преобразует методы HTTP в собственные глаголы авторизации:
| Метод HTTP | Verb Kubernetes |
|---|---|
GET | get, list или watch |
POST | create |
PUT | update |
PATCH | patch |
DELETE | delete или deletecollection |
Исторически kubectl exec открывал потоковое соединение через SPDY. Начальный запрос к pods/exec использовал HTTP POST, поэтому API server преобразовывал его в проверку:
create pods/exec
create здесь не означает создание нового Pod или другого сохраняемого ресурса Kubernetes. Он разрешает начать новую операцию через подресурс.
Это важное различие. Глагол RBAC описывает способ обращения к API. Он не гарантирует, что результатом станет новый объект в etcd.
Что изменил переход на WebSocket
С Kubernetes 1.31 kubectl по умолчанию использует WebSocket вместо SPDY для exec, attach, cp и port-forward.
Рукопожатие WebSocket начинается с HTTP GET:
GET /api/v1/namespaces/<namespace>/pods/<pod>/exec
Connection: Upgrade
Upgrade: websocket
Из-за этого прежнее объяснение через соответствие POST и create перестало описывать всю картину. Во время перехода WebSocket-запрос к pods/exec мог проверяться как get, хотя тот же вызов через SPDY требовал create.
Одинаковая операция получала разные требования RBAC в зависимости от транспортного протокола.
В Kubernetes 1.35 появилась включённая по умолчанию feature gate AuthorizePodWebsocketUpgradeCreatePermission. При WebSocket-подключении API server выполняет дополнительную проверку create для следующих подресурсов:
pods/execpods/attachpods/portforward
Так Kubernetes вернул единое правило: смена потокового протокола не должна превращать активную операцию в разрешение на чтение.
Как выглядит Role
Типичная Role для оператора, который просматривает Pod и при необходимости запускает в нём команды:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-debugger
namespace: example
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
Разрешения разделены намеренно.
Чтение Pod и логов не даёт возможности выполнять команды в контейнере. Доступ к exec можно выдать отдельно только тем пользователям и ServiceAccount, которым он действительно нужен.
Это важно ещё и потому, что Kubernetes RBAC не ограничивает команды внутри открытой exec-сессии. Получив доступ к pods/exec, пользователь может выполнять любые команды, доступные процессу контейнера.
Как проверить разрешение
Проверить доступ к подресурсу можно без запуска команды:
kubectl auth can-i create pods \
--subresource=exec \
--namespace=example
Отдельно следует проверить доступ к самому Pod:
kubectl auth can-i get pods \
--namespace=example
Для проверки другого пользователя или ServiceAccount можно использовать --as:
kubectl auth can-i create pods \
--subresource=exec \
--namespace=example \
--as=system:serviceaccount:example:debugger
Результат yes для get pods не говорит ничего о доступе к pods/exec. Проверять нужно именно сочетание ресурса, подресурса и глагола.
Вывод
kubectl exec требует create не потому, что создаёт новый Pod. Команда обращается к активному подресурсу pods/exec и начинает новый сеанс выполнения команды.
Исторически create следовал из HTTP POST, который использовал SPDY. После перехода на WebSocket с GET-рукопожатием Kubernetes сохранил ту же модель с помощью отдельной проверки авторизации.
Поэтому глаголы RBAC не стоит выводить из названия команды или предположения о том, изменяется ли объект. Надёжнее определить ресурс, подресурс и операцию, которую API server авторизует для конкретного запроса.
