Pinniped как способ логина в Kubernetes через Active Directory

e9a0a3806a291ff03264e4997824c5cb.png

Многие пользователи Яндекс облака применяют сервис Managed Service for Kubernetes. При этом учетные записи сотрудников организаций хранятся на контроллерах домена Windows. Удобно и правильно логиниться в Kubernetes учетными записями из Windows-домена, оптимизируя ресурсы DevOps и сисадминов. Но без специального инструмента сделать это нельзя. 

Меня зовут Дмитрий Глушков, я системный инженер эксплуатации в Lamoda Tech. Мои поиски удобного решения задачи привели к программе Pinniped. Она поможет использовать учетные записи Windows AD для предоставления и контроля доступа сотрудников в Kubernetes. Разберемся, как ее установить и использовать.

Что такое Pinniped 

Pinniped (Пиннипед) — это программа, которая позволяет логиниться в Kubernetes-кластеры, в которых у вас нет доступа к их мастер-нодам (пользуясь программой kubectl и специальным файлом-шаблоном кубконфига). Вы можете использовать учетки от разных провайдеров учетных записей (identity providers), например, MS Windows Active Directory. Это подходящее решение, например, в Яндекс облаке для их управляемых куб-кластеров.

Pinniped состоит из двух частей — супервизора и консьержа. Супервизор нужен, чтобы можно было залогиниться с учеткой из Active Directory, а консьерж принимает все запросы в куб-апи и проверяет, что они корректно авторизованы супервизором, и если да, то проксирует их в куб-апи. Обе части — это просто поды в кубе.

Источник: pinniped.dev

Чтобы получить работающую систему, необходимо:

  • подготовить необходимые ресурсы;

  • установить и настроить Pinniped;

  • настроить права в куб-кластере;

  • создать файл шаблона кубконфига;

  • научить пользователей работать с Pinniped. 

Звучить объемно, но выполнимо. Поехали!

1. Подготовка к установке

Предполагается, что у вас есть следующие ресурсы, и вы можете ими управлять:

  • аккаунт в Яндекс облаке, в котором есть работающий managed куберкластер;

  • контроллер домена Windows, к которому есть сетевой доступ из Яндекс облака;

  • tls-certificate, который вы можете создать;

  • DNS-запись, которую вы можете создать, и которая будет работать у вас в сети и показывать на IP-адрес в Яндекс облаке.

2. Установка

Просто создаем в нашем куб-кластере объекты из ямл-файлов от создателей Pinniped:

kubectl apply -f https://get.pinniped.dev/v0.32.0/install-pinniped-supervisor.yaml

kubectl apply -f https://get.pinniped.dev/v0.32.0/install-pinniped-concierge-crds.yaml

kubectl apply -f https://get.pinniped.dev/v0.32.0/install-pinniped-concierge-resources.yaml

2.1 Load balancer для консьержа

После выполнения этих трех команд в куб-кластере в неймспейсе pinniped-concierge появится сервис с именем pinniped-concierge-impersonation-proxy-load-balancer. Мы будем использовать этот сервис как лоад-балансер, который будет принимать запросы для Pinniped-консьержа. Чтобы этот сервис стал работать как лоад-балансер в Яндекс облаке, надо в его конфиге добавить две аннотации:

yandex.cloud/load-balancer-type: internal

yandex.cloud/subnet-id: id-подсети

Тут internal — это тип лоад-балансера NLB в ЯО, такой как в документации Яндекс облака, а id-подсети — это подсетка в Яндекс облаке, в которой будет создан лоад-балансер, как только мы добавим эти две аннотации и сохраним конфиг. Покажу только важную для нас сейчас часть конфигурации, неважные строки пропущены. Вот что должно получиться в итоге:

apiVersion: v1
kind: Service
metadata:
 annotations:
   . . .
   yandex.cloud/load-balancer-type: internal
   yandex.cloud/subnet-id: id-подсети
 labels:
   app: pinniped-concierge
 name: pinniped-concierge-impersonation-proxy-load-balancer
 namespace: pinniped-concierge
 . . .
spec:
 . . .

Как только облако создаст лоад-балансер, у него появится IP-адрес, и можно проверить, что он работает. Вот ожидаемый ответ:

% curl -k https://ip-addr-of-load-balancer-of-concierge
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\": decision made by impersonation-proxy.concierge.pinniped.dev",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

2.2 Load balancer для супервизора

Делаем сервис+NLB  для супервизора в той же подсетке, что и у консьержа:

apiVersion: v1
kind: Service
metadata:
  annotations:
    yandex.cloud/load-balancer-type: internal
    yandex.cloud/subnet-id: id-подсети-из-пункта-2.1
  name: pinniped-supervisor-loadbalancer
  namespace: pinniped-supervisor
spec:
  ports:
  - targetPort: 8443
    port: 443
    protocol: TCP
  selector:
    app: pinniped-supervisor
  type: LoadBalancer

Как только облако создаст лоад-балансер для супервизора, у него появится IP-адрес. Надо сделать DNS-запись, указывающую на этот IP-адрес:

pinniped-supervisor.ваш_домен.да → ip-адрес лоад-балансера супервизора.

2.3 Куб-секрет с TLS-сертификатом супервизора

Супервизор обязательно требует использовать TLS-защищенное подключение к нему, поэтому создадим куб-секрет, и положим туда сертификат и приватный ключ. TL-сертификат надо создать, он должен удостоверять url pinniped-supervisor.ваш_домен.да.

apiVersion: v1
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUS...
  tls.key: LS0tLS1CRUdJTiBQUkl...
kind: Secret
metadata:
  name: supervisor-lb-tls-cert
  namespace: pinniped-supervisor
type: kubernetes.io/tls

2.4 FederationDomain

Создаем объект, который нужен для указания url, на котором будет работать Cупервизор, и тут же указываем секрет с TLS-сертификатом для этого url.

apiVersion: config.supervisor.pinniped.dev/v1alpha1
kind: FederationDomain
metadata:
  name: my-fd
  namespace: pinniped-supervisor
spec:
  issuer: https://pinniped-supervisor.ваш_домен.да
  tls:
    secretName: supervisor-lb-tls-cert

После этого шага выполнение запроса типа:

% curl https://pinniped-supervisor.ваш_домен.да должно показывать, что TLS-сертификат правильный.

2.5 Секрет для подключения в домену Active Directory

Создаем секрет для подключения в домену AD. Пароль обязательно должен быть в кавычках. В домене Windows надо создать учетку, которая может читать учетные записи и группы, и использовать её.

apiVersion: v1
kind: Secret
metadata:
  name: active-directory-bind-credentials
  namespace: pinniped-supervisor
type: kubernetes.io/basic-auth
stringData:
  # The dn (distinguished name) of your Active Directory bind account.
  username: CN=s-pinniped,OU=Service Accounts,DC=office,DC=ru
  # The password of your Active Directory bind account.
  password: "ТУТПАРОЛЬОТУЧЁТКИ..."

2.6 ActiveDirectoryIdentityProvider

Продолжаем: делаем ActiveDirectoryIdentityProvider. В этом, например, LDAP-фильтр проверяет, что учетка юзера есть в AD группе admins и/или в AD группе developers. В поле certificateAuthorityData надо записать сертификат контроллера домена, только открытую часть. Используйте подсказку по фильтрам LDAP.

apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: ActiveDirectoryIdentityProvider
metadata:
  name: ad-idp
  namespace: pinniped-supervisor
spec:
  host: ad-dc1.office.ru
  userSearch:
    base: "OU=Users,OU=Offices,DC=office,DC=ru"
    filter: "&(objectClass=person)(userPrincipalName={})(|(memberOf=CN=admins,OU=Groups,DC=office,DC=ru)(memberOf=CN=developers,OU=Groups,DC=office,DC=ru))"
    attributes:  
      username: "sAMAccountName"
  groupSearch:
    base: "ou=Groups,DC=office,DC=ru"
    filter: "&(objectClass=group)(member={})"
  tls:
    certificateAuthorityData: LS0t...
  bind:
    secretName: active-directory-bind-credentials

2.7 JWTAuthenticator

Идем дальше. В issuer пишем url лоад-балансера супервизора. В audience не принципиально, что будет написано, но нужно, чтобы для разных куб-кластеров это значение было разным. В certificateAuthorityData кладем сертификат от супервизора, только открытую часть.

apiVersion: authentication.concierge.pinniped.dev/v1alpha1
kind: JWTAuthenticator
metadata:
  name: jwt-auth
spec:
  issuer: https://pinniped-supervisor.ваш_домен.да
  audience: from-pinniped
  tls:
    certificateAuthorityData: LS0t...

Установка закончена.

3. Конфигурация Kubernetes

Допустим, мы хотим, чтобы после успешного логина сотрудник мог в кубере успешно выполнять команды типа k get pods, k logs и подобные им — в некоторых заранее разрешенных неймспейсах.

  1. Для этого создадим в нашем кубе ClusterRole, и будем её назначать (bind) на неймспейсы и группы AD.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pinniped-view
rules:
- apiGroups: [""]
  resources: ["pods", "configmaps"]
  verbs: ["get", "list"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]

2. Для каждого неймспейса, доступ в который мы хотим разрешить, делаем RoleBinding, который будет связывать кластер-роль с неймспейсом и с AD-группой, например, для неймспейса, который называется «util» и группы AD developers.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pinniped-view-util-developers
  namespace: util
subjects:
- kind: Group
  name: developers@office.ru
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: pinniped-view
  apiGroup: rbac.authorization.k8s.io

Чтобы наши пользователи могли логиниться в кубер-кластер, им надо предоставить файл — ‎шаблон кубконфига, который будет направлять логин в Pinniped. 

Чтобы сделать этот файл, на свой комп ставим Pinneped-CLI. Логинимся в целевой куб-кластер как обычно, например:

kubectl config use-context ваш_кластер

Затем выполняем команду:

pinniped get kubeconfig > kubeconfig-template.yml 

Теперь в kubeconfig-template.yml файле у нас есть шаблон для авторизации. Этот файл раздаем коллегам, коллеги устанавливают себе Pinniped и используют этот файл как обычный куб-конфиг файл. В файле нет никакой конфиденциальной информации типа паролей и токенов.

5. Пользуемся логином 

Для работы с кластером коллеги будут выполнять подобную команду:

kubectl -n util get pods --kubeconfig kubeconfig-template.yml 

Появится приглашение на ввод имени пользователя Username — тут надо написать свою почту с доменом.

Дальше появится приглашение на ввод пароля — пишем свой пароль домена Windows.

Если все работает правильно, то должен показаться список подов в неймспейсе util, примерно такой:  

90576dfabbe157515753026046e7d985.png

Всё, мы настроили логин в кубернетес с учеткой из домена Windows.

Заключение

Описанный инструмент помогает избавиться от рутинной операции создания новых учеток для использования в Kubernetes. При этом Pinniped оказался не самым популярным — чтобы его найти, пришлось спуститься в самые глубины CNCF Landscape. Тем ценнее навык работы с ним. 

Надеюсь, эта инструкция была для вас полезной. Если вы решали подобную задачу иначе — поделитесь в комментариях.

© Habrahabr.ru