На первый взляд система RBAC в k8s достаточно простая и безопасная, но многие забывают или не знают некоторых осоюенностей В kubernetes есть аналог sudo, благодаря которому можно обойти ограничения в назначенных ролях и получить доступ туда, куда не предполагалось и прочитать скеретные данные или даже получить полный контроль над кластером. И эта возможность есть у любого юзера с дефолтной кластерролью edit. В этой статье попробуем разобраться что это такое и как защититься от такого легального повышения привилегий.
From first view Kubernetes RBAC is simple and secure. While you don’t know some nuances. Kubernetes has its own sudo. It allows to break restrictions in roles and get access to hidden areas. Anyone with default clusterrole edit can do it. This article helps you understand what is it and how to prevent such violations
Kubernetes Role Based Access Model
There are several approaches to restrict access to resources:
- ABAC - Attribute Based Access Control
- RBAC - Role Based Access Control
- PBAC - Policy Based Access Control
- DAC - Discretionary Access Control
- MAC - Mandatory Access Control
- MLS/MCS - Multi Level Security / Multi Category Security
Kubernetes может использовать модели доступа: Node, ABAC, RBAC, WebHook. Чаще всего приходится работать с ролевой моделью доступа - RBAC. В ней есть несколько взаимосвязанных сущностей:
- роль, описывающая доступ к ресурсам
- пользователь (это может быть User, Group или ServiceAccount)
- Rolebinding - то, что объединяет роли и пользователей
На самом деле этих сущностей немного больше. Рассмотрим их детальней. Про RBAC хорошо написано в документации k8s и я не буду пытаться написать ещё раз. Просто рассмотрим то, без чего остальная статья не имеет смысла. Если читатель знает концепции RBAC k8s - эту часть можно пропусать и сразу переходить к следующей НАЗВАНИЕ ЧАСТИ ТУТ
In Kubernetes you can use bellow access models: Node, ABAC, RBAC, WebHook. The most popular model is Role Based Access Control (RBAC) - default system in k8s. It has several related entities:
- role describes access rigths
- user (can be User, Group or ServiceAccount)
- RoleBinding - entity united roles and users
To be honest we have more enitites. Let’s look at it deeply. Kubernetes has amazing documentation and we won’t try to repeat it here. Just will look at the most important things accorded to this article. If you already have known k8s RBAC well just skip this part and go to NEXT_PART_NAME
Kubernetes RBAC resources
Role
Роль состоит из списка правил. Каждое правило включает в себя:
- apiGroups - указатель на API groups
- resources - список ресурсов, к которым будет применяться правило. Тут может быть
pods
,configmaps
,secrets
, … - verbs - доступные операции с перечисленными ресурсами
Role contains rules that represent a set of permissions. Every rule contains:
- apiGroups - pointer to API groups
- resources - list of resources rule will manipulate. Can be
pods
,configmaps
,secrets
, … - verbs - allowed operations with resources
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
Verbs
Verbs описывают что можно делать с ресурсом - как rwx
в chmod
. В роли могут использоваться следующие verbs:
create
- создать ресурс. HTTP методPOST
- get, list, watch - прочитать ресурс. HTTP метод
GET
,HEAD
- update - редактировать ресурс. HTTP метод
PUT
- patch - редактировать ресурс. HTTP метод
PATCH
- delete, deletecollection - удалить ресурс. HTTP метод
DELETE
Описанные выше действия очевидны и не требуют пояснений. Самое интересное начинается со следующими verbs:
- escalate - создавать и редактировать роли
- bind - создавать и редактировать биндинги
- impersonate - представиться k8s API другим пользователем, группой или предоставить другие данные (extra)
Verbs describe what can be done with a resource. Like rwx
in chmod
. The most popular verbs:
create
- create resource. HTTP method POSTget
,list
,watch
- прочитать ресурс. HTTP методGET
,HEAD
update
- edit resource. HTTP methodPUT
patch
- edit resource. HTTP methodPATCH
delete
,deletecollection
- delete resource. HTTP methodDELETE
These actions are obvious and require no explanation. The most interesting verbs are:
- escalate - create and edit roles
- bind - create and edit rolebindings and clusterrolebindings
- impersonate - This verb allows users to impersonate and gain the rights of other users in the cluster, other group or get other data (extra)
ServiceAccounts, Users, Groups
ServiceAccount(дальше - SA) - тип учетной записи в k8s, используемый подами, системными компонентами и всем, что не кожаный мешок. В качестве аутентификатора SA использует токен JWT. Посмотрим подробнее на создание SA и его JWT токен.
Создание SA:
k create sa mysa
k get sa mysa -oyaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: mysa
namespace: defaul
Как видим, в манифесте SA нет ничего особенного. Но есть нюансы =). Обратите внимаение, что SA - namespaced resource, то есть у SA всегда есть Namespace.
ServiceAccount(bellow - SA) - a type of non-human account. Application Pods, system components, and entities inside and outside the cluster can use a specific ServiceAccount’s credentials to identify as that ServiceAccount. As authenticator uses JSON Web Token. Let’s look deeper at SA and its JWT. Create namespace:
kubectl create ns rbac
namespace/rbac created
Create ServiceAccount:
kubectl -n rbac create sa privesc
serviceaccount/privesc created
ServiceAccount manifest:
k get sa -n rbac privesc -oyaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: privesc
namespace: rbac
JWT
Сгенерируем токен для аутентификации от имени SA:
TOKEN=$(k create token mysa)
СГЕНЕРИТЬ И ПОКАЗАТЬ
Получили JWT.
Обычно по-русски пишут JWT токен, что является плеоназмом: JSON Web Token токен. Поэтому я буду писать просто JWT
JWT состоит из трех частей, разделенных точками. Первые две части - закодированный в base64 текст, последняя - подпись.
echo $TOKEN | jq -R ' split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson'
ПОКАЗАТЬ ЧТО ПОЛУЧИЛОСЬ
Let’s generate token to authenticate as SA:
TOKEN=$(k -n rbac create token privesc --duration=8h)
echo $TOKEN
eyJhbGciOiJSUzI1NiIsImtpZCI6ImxrNzcybkhfVXZiZW1YSXV0S1BaZDlxNUlFOTRjX1Y1M1o3RWhvLWRsbm8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzEyMzI0NjYxLCJpYXQiOjE3MTIyOTU4NjEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJyYmFjIiwic2VydmljZWFjY291bnQiOnsibmFtZSI6InByaXZlc2MiLCJ1aWQiOiJkODc1NmY0NS01ZDJjLTQ0YjQtYWFjOS02NDU1MjcwNDViZTMifX0sIm5iZiI6MTcxMjI5NTg2MSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OnJiYWM6cHJpdmVzYyJ9.GxJKpZevOkFwksFsA8ZPU5qLLwQdl6D3Rlt1gU-2feExcy6GadGQJlumrrpq-ih0Ufgm7YUz4jRsNld9yXT93nu27sPyxkMSjMT4rAdfFAV59Q8Z6kFyzOjuJBsEEzErB2Oft5KcGVSXBh01KWvHU8vPvBHaS_JgSV0yym3-9ruGh4eARwc3lbPZi9_PF-P8x0gCvpqaEZWF_aDjxAlcCxlkZjC2ADOHtiVlnBrDt1fqheOZ-W2BKxQ8-z9OG7PMo_x6G6VM2EQIGmY3tzyWd1gMB6bDRrWfSWjj0EPzqdXGov6w-znmzobWHJQN4BoeXBBDJA7BGUIA8VphXHu7yw
Now we have JWT. It contains 3 parts divided by dots. First and second parts - base64 encoded text, last part - signature. It’s possible to base64 decode first 2 parts and read data:
echo $TOKEN | jq -R ' split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson'
{
"alg": "RS256",
"kid": "lk772nH_UvbemXIutKPZd9q5IE94c_V53Z7Eho-dlno"
}
{
"aud": [
"https://kubernetes.default.svc.cluster.local"
],
"exp": 1712324661,
"iat": 1712295861,
"iss": "https://kubernetes.default.svc.cluster.local",
"kubernetes.io": {
"namespace": "rbac",
"serviceaccount": {
"name": "privesc",
"uid": "d8756f45-5d2c-44b4-aac9-645527045be3"
}
},
"nbf": 1712295861,
"sub": "system:serviceaccount:rbac:privesc"
}
Users, Groups
Это может показаться странным, но API k8s не имеет понятия юзера или группы. Невозможно создать пользователя или группу. Но эти данные необходимы для авторизации. API Server распознает пользователя по полю CN в Subject сертификата. ПОКАЗАТЬТ КАК openssl x509 -noout -text -in ~/gcore/tmp/c.crt
В качестве системы аутентификации k8s может использовать сторонние Identity Providers и интегрироваться с ними по OpenID Connect. Например, keycloak (ССЫЛКА ТУТ)
It looks strange but k8s API doesn’t have user and group entities. It’s impossible to create user or group via kubectl. But those data are required to authorization. Kubernetes API Server gets user from CN in Subject field of Certificate. Let’s look at it:
yq '.users[0].user.client-certificate-data | @base64d' ~/.kube/config | openssl x509 -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2201338778473110666 (0x1e8cb9f4b12e9c8a)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = kubernetes
Validity
Not Before: Mar 22 15:21:44 2024 GMT
Not After : Mar 22 15:26:39 2025 GMT
Subject: O = system:masters, CN = kubernetes-admin
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c5:9b:5a:7a:82:cd:1e:c6:8b:d6:66:55:68:2f:
...
ba:5b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Authority Key Identifier:
D5:DD:0A:9A:6B:08:9B:94:73:13:11:16:93:7C:E4:C1:24:91:A3:90
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
6d:6c:58:62:4a:2a:9e:2a:70:cb:f9:52:64:05:6f:f2:18:72:
...
1f:ad:a3:7a
Field Subject
contains data about user and access rights:O = system:masters, CN = kubernetes-admin
.
Kubernetes can use third-party Identity Providers as authentication system and integrate with it by OpenID Connect. For example keycloak, dex (LINK HERE)
RoleBinding
Ресурс, связывающий роль и пользователя/группу/сервисаккаунт. Содержит всего два поля:
- список Subjects, в котором перечислены субъекты доступа: пользователи и/или группы и/или сервисаккаунты
- roleRef - роль, которая привязывается к перечисленным субъектам
Resource united roles and users/groupd/serviveAccounts. Has two fields:
- list of Subjects, holds access subjects: user and/or group and/or ServiceAccount
- roleRef - role united with these subjects
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
name: jane # "name" is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" specifies the binding to a Role / ClusterRole
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
ClusterRole, ClusterRoleBinding
Ресурсы кластера делятся на namespaced и non-namespaced. Это значит, что первые всегда имеют namespace, а вторые нет (например node
).
- Role - namespaced ресурс. То есть она описывает доступы только внутри namespace
- ClusterRole - non-namespaced. Описывает права доступа на ресурсы, которые не имеют namespace. Но также может описывать и namespaced. За подробностями - в документацию
RoleBinding отличается от ClusterRoleBinding примерно тем же. Подробности опять же в документации
There are namespaced and non-namespaced cluster resources. It means the first always has namespace, and second never (node, for example)
- Role - namespaced resource. It means it manages access rights in particular namespace
- ClusterRole - non-namespaced resource. It manages access right for any resource: namespaced and non-namespaced. Detailed in documentation
Unobvious Kubernetes RBAC nuances
Not very popular but too dangerous nuances in k8s RBAC
Escalate
DOC: You can only create/update a role if at least one of the following things is true:
- You already have all the permissions contained in the role, at the same scope as the object being modified (cluster-wide for a ClusterRole, within the same namespace or cluster-wide for a Role).
- You are granted explicit permission to perform the
escalate
verb on the roles or clusterroles resource in the rbac.authorization.k8s.io API group.
Kubernetes RBAC API doesn’t allow to escalate privileges by simple redacting Role or RoleBinding. It works at API level and will work even if RBAC authorizer turned off. Исключение из этого правила - наличие права escalate
у роли.
НАРИСОВАТЬ!
Create role allowed to read-only access to pods and roles in rbac
namespace:
kubectl -n rbac create role view --verb=list,watch,get --resource=role,pod
role.rbac.authorization.k8s.io/view created
Bind role to ServiceAccount privesc
:
kubectl -n rbac create rolebinding view --role=view --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/view created
Check it:
kubectl auth can-i get role -n rbac --as=system:serviceaccount:rbac:privesc
yes
kubectl auth can-i update role -n rbac --as=system:serviceaccount:rbac:privesc
no
Create new role allowed role editing in rbac
namespace:
kubectl -n rbac create role edit --verb=update,patch --resource=role
role.rbac.authorization.k8s.io/edit created
Bind it to ServiceAccount privesc
:
kubectl -n rbac create rolebinding edit --role=edit --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/edit created
Check:
kubectl auth can-i update role -n rbac --as=system:serviceaccount:rbac:privesc
yes
kubectl auth can-i delete role -n rbac --as=system:serviceaccount:rbac:privesc
no
Теперь для чистоты эксперимента проверим возможности нашего сервисаккаунта. Для этого используем его токен в конфиге.
Now for the purity of the experiment let’s check the capabilities of our account service. Will use its token for it.
TOKEN=$(kubectl -n rbac create token privesc --duration=8h)
Придется удалить из конфига старые параметры аутентификации, т.к. k8s сначала проверяет сертификат пользователя и не будет проверять токен, если данные о сертификате переданы.
We should remove old authentication parameters from config, becaus k8s firstly checks user’s certificate and won’t check token if it has know about certificate already.
cp ~/.kube/config ~/.kube/rbac.conf
export KUBECONFIG=~/.kube/rbac.conf
kubectl config delete-user kubernetes-admin
kubectl config set-credentials privesc --token=$TOKEN
kubectl config set-context --current --user=privesc
edit
role shows we can edit roles:
kubectl -n rbac get role edit -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: edit
namespace: rbac
rules:
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
verbs:
- update
- patch
Let’s try to add new verb (list), which we have already used in other role (view)
kubectl -n rbac edit role edit
OK
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: edit
namespace: rbac
rules:
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
verbs:
- update
- patch
- list # <-- added this string
Let’s try to add new verb (delete), which we haven’t used in other roles:
kubectl -n rbac edit role edit
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: edit
namespace: rbac
rules:
- apiGroups:
- rbac.authorization.k8s.io
resources:
- roles
verbs:
- update
- patch
- delete # <-- added this string
error: roles.rbac.authorization.k8s.io "edit" could not be patched: roles.rbac.authorization.k8s.io "edit" is forbidden: user "system:serviceaccount:rbac:privesc" (groups=["system:serviceaccounts" "system:serviceaccounts:rbac" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:["rbac.authorization.k8s.io"], Resources:["roles"], Verbs:["delete"]}
Kubernetes не позволяет добавлять себе новых прав, которых ещё нет у пользователя - прав, которые не описаны в других ролях, забинденых к этому пользователю
Kubernetes doesn’t allow to add new access rights for user or ServiceAccount if it still hasn’t have it. If there aren’t such access rights in other roles binded to this user or ServiceAccount.
Let’s expand ServiceAccount privesc
access rights. Will add new role with verb escalate
using admin config:
KUBECONFIG=~/.kube/config kubectl -n rbac create role escalate --verb=escalate --resource=role
role.rbac.authorization.k8s.io/escalate created
KUBECONFIG=~/.kube/config kubectl -n rbac create rolebinding escalate --role=escalate --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/escalate created
Check once again - can we add new verb to role:
kubectl -n rbac edit role edit
role.rbac.authorization.k8s.io/edit edited
Теперь это работает. Пользователь может повышать свои привилегии редактируя существующие роли. То есть verb escalate фактически дает права администратора, т.к. пользователь, обладающий привилегиями escalate может выписать себе любые права на неймспейс или кластер, если это указано в resources.
It works now. User can expand his privileges by editing existing role (or by adding new roles if user has create
privileges). So, verb escalate
gives admin privileges in fact. If user has escalate
privileges, he can expand their privileges to namespace admin or even cluster admin.
Bind
DOC: To allow a user to create/update role bindings:
- Grant them a role that allows them to create/update RoleBinding or ClusterRoleBinding objects, as desired.
- Grant them permissions needed to bind a particular role:
- implicitly, by giving them the permissions contained in the role.
- explicitly, by giving them permission to perform the bind verb on the particular Role (or ClusterRole).
Bind
работает аналогично escalate
. Если escalate
разрешает редактировать Role
или ClusterRole
для повышения привилегий, то bind
разрешает редактировать RoleBinding
или ClusterRoleBinding
.
Bind
works similarly escalate
. escalate
allows to edit Role
or ClusterRole
for privilege escalation, as well as bind
allows to edit RoleBinding
or ClusterRoleBinding
.
Let’s change kubeconfig to admin: export KUBECONFIG=~/.kube/config
Remove old roles and bindings:
kubectl -n rbac delete rolebinding view edit escalate
kubectl -n rbac delete role view edit escalate
Allow ServiceAccount to view and edit rolebinding and pod in namespace:
kubectl -n rbac create role view --verb=list,watch,get --resource=role,rolebinding,pod
kubectl -n rbac create rolebinding view --role=view --serviceaccount=rbac:privesc
kubectl -n rbac create role edit --verb=update,patch,create --resource=rolebinding,pod
kubectl -n rbac create rolebinding edit --role=edit --serviceaccount=rbac:privesc
Create separate roles to work with pods, but still don’t bind it:
kubectl -n rbac create role pod-view-edit --verb=get,list,watch,update,patch --resource=pod
kubectl -n rbac create role delete-pod --verb=delete --resource=pod
Let’s change kubeconfig to ServiceAccount privesc and try to edit rolebinding:
export KUBECONFIG=~/.kube/rbac.conf
kubectl -n rbac create rolebinding pod-view-edit --role=pod-view-edit --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/pod-view-edit created
Новая роль успешно забиндилась к существующему сервисаккаунту. Обратите внимание, что роль pod-view-edit
содержит verbs
и resources
уже подключенные сервисаккаунту в rolebinding view
и edit
.
Теперь попробуем забиндить роль с новым verb delete
, которого ещё нет в уже подключенных к сервисаккаунту ролях
New role’s binded to ServiceAccount successfully. Pay attention role pod-view-edit
contains verbs
abd resources
already binded to ServiceAccount at rolebinding view
и edit
:
kubectl -n rbac create rolebinding delete-pod --role=delete-pod --serviceaccount=rbac:privesc
error: failed to create rolebinding: rolebindings.rbac.authorization.k8s.io "delete-pod" is forbidden: user "system:serviceaccount:rbac:privesc" (groups=["system:serviceaccounts" "system:serviceaccounts:rbac" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["pods"], Verbs:["delete"]}
Kubernetes не позволяет нам это сделать, несмотря на то, что у нас есть права на редактирование и создание RoleBindings. Исправить это нам поможет право bind
. Используя админский конфиг выдадим его сервисаккаунту
Kubernetes doesn’t allow to do it even with access rights to edit and create RoleBindings. You could fix it with verb bind
. Let’s do it with admin config:
KUBECONFIG=~/.kube/config kubectl -n rbac create role bind --verb=bind --resource=role
role.rbac.authorization.k8s.io/bind created
KUBECONFIG=~/.kube/config kubectl -n rbac create rolebinding bind --role=bind --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/bind created
Try again to create RoleBinding with new verb delete
:
kubectl -n rbac create rolebinding delete-pod --role=delete-pod --serviceaccount=rbac:privesc
rolebinding.rbac.authorization.k8s.io/delete-pod created
It works now.
Impersonate
DOC Impersonation requests first authenticate as the requesting user, then switch to the impersonated user info.
- A user makes an API call with their credentials and impersonation headers.
- API server authenticates the user.
- API server ensures the authenticated users have impersonation privileges.
- Request user info is replaced with impersonation values.
- Request is evaluated, authorization acts on impersonated user info.
Impersonate это такой sudo для k8s. С правами impersonate пользователь может представиться другим пользователем и выполнять команды от его имени. kubectl имеет опции --as
, --as-group
, --as-uid
, позволяющие выполнить команду от имени юзера, группы или uid соответственно. То есть, если в одной из ролей пользователь получил impersonate, то его можно считать админом неймспейса. Или кластера в худшем варианте.
Имперсонэйт удобно использовать для проверки корректности RBAC - запускать kubectl auth can-i --as=$USERNAME -n $MANESPACE get pod
.
Хорошие виндовые админы должны помнить, что даже главные админы должны использовать аккаунты с минимальными правами для ежедневных дел, а важные консоли запускать от имени другого юзера с бОльшим количеством прав. В линуксе тот же принцип обеспечивается с помощью sudo
. Так и в кубернетес - для защиты от случайного удаления важных данных, лучше не разрешать это действие обычному юзеру, а разрешить только админу, а юзеру дать права на impersonate, чтобы он мог использовать --as=
когда это действительно нужно.
Impersonate verb is like sudo but for k8s instead of Linux. If user has imeprsonate
access, he can authenticate as other user and run commands as other user. kubectl has options --as
, --as-group
, --as-uid
, which allowed to run command as other user, other group or other uid respectively. So, we can say if any user got impersonate rights, he would be namespace admin or even cluster admin.
Impersonate is usefull to check RBAC rules - admin should run kubectl auth can-i --as=$USERNAME -n $MANESPACE $VERB $RESOURCE
and check is authorization works as designed.
kubectl auth can-i get pod -n rbac --as=system:serviceaccount:rbac:privesc
yes
Or look at all user/ServiceAccounts access rights:
kubectl auth can-i --list -n rbac --as=system:serviceaccount:rbac:privesc
Resources Non-Resource URLs Resource Names Verbs
roles.rbac.authorization.k8s.io [] [edit] [bind escalate]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
...
rolebindings.rbac.authorization.k8s.io [] [] [update patch create bind escalate list watch get]
roles.rbac.authorization.k8s.io [] [] [update patch create bind escalate list watch get]
pods [] [] [update patch create delete get list watch]
configmaps [] [] [update patch create delete]
secrets [] [] [update patch create delete]
Good system administrators remember: even if you have domain admin access level you have to use limited account to manage your infrastructure and use privileged account only if it needed for such tasks. It called The principle of least privilege. In cloud era this principle realized by impersonate. To prevent accidentally important resources deletion it is possible to create separate ServiceAccount with verb delete
and allow users to impersonate only with this ServiceAccount ПОКАЗАТЬ КАК!
There is a project kubectl-sudo implemented such technique as a kubectl plugin.
НКАРИСОВАТЬ!
Создадим новый сервисаккаунт в неймспейсе rbac с именем impersonator, роль с правами impersonate и забиндим её на новый сервисаккаунт. Обратите внимание, что в роли мы разрешаем представляться только сервисаккаунтом privesc
и никаким другим:
Let’s create new ServiceAccount impersonator
in namespace rbac
, role with impersonate
verb and RoleBinding. Take a look at --resource-name
parameter - it is allowed to impersonate only as privesc
ServiceAccount:
KUBECONFIG=~/.kube/config kubectl -n rbac create sa impersonator
serviceaccount/impersonator created
KUBECONFIG=~/.kube/config kubectl -n rbac create role impersonate --resource=serviceaccounts --verb=impersonate --resource-name=privesc
role.rbac.authorization.k8s.io/impersonate created
KUBECONFIG=~/.kube/config kubectl -n rbac create rolebinding impersonator --role=impersonate --serviceaccount=rbac:impersonator
rolebinding.rbac.authorization.k8s.io/impersonator created
Let’s create new context and check access rights:
TOKEN=$(KUBECONFIG=~/.kube/config kubectl -n rbac create token impersonator --duration=8h)
kubectl config set-credentials impersonate --token=$TOKEN
User "impersonate" set.
kubectl config set-context impersonate@kubernetes --user=impersonate --cluster=kubernetes
Context "impersonate@kubernetes" created.
kubectl config use-context impersonate@kubernetes
Switched to context "impersonate@kubernetes".
kubectl auth can-i --list -n rbac
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
...
serviceaccounts [] [privesc] [impersonate]
Ничего лишнего нет - только impersonate, как и указано в роли. Но если воспользоваться правом impersonate
и представиться как сервисаккаунт privesc
, то видно, что права совпадают с сервисаккаунтом privesc
:
There is nothing extra - just impersonate, as specified in the role. But if you use the impersonate
and impersonate as serviceaccount privesc
, you can see that the rights are the same as serviceaccount privesc
:
kubectl auth can-i --list -n rbac --as=system:serviceaccount:rbac:privesc
Resources Non-Resource URLs Resource Names Verbs
roles.rbac.authorization.k8s.io [] [edit] [bind escalate]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
pods [] [] [get list watch update patch delete create]
...
rolebindings.rbac.authorization.k8s.io [] [] [list watch get update patch create bind escalate]
roles.rbac.authorization.k8s.io [] [] [list watch get update patch create bind escalate]
configmaps [] [] [update patch create delete]
secrets [] [] [update patch create delete]
То есть с impersonate
сервисаккаунт получил все свои права + все права сервисаккаунта, которым он может представляться.
That is, with impersonate
the serviceaccount got all its rights + all the rights of the serviceaccount it can represent itself to.
Mitigation
Система авторизации k8s очень гибкая и позволяет гранулярно настраивать параметры доступа. Даже в тех случаях, когда пользователю необходимо управлять такими чувствительными для безопасности примитивами как Role/ClusterRole и RoleBinding/ClusterRoleBinding. При этом пользователь не сможет повысить свои привилегии и получить доступ к закрытым данным, если администратор явно не позволит ему повышать привилегии.
Повысить привилегии позволяют verbs escalate
и bind
. Первый разрешает добавлять новые записи в Role/ClusterRole, а второй - в RoleBinding/ClusterRoleBinding. Это очень мощные инструменты, неправильное использование которых может нанести значительный урон работе кластера. Следует очень внимательно проверять любое использование этих verbs и всегда убеждаться в том, что соблюдается правило Least Privilegies - пользователь должен иметь минимальный набор привилегий, необходимый для работы.
Для ограничения использования этих или любых других правил в манифестах Role/ClusterRole есть поле resourceNames
, куда можно (и нужно!) вписать имена ресурсов, которые можно использовать.
The k8s authorisation system is very flexible and allows granular configuration of access parameters. Even in cases where the user needs to manage security sensitive primitives such as Role/ClusterRole and RoleBinding/ClusterRoleBinding. The user will not be able to escalate privileges and access sensitive data unless the administrator explicitly allows the user to escalate privileges.
The verbs escalate
, bind
and impersonate
allow to escalate privileges. The former allows adding new entries to Role/ClusterRole the second to RoleBinding/ClusterRoleBinding and the latter to impersonate as a different user. These are very powerful tools, the misuse of which can cause significant damage to a cluster. You should check any use of these verbs very carefully and always make sure that the Least Privilegies rule is followed - the user must have the minimum set of privileges required to operate.
To restrict the use of these or any other rules, Role/ClusterRole manifests have a resourceNames
field where you can (and should!) enter the names of resources that can be used.
Example allows create ClusterRoleBinding with roleRef
named “admin”,“edit”,“view”:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
resourceNames: ["admin","edit","view"]
То же самое можно сделать с escalate. С bind
права в роли задает админ, а пользователь только может забиндить эту роль на себя, если это разрешено в resourceNames
, то с escalate
пользователь может внутри роли прописать любые параметры и стать админом неймспейса или кластера. То есть escalate
дает больше возможностей, в то время как bind
ограничивает пользователя. Имейте это в виду, когда приедтся давать эти права пользователям.
The same can be done with escalate as well as impersonate. With bind
the rights in the role are set by the admin, and the user can only bind this role to himself if it is allowed in resourceNames
, while with escalate
the user can write any parameters inside the role and become the admin of a namespace or cluster. That is, escalate
gives more options, while bind
restricts the user. Keep this in mind when you have to give these rights to users.
To prevent unathorized access and RBAC misconfiguration you should periodically check cluster RBAC manifests:
kubectl get clusterrole -A -oyaml | yq '.items[] | select (.rules[].verbs[] | contains("esalate" | "bind" | "impersonate")) | .metadata.name'
kubectl get role -A -oyaml | yq '.items[] | select (.rules[].verbs[] | contains("esalate" | "bind" | "impersonate")) | .metadata.name'
Or use automatic systems which monitor creating or editing roles with suspicious content. Falco, for example. СЮДА МОЖНО ПРИЛЕПИТЬ РЕКЛАМУ НАШЕГО Logs as a Service
Kubernetes предоставляет очень гибкие возможности для управления правами доступа. Вместе с гибкостью приходят и сложность поддержки наряду с угрозами безопасности. Для обеспечения надежной и безопасной работы нужно понимать принципы функционирования системы авторизации и типы вспомогательного инструментария, снижающего риски. И помните, что безопасность всей системы равна безопасности её самого слабого звена.
Kubernetes provides very flexible options for managing access rights. Along with flexibility comes support complexity along with security threats. To ensure safe and secure operation, you need to understand how the authorisation system works and the types of support tools that mitigate risks. And remember, the security of the entire system is equal to the security of its weakest link.
comments powered by Disqus