Pinniped как способ логина в Kubernetes через Active Directory
Многие пользователи Яндекс облака применяют сервис 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;
настроить права в куб-кластере;
создать файл шаблона кубконфига;
научить пользователей работать с 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 и подобные им — в некоторых заранее разрешенных неймспейсах.
Для этого создадим в нашем кубе 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, примерно такой:
Всё, мы настроили логин в кубернетес с учеткой из домена Windows.
Заключение
Описанный инструмент помогает избавиться от рутинной операции создания новых учеток для использования в Kubernetes. При этом Pinniped оказался не самым популярным — чтобы его найти, пришлось спуститься в самые глубины CNCF Landscape. Тем ценнее навык работы с ним.
Надеюсь, эта инструкция была для вас полезной. Если вы решали подобную задачу иначе — поделитесь в комментариях.