[Перевод] Понимаем RBAC в Kubernetes
Прим. перев.: Статья написана Javier Salmeron — инженером из хорошо известной в Kubernetes-сообществе компании Bitnami — и была опубликована в блоге CNCF в начале августа. Автор рассказывает о самых основах механизма RBAC (управление доступом на основе ролей), появившегося в Kubernetes полтора года назад. Материал будет особенно полезным для тех, кто знакомится с устройством ключевых компонентов K8s (ссылки на другие подобные статьи см. в конце).
Слайд из презентации, сделанной сотрудником Google по случаю релиза Kubernetes 1.6
Многие опытные пользователи Kubernetes могут вспомнить релиз Kubernetes 1.6, когда авторизация на основе Role-Based Access Control (RBAC) получила статус бета-версии. Так появился альтернативный механизм аутентификации, который дополнил уже существующий, но трудный в управлении и понимании, — Attribute-Based Access Control (ABAC). Все с восторгом приветствовали новую фичу, однако в то же время бесчисленное число пользователей были разочарованы. StackOverflow и GitHub изобиловали сообщениями об ограничениях RBAC, потому что большая часть документации и примеров не учитывали RBAC (но сейчас уже всё в порядке). Эталонным примером стал Helm: простой запуск helm init
+ helm install
больше не работал. Внезапно нам потребовалось добавлять «странные» элементы вроде ServiceAccounts
или RoleBindings
ещё до того, как разворачивать чарт с WordPress или Redis (подробнее об этом см. в инструкции).
Оставив же эти неудачные первые попытки в стороне, нельзя отрицать тот огромный вклад, что внёс RBAC в превращение Kubernetes в готовую к production платформу. Многие из нас успели поиграть с Kubernetes с полными привилегиями администратора, и мы прекрасно понимаем, что в реальном окружении необходимо:
- Иметь множество пользователей с разными свойствами, обеспечивающими нужный механизм аутентификации.
- Иметь полный контроль над тем, какие операции может исполнять каждый пользователь или группа пользователей.
- Иметь полный контроль над тем, какие операции может исполнять каждый процесс в поде.
- Ограничивать видимость определённых ресурсов в пространствах имён.
И в этом отношении RBAC — ключевой элемент, предоставляющий столь необходимые возможности. В статье мы быстро пройдёмся по основам (для подробностей смотрите это видео; по ссылке 1-часовой вебинар от Bitnami на английском языке — прим. перев.) и немного углубимся в самые запутанные моменты.
Ключ к пониманию RBAC в Kubernetes
Чтобы полностью осознать идею RBAC, нужно понимать, что к ней причастны три элемента:
- Subjects (субъекты) — совокупность пользователей и процессов, которые хотят иметь доступ в Kubernetes API;
- Resources (ресурсы) — совокупность объектов Kubernetes API, доступных в кластере. Их примерами (среди прочих) являются Pods, Deployments, Services, Nodes, PersistentVolumes;
- Verbs (глаголы) — совокупность операций, которые могут быть выполнены над ресурсами. Существуют различные verbs (get, watch, create, delete и т.п.), но все они в конечном счёте являются операциями из разряда CRUD (Create, Read, Update, Delete).
Если помнить об этих трёх элементах, ключевая идея RBAC звучит так:
— Мы хотим соединить субъекты, ресурсы API и операции. Другими словами, мы хотим указать для заданного пользователя, какие операции могут быть исполнены на множестве ресурсов.
Разбираемся с объектами RBAC в API
В соединении этих трёх типов сущностей становятся понятными и доступные в Kubernetes API объекты RBAC:
Roles
соединяют ресурсы и глаголы. Они могут повторно использоваться для разных субъектов. Привязаны к одному пространству имён (мы не можем использовать шаблоны, представляющие более одного [пространства имён], зато можем деплоить один и тот же объект роли в разные пространства имён). Если вы хотите применить роль ко всему кластеру, есть аналогичный объектClusterRoles
.RoleBindings
соединяют оставшиеся сущности-субъекты. Указав роль, которая уже связывает объекты API с глаголами, теперь мы выбираем субъекты, которые могут их использовать. Эквивалентом для уровня кластера (т.е. без привязки к пространствам имён) являетсяClusterRoleBindings
.
В примере ниже мы выдаём пользователю jsalmeron право на чтение, получение списка и создание подов в пространстве имён test. Это означает, что jsalmeron сможет выполнять такие команды:
kubectl get pods --namespace test
kubectl describe pod --namespace test pod-name
kubectl create --namespace test -f pod.yaml # в этом файле описание пода
…, но не такие:
kubectl get pods --namespace kube-system # другое пространство имён
kubectl get pods --namespace test -w # требует также глагола watch
Примеры YAML-файлов:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: pod-read-create
namespace: test
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "create"]
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: salme-pods
namespace: test
subjects:
- kind: User
name: jsalmeron
apiGroup: rbac.authorization.k8s.io
roleRef: Role
kind: Role
name: ns-admin
apiGroup: rbac.authorization.k8s.io
Другой интересный момент в следующем: теперь, когда пользователь может создавать поды, можем ли мы ограничить, как много? Для этого потребуются другие объекты, не относящиеся напрямую к спецификации RBAC и позволяющие настраивать ограничения по количеству ресурсов: ResourceQuota
и LimitRanges
. Их определённо стоит изучить при конфигурации столь важной составляющей кластера [как создание подов].
Субъекты: пользователи и… ServiceAccounts?
Одной из сложностей, с которой сталкиваются многие пользователи Kubernetes в контексте субъектов, является различие между обычными пользователями и ServiceAccounts
. В теории всё просто:
Users
— глобальные пользователи, предназначены для людей или процессов, живущих вне кластера;ServiceAccounts
— ограниченные пространством имён и предназначенные для процессов внутри кластера, запущенных на подах.
Сходство обоих типов заключается в необходимости аутентифицироваться в API для исполнения определённых операций над множеством ресурсов, и их предметные области выглядят весьма конкретными. Они также могут относиться к группам, поэтому RoleBinding
позволяет привязывать более одного субъекта (хотя для ServiceAccounts
допустима лишь одна группа — system:serviceaccounts
). Тем не менее, основное отличие и есть причина головной боли: у пользователей нет соответствующих им объектов в Kubernetes API. Получается, что такая операция существует:
kubectl create serviceaccount test-service-account # OK
…, но вот такой уже нет:
kubectl create user jsalmeron # Ошибка!
У этой ситуации серьёзное последствие: если кластер не будет хранить информацию о пользователях, администратору придётся управлять учётными записями вне кластера. Здесь есть разные способы решения проблемы: TLS-сертификаты, токены, OAuth2 и т.п.
Вдобавок, потребуется создать контексты kubectl
, чтобы мы могли получить доступ к кластеру через эти новые учётные записи. Чтобы создать файлы с ними, можно воспользоваться командами kubectl config
(что не требуют доступа к Kubernetes API, поэтому могут исполняться любым пользователем). В приведённом выше видео есть пример по созданию пользователя с TLS-сертификатами.
RBAC в Deployments: пример
Мы видели пример, в котором указанному пользователю выдаются права на операции в кластере. Но что насчёт Deployments, требующих доступа к Kubernetes API? Рассмотрим конкретный сценарий, чтобы разобраться получше.
Возьмём для примера популярное инфраструктурное приложение — RabbitMQ. Будем использовать Helm-чарт для RabbitMQ от Bitnami (из официального репозитория helm/charts), который использует контейнер bitnami/rabbitmq. В контейнер встроен плагин для Kubernetes, отвечающий за обнаружение других членов кластера RabbitMQ. Из-за этого процесс внутри контейнера требует доступа к Kubernetes API, и нам потребуется настроить ServiceAccount
с правильными RBAC-привилегиями.
Когда речь заходит о ServiceAccounts
, следуйте этой хорошей практике:
— Настраивайте ServiceAccounts для каждого Deployment с минимальным набором привилегий.
В случае приложений, требующих доступа к Kubernetes API, у вас может возникнуть соблазн создать некий «привилегированный ServiceAccount
», который сможет делать в кластере практически всё. Хотя это кажется более простым решением, в конечном счёте оно может привести к уязвимости в безопасности, позволяющей выполнять нежелательные операции. (В видео рассматривается пример Tiller [компонента Helm] и последствий наличия ServiceAccounts
с большими привилегиями.)
Вдобавок, у разных Deployments будут разные потребности в смысле доступа к API, поэтому для каждого Deployment разумно иметь разные ServiceAccounts
.
Не забывая об этом, посмотрим, какая конфигурация RBAC будет правильной для случая Deployment'а с RabbitMQ.
В документации плагина и его исходном коде можно увидеть, что он запрашивает у Kubernetes API список Endpoints. Так и происходит обнаружение остальных членов кластера RabbitMQ. Поэтому чарт RabbitMQ от Bitnami создаёт:
- ServiceAccount для подов с RabbitMQ:
{{- if .Values.rbacEnabled }} apiVersion: v1 kind: ServiceAccount metadata: name: {{ template "rabbitmq.fullname" . }} labels: app: {{ template "rabbitmq.name" . }} chart: {{ template "rabbitmq.chart" . }} release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" {{- end }}
- Role (мы предполагаем, что весь кластер RabbitMQ разворачивается в едином пространстве имён), разрешающую глагол get для ресурса Endpoint:
{{- if .Values.rbacEnabled }} kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ template "rabbitmq.fullname" . }}-endpoint-reader labels: app: {{ template "rabbitmq.name" . }} chart: {{ template "rabbitmq.chart" . }} release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get"] {{- end }}
- RoleBinding, соединяющую
ServiceAccount
с ролью:{{- if .Values.rbacEnabled }} kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ template "rabbitmq.fullname" . }}-endpoint-reader labels: app: {{ template "rabbitmq.name" . }} chart: {{ template "rabbitmq.chart" . }} release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" subjects: - kind: ServiceAccount name: {{ template "rabbitmq.fullname" . }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: {{ template "rabbitmq.fullname" . }}-endpoint-reader {{- end }}
Схема показывает, что мы разрешили процессам, запущенным в подах RabbitMQ, исполнять операции get над объектами Endpoint. Это минимальный набор операций, который требуется для того, чтобы всё работало. В то же время мы знаем, что развёрнутый чарт безопасен и не выполнит нежелательных действий внутри кластера Kubernetes.
Заключительные мысли
Чтобы работать с Kubernetes в production, политики RBAC не являются опциональными. Их нельзя рассматривать как набор объектов API, который должны знать только администраторы. Они на самом деле нужны разработчикам для развёртывания безопасных приложений и полного использования потенциала, предлагаемого Kubernetes API для облачных (cloud native) приложений. Больше информации по RBAC можно получить по этим ссылкам:
P.S. от переводчика
Читайте также в нашем блоге: