Система оркестрации Kubernetes или как ее называют сокращенно K8s на сегодняшний день стала стандартом де-факто в управлении контейнерами. Сейчас на K8s работает множество различных сложных микросервисных приложений. И для их грамотного функционирования важно правильно выполнить настройки безопасности в данной среде оркестрации.

В состав Kubernetes входит несколько основных компонентов. Это распределенное хранилище данных etcd, kube-scheduler, kube-controller-manager и kube-api-server. Вот о последнем мы и поговорим в сегодняшней статье. 

Но сначала рассмотрим архитектуру K8s и какие роли играют перечисленные выше компоненты.

Компонент 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 получает наш запрос будут выполнены шаги, представленные на рисунке.

Если наша инфраструктура кластера 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 предоставляется на основе правил, разрешающих доступ пользователям с определенными атрибутами. 

Вот несколько примеров работы с 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 не совсем применимы.

Мы можем динамически настраивать политики с помощью 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

Комментарии (0)