[Перевод] Понимаем RBAC в Kubernetes

Прим. перев.: Статья написана Javier Salmeron — инженером из хорошо известной в Kubernetes-сообществе компании Bitnami — и была опубликована в блоге CNCF в начале августа. Автор рассказывает о самых основах механизма RBAC (управление доступом на основе ролей), появившегося в Kubernetes полтора года назад. Материал будет особенно полезным для тех, кто знакомится с устройством ключевых компонентов K8s (ссылки на другие подобные статьи см. в конце).

tzxmqenidfjgy8nps6myfacun2i.jpeg
Слайд из презентации, сделанной сотрудником 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).


xcypcyyglvjo5v6igg213nbkfxo.png

Если помнить об этих трёх элементах, ключевая идея 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


rzes71nhwjlob2scvnv0m5yfahy.png

Примеры 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 }}
    


lxuwvgvlcluwmoln2mx0wgkxdyk.png

Схема показывает, что мы разрешили процессам, запущенным в подах RabbitMQ, исполнять операции get над объектами Endpoint. Это минимальный набор операций, который требуется для того, чтобы всё работало. В то же время мы знаем, что развёрнутый чарт безопасен и не выполнит нежелательных действий внутри кластера Kubernetes.

Заключительные мысли


Чтобы работать с Kubernetes в production, политики RBAC не являются опциональными. Их нельзя рассматривать как набор объектов API, который должны знать только администраторы. Они на самом деле нужны разработчикам для развёртывания безопасных приложений и полного использования потенциала, предлагаемого Kubernetes API для облачных (cloud native) приложений. Больше информации по RBAC можно получить по этим ссылкам:

P.S. от переводчика


Читайте также в нашем блоге:

© Habrahabr.ru