Система оркестрации Kubernetes или как ее называют сокращенно K8s на сегодняшний день стала стандартом де-факто в управлении контейнерами. Сейчас на K8s работает множество различных сложных микросервисных приложений. И для их грамотного функционирования важно правильно выполнить настройки безопасности в данной среде оркестрации.
В состав Kubernetes входит несколько основных компонентов. Это распределенное хранилище данных etcd, kube-scheduler, kube-controller-manager и kube-api-server. Вот о последнем мы и поговорим в сегодняшней статье.
Но сначала рассмотрим архитектуру K8s и какие роли играют перечисленные выше компоненты.
![](https://habrastorage.org/getpro/habr/upload_files/d84/058/25d/d8405825d4fbfdeee1aa1203bcb625e3.png)
Компонент etcd это по сути распределенная база типа ключ-значение. В Kubernetes это основное хранилище всех настроек. И кстати, etcd не является аббревиатурой и никак не расшифровывается.
Следующий компонент это Kube-scheduler — он назначает Pod на узлы. Pod —минимальная сущность, с которой работает Kubernetes. Pod должен содержать как минимум один контейнер.
Kube-scheduler определяет, какие узлы являются допустимыми для размещения каждого Pod в очереди планирования. Критерием для выбора являются аппаратные мощности узлов кластера. Затем kube-scheduler ранжирует каждый допустимый узел и привязывает Pod к подходящему узлу.
При этом, в кластере может использоваться несколько разных планировщиков.
kube-controller-manager — это демон, обеспечивающий непрерывный цикл, который регулирует состояние системы пытаясь приблизить текущее состояние к желаемому. Другими словами, он постоянно следит за тем, чтобы характеристики наших компонентов K8s соответствовали нашим требованиям.
И наконец, kube-api-server — центральный компонент инфраструктуры Kubernetes. Это единственный компонент, который может писать и читать в базу etcd. Также, через API осуществляется значительная часть взаимодействий пользователя с системой. Поэтому крайне важно, чтобы взаимодействие с этим компонентом было безопасным. И сначала мы рассмотрим, из каких этапов состоит аутентификация, а потом уже поговорим непосредственно о ролевой модели.
Как происходит аутентификация
С сервисом Kubernetes можно взаимодействовать с помощью консольной утилиты kubectl, либо с помощью клиентских библиотек, либо с помощью REST запросов. REST (Representational State Transfer) — это архитектурный подход, который устанавливает ограничения для API: как они должны быть устроены и какие функции поддерживать.
При этом не имеет большого значения, кто именно будет аутентифицироваться пользователь или сервис K8s и тот, и другой будут аутентифицироваться через API. Когда API получает наш запрос будут выполнены шаги, представленные на рисунке.
![](https://habrastorage.org/getpro/habr/upload_files/9c6/181/ec0/9c6181ec0db74c1f92e895d0be81ae73.png)
Если наша инфраструктура кластера K8s состоит более чем из одного узла, то API сервер по умолчанию слушает порт 6443 на первом, не локальном интерфейсе. Весь трафик идущий между клиентом и API сервером защищен TLS. Также API сервер использует сертификат, который может быть подписан внутренним центром сертификации.
После установки соединения, выполняется следующий шаг — аутентификация. Kubernetes использует клиентские сертификаты, специальные токены или аутентифицирующий прокси-сервер для проверки подлинности запросов API с помощью плагинов аутентификации. При отправке HTTP-запросов на сервер API плагины пытаются связать следующие атрибуты с запросом:
Имя пользователя: (kube-admin или jane@example.com).
UID: уникальный идентификатор конечного пользователя. Надежнее логина.
Groups: набор строк, каждая из которых указывает на принадлежность пользователя к именованной логической группе пользователей. Общими значениями могут быть system:masters или devops-team.
Дополнительные поля: сопоставление со списком строк, содержащим дополнительную информацию, которая может оказаться полезной при авторизации.
Вы можете включить несколько методов аутентификации одновременно. Обычно рекомендуется использовать как минимум два метода: токены для учетных записей служб и один из методов аутентификации пользователя.
Но здесь важно учитывать, что если включено несколько модулей проверки подлинности, то первый модуль, успешно прошедший проверку подлинности запроса, завершает проверку. И при этом, сервер API не гарантирует выполнение проверки подлинности строго по порядку. То есть, теоретически возможна ситуация, когда последовательность аутентификации одного и того же пользователя будет разной, в зависимости от того, какой модуль осуществил проверку.
Входными данными для этапа аутентификации является весь HTTP-запрос. Однако, на практике, как правило, проверяются заголовки и/или сертификат клиента. Модули аутентификации включают клиентские сертификаты, пароль, токены начальной загрузки и веб-токены JSON (используемые для учетных записей служб).
Очевидно, что предоставленные учетные данные должны пройти проверку, в результате которой принимается решения о принятии или отклонении запроса. Если аутентификация не пройдена, то все просто — запрос отклоняется со статусом HTTP 401. В противном случае пользователь успешно проходит аутентификацию под определенным логином, и это имя пользователя доступно для последующих шагов по авторизации, о которых мы поговорим далее.
В зависимости от того, какое средство аутентификации вы используете, по результатам успешной проверки учетных данных вам может быть предоставлена информация о членстве пользователя в группах.
Важно учесть, что хотя Kubernetes использует имена пользователей для принятия решений об управлении доступом и ведения журнала запросов, у него нет объекта User и он не хранит имена пользователей или другую информацию о пользователях в своем API.
На этом этап аутентификации можно считать завершенным, и мы можем перейти к рассмотрению механизмов авторизации, используемых в K8s.
Авторизация
После успешной проверки подлинности пользовательского запроса, он должен быть авторизован. Для этого, запрос должен содержать имя отправителя, запрашиваемое действие и объект, на который он направлен. Запрос считается авторизованным, если в существующей политике указано, что у пользователя есть разрешения на выполнение запрошенного действия. И вот здесь мы рассмотрим несколько примеров, поясняющих принципы работы политик доступа.
Например, если у пользователя Alex есть политика, описанная ниже, он имеет доступ на чтение подов только в пространстве имен Myproject:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "alex",
"namespace": "Myproject",
"resource": "pods",
"readonly": true
}
}
По документации, формат файлов Policy — это «Один объект JSON в строке», но для простоты восприятия мы отформатировали эти объекты.
Если наш пользователь Alex отправит следующий запрос, то он будет успешно авторизован, поскольку ему разрешено читать объекты в пространстве имен Myproject:
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "Myproject",
"verb": "get",
"group": "unicorn.example.org",
"resource": "pods" }
}
}
Но если Alex попробует отправить запрос на создание или обновление объектов в этом пространстве имен, то ему будет отказано. Если Alex также отправит запрос на получение объектов в другом пространстве имен, таком как Default, в этом разрешении будет отказано.
Модули авторизации
В качестве режимов аутентификации мы можем использовать несколько различных модулей: ABAC, RBAC и режим Webhook. Режим ABAC (Attribute Based Access Control) в K8s определяет метод управления доступом, при котором права доступа предоставляются пользователям на основе атрибутов. Политики ABAC могут использовать атрибуты любого типа (атрибуты пользователя, атрибуты ресурса, атрибуты объекта, атрибуты среды и т.д.).
На рисунке ниже у каждого из трех пользователей есть свои атрибуты и доступ к активу Asset предоставляется на основе правил, разрешающих доступ пользователям с определенными атрибутами.
![](https://habrastorage.org/getpro/habr/upload_files/b2e/3a9/dff/b2e3a9dffbdc8d9a57a6a43a18642917.png)
Вот несколько примеров работы с ABAC.
здесь мы разрешаем пользователю Alex любой доступ ко всем ресурсам:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": { "user": "ALex", "namespace": "*", "resource": "*", "apiGroup": "*" }
}
а здесь у нас пользователю kubelet разрешен доступ на чтение подов:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "kubelet",
"namespace": "*",
"resource": "pods",
"readonly": true
}
}
и наконец, если вы хотите предоставить учетной записи службы по умолчанию (в пространстве имен kube-system) полные права доступа к API с помощью ABAC, вам следует добавить эту строку в свой файл политики:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "system:serviceaccount:kube-system:default",
"namespace": "*",
"resource": "*",
"apiGroup": "*"
}
}
Режим Webhook
Режим Kubernetes webhook для авторизации выполняет синхронный HTTP-запрос, блокируя его до тех пор, пока удаленная HTTP-служба не ответит. Вы можете написать собственное программное обеспечение для обработки этого запроса или использовать сторонние решения. По сути, в режиме webhook веб-приложение отправляет сообщение на URL-адрес, когда происходят определенные события. Здесь можно провести аналогию с работой конвейера CI/CD когда Jenkins с помощью вебхуков перехватывает новые коммиты в репозитории и автоматически запускает задачи по их обработке. В рамках этой статьи мы не будем подробно останавливаться на данном режиме, а перейдем к более подробному рассмотрению RBAC.
Role Based Access Control
Ролевая модель доступа, пожалуй, является самой распространенной. В большинстве операционных систем у нас есть роли, которые мы назначаем пользователям. Например, Administrators, Users, Backup Operators и другие. K8s не является исключением.
Для лучшего понимания отличий RBAC от ABAC на рисунке ниже приведена сравнительная таблица. Как видно, у атрибутной модели есть ряд преимуществ, однако не все они полностью применимы именно к Kubernetes. Так описание, близкое к бизнес-терминам и упоминание ручного труда для k8s не совсем применимы.
![](https://habrastorage.org/getpro/habr/upload_files/2d5/ae9/c3d/2d5ae9c3d1afe217b704c6a9732d8b22.png)
Мы можем динамически настраивать политики с помощью API Kubernetes, используя группу rbac.authorization.k8s.io. Для использования режима RBAC необходимо запустить API сервер с соответствующим значением флага authorization-mode:
kube-apiserver --authorization-mode=RBAC --other-options --more-options
При использовании API в ролевой модели мы можем объявить четыре типа объектов Kubernetes: Role, Cluster Role, RoleBinding и ClusterRoleBinding. Для редактирования объектов RBAC можно использовать kubectl.
Role и ClusterRole
Объекты Role и ClusterRole содержат правила, которые представляют собой набор разрешений. При этом по умолчанию у нас естественно все запрещено (запрещено все, что явно не разрешено). Таким образом, правила в этих объектах содержат только разрешения. Role всегда устанавливает права доступа в пределах определенного пространства имен; при создании роли необходимо указать пространство имен, к которому она принадлежит.
Объект Cluster Role напротив, является ресурсом, не имеющим пространства имен. Этот объект может использоваться по-разному. Вы можете использовать ClusterRole для:
определения разрешений для ресурсов namespace и получения доступа в пределах отдельных пространств имен;
определения разрешений для ресурсов namespace и получения доступа во всех пространствах имен;
определения разрешения для ресурсов кластера.
Для лучшего понимания давайте посмотрим небольшой пример с объектом Role, в котором мы предоставляем доступ к подам на чтение (get, watch, list):
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"]
Объект ClusterRole может использоваться для предоставления тех же разрешений, что и Role. Поскольку Cluster Role относятся не к пространству имен, а к кластеру, вы также можете использовать их для предоставления доступа к:
ресурсам кластера (например, узлам)
конечным точкам, не связанным с ресурсами (например, /health)
ресурсы, размещенные в пространстве имен (например, модули), во всех пространствах имен
Например: вы можете использовать роль кластера, чтобы разрешить конкретному пользователю запускать kubectl get pods –all-namespaces, как показано в примере ниже:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: pod-reader
rules:
- apiGroups: [""]
#
# at the HTTP level, the name of the resource for accessing Secret
# objects is "secrets"
resources: ["pods"]
verbs: ["get", "watch", "list"]
RoleBinding
Объект RoleBinding предоставляет пользователю или группе пользователей определенные в роли. Данный объект содержит список пользователей, групп или учетных записей служб и ссылку на предоставляемую роль. RoleBinding предоставляет разрешения в рамках определенного пространства имен, в то время как ClusterRoleBinding предоставляет этот доступ в масштабах всего кластера.
Сущность RoleBinding может ссылаться на любую сущность Role в том же пространстве имен. В качестве альтернативы, RoleBinding может ссылаться на ClusterRole и привязывать эту сущность к пространству имен RoleBinding. Если вы хотите ClusterRole ко всем пространствам имен в вашем кластере, вы используете ClusterRoleBinding. Таким образом эти сущности связаны между собой. Ниже приведен пример привязки ролей, которая предоставляет роль "pod-reader" пользователю "jane" в пространстве имен default.
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
Чтобы предоставить разрешения для всего кластера, вы можете использовать ClusterRoleBinding. Следующая ClusterRoleBinding позволяет любому пользователю из группы manager читать секреты в любом пространстве имен.
apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
Как видно, работать с RBAC и назначать права достаточно просто. Для описания используется формат YAML.
Заключение
В этой статье мы рассмотрели основные принципы работы механизмов аутентификации и авторизации в Kubernetes, уделив особое внимание использованию ролевой модели RBAC, как основному механизму аутентификации. Стоит отметить, что мы рассмотрели лишь небольшую часть этой темы и многие аспекты остались за рамками этой статьи.
Примеры конфигураций в статье взяты с сайта kubernetes.io