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

Kubernetes pods/exec

· 8 мин. чтения

Kubernetes pods/exec #

pods/exec — удобный способ выполнять команды внутри контейнера для отладки и администрирования. Но вот вам сходу задача: как вы считаете, насколько безопасна вот такая роль в Kubernetes, которую можно выдать любому пользователю, предположив абсурдную ситуацию, что ресурс Secret в кластере не используется?

pods/exec audit
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-role
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["get"]

Если вы ответили «безопасная», то вы не одиноки — и вы предоставили бы доступ к pods/exec любому пользователю в кластере, конечно если бы создали сверху ClusterRolebinding.

Оглавление

Что изменилось

На самом деле — ничего. Проблеме уже больше 7 лет, но о ней мало кто знает. Проблема заключается в том, что в головах умных мужей укоренилась мысль, что доступ к pods/exec можно дать вот таким простым правилом:

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-role
rules:
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]

В принципе вы были бы правы, но, как показала практика — не только =)

Первое упоминание о проблеме мне помогли найти ребята из ever_secure:

kubernetes RBAC role verbs to exec to pod

I had to add the verb "get" to my pods/exec section as well since the client library I'm using is doing an http GET to negotiate a websocket first. Using kubectl it sends an http POST and requires the "create" verb in that case. It may be worth updating this example to include the "get" verb.

То есть, уже 7 лет назад был тревожный звоночек: kubectl exec может работать не только через POST, но и через GET.

Это окончательно подтвердилось после перехода kubectl на WebSocket в версии 1.31, где теперь kubectl exec использует GET для установления соединения, а не POST по умолчанию.

Источник: Streaming Transitions from SPDY to WebSockets

Влияние на kubectl и RBAC

Чтобы понять, почему теперь нужен get, надо разобраться, как HTTP-методы отображаются в Kubernetes verbs:

HTTP-методKubernetes verb
GETget
POSTcreate
PUTupdate
DELETEdelete

Некоторые клиенты используют GET для установки WebSocket-соединения с pods/exec, а не POST, как kubectl. Поэтому для работы exec может потребоваться не только create, но и get в RBAC.

Проверка на Minikube

Кластер и контексты

minikube -p 13201 start --kubernetes-version=1.32.1 --ssh-port=13201
kubectl get nodes
NAME     STATUS   ROLES           AGE   VERSION
13201 Ready control-plane 60s v1.32.1

cluster-admin и anonymous

export KUBECONFIG=cluster-admin
minikube -p 13201 update-context
export KUBECONFIG=anonymous-view
minikube -p 13201 update-context
sed -i '/^[[:space:]]*users:/,$d' anonymous-view

Pod и проверки exec

kubectl \
--kubeconfig=cluster-admin \
apply -f - <<EOF
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
EOF
kubectl --kubeconfig=cluster-admin get po
NAME    READY   STATUS    RESTARTS   AGE
nginx 1/1 Running 0 23s

exec: cluster-admin vs anonymous

kubectl --kubeconfig=cluster-admin exec -it nginx -- sh
# ls -al
total 76
drwxr-xr-x 1 root root 4096 Aug 18 15:09 .
drwxr-xr-x 1 root root 4096 Aug 18 15:09 ..
kubectl --kubeconfig=anonymous-view exec -it nginx -- sh
Error from server (Forbidden): pods "nginx" is forbidden: User "system:anonymous" cannot get resource "pods" in API group "" in the namespace "default"

Добавляем доступ только на get

примечание

Просьба обратить внимание, что вместе с pods/exec добавляется доступ к pods, иначе exec не будет работать.

kubectl \
--kubeconfig=cluster-admin \
apply -f - <<EOF
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: anonymous-view
rules:
- apiGroups: [""]
resources:
- pods
- pods/exec
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: anonymous-view
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: anonymous-view
subjects:
- kind: User
name: system:anonymous
apiGroup: rbac.authorization.k8s.io
EOF
clusterrole.rbac.authorization.k8s.io/anonymous-view created
clusterrolebinding.rbac.authorization.k8s.io/anonymous-view created
kubectl --kubeconfig=anonymous-view exec -it nginx -- sh
# ls -la
total 76
drwxr-xr-x 1 root root 4096 Aug 18 15:09 .
drwxr-xr-x 1 root root 4096 Aug 18 15:09 ..
-rwxr-xr-x 1 root root 0 Aug 18 15:09 .dockerenv
drwxr-xr-x 2 root root 4096 Mar 26 2019 bin
drwxr-xr-x 2 root root 4096 Feb 3 2019 boot
drwxr-xr-x 5 root root 360 Aug 18 15:09 dev

Демонстрация (kubectl 1.33.3 vs 1.29.0)

kubectl v1.33.3

kubectl version
Client Version: v1.33.3
kubectl exec -it nginx -- sh
****** verb="GET" url="/api/v1/namespaces/default/pods/incloud-web-incloud-web-chart-d99877d74-27tcd/exec?command=sh&container=nginx&stdin=true&stdout=true&tty=true" ******
/ $ ls -al
total 76
drwxr-xr-x 1 root root 4096 Aug 16 20:43 .
drwxr-xr-x 1 root root 4096 Aug 16 20:43 ..
drwxr-xr-x 2 root root 4096 Jul 15 10:42 bin
drwxr-xr-x 5 root root 360 Aug 16 20:43 dev

kubectl v1.29.0

kubectl version
Client Version: v1.29.0
kubectl exec -it nginx -- sh
****** POST https://api/v1/namespaces/default/pods/incloud-web-incloud-web-chart-d99877d74-27tcd/exec?command=sh&container=nginx&stdin=true&stdout=true&tty=true ******
Error from server (Forbidden): pods "nginx" is forbidden:
User "system:anonymous" cannot get resource "pods" in API group "" in the namespace "default"
Внимание!

В данном примере мы хотели бы показать поведение kubectl exec в разных версиях клиента. При одной и тоже конфигурации кластера и ролей. Как видно поведение отличается, и в зависимости от версии клиента может потребоваться разная RBAC конфигурация.


Kubernetes Audit

{
"annotations": {
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"anonymous-view-all\" of ClusterRole \"anonymous-view\" to User \"system:anonymous\""
},
"apiVersion": "audit.k8s.io/v1",
"auditID": "142e967e-4958-4a77-b691-2f71e26ba6ec",
"kind": "Event",
"level": "Request",
"objectRef": {
"apiVersion": "v1",
"name": "nginx",
"namespace": "default",
"resource": "pods",
"subresource": "exec"
},
"requestURI": "/api/v1/namespaces/default/pods/nginx/exec?...",
"responseStatus": {
"code": 101,
"metadata": {}
},
"sourceIPs": ["10.0.0.33"],
"stage": "ResponseComplete",
"stageTimestamp": "2025-08-15T16:46:50.296700Z",
"user": {
"groups": ["system:unauthenticated"],
"username": "system:anonymous"
},
"userAgent": "Mozilla/5.0 (...) Safari/537.36",
"verb": "get"
}

Итоги и рекомендации

К сожалению нельзя знать все на свете, но можно стараться минимизировать риски.

Проверяйте RBAC, не доверяйте слепо ролям с * в resources и verbs.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create", "get"]
Опасный паттерн

Лучшие политики — явные. Не используйте * в resources или verbs. Это спасёт вас от неожиданных сюрпризов.