Сегодня я подробно разберу настройку аутентификации в Kubernetes с помощью Dex в связке с LDAP, а также покажу, как можно добавлять статических пользователей в Dex.
В статье не буду останавливаться на основных принципах работы Dex, а сразу перейду к установке и настройке LDAP. Познакомиться с принципами работы Dex можно в этой статье.
Что будем делать:
- Установим OpenLDAP и настроим на нем поддержку STARTTLS.
- Опишем структуру LDAP-каталога нашей организации.
- Включим поддержку OIDC (OpenID Connect) на kube-api-серверах.
- Получим SAN-сертификат для доменов, которые будет использовать Dex.
- Установим Dex и Dex-auth, где мы опишем LDAP-каталог и статических пользователей.
- Сгенерируем kubeconfig нашего пользователя для работы с кластером.
- Настроим RBAC-авторизацию для групп и пользователей в кластере.
Итак, поехали.
Показывать буду на примере уже готового кластера Kubernetes с Helm версии 3 и Ingress, а также тремя доменными именами.
Устанавливаем и настраиваем OpenLDAP-сервер
В качестве LDAP будем использовать OpenLDAP на дистрибутиве ubuntu 18.04.
Имя нашего сервера: openldap.dtln.cloud.
Подключаемся к серверу и приступаем к установке OpenLDAP. В процессе установки нам предложат установить пароль:
sudo apt update
sudo apt install slapd ldap-utils
Переконфигурируем OpenLDAP для нашего домена:
sudo dpkg-reconfigure slapd
Выбираем No:
Вводим доменное имя:
Вводим имя организации:
Повторяем ввод пароля:
Включаем поддержку STARTTLS. Ставим необходимые пакеты:
sudo apt install gnutls-bin ssl-cert
- Генерируем CA-ключ:
sudo sh -c "certtool --generate-privkey > /etc/ssl/private/cakey.pem"
- Описываем нашу организацию в /etc/ssl/ca.info:
cn = DTLN Company
ca
cert_signing_key
- Генерируем CA-сертификaт и ключ, который в дальнейшем будем использовать:
sudo certtool --generate-self-signed --load-privkey /etc/ssl/private/cakey.pem --template /etc/ssl/ca.info --outfile /etc/ssl/certs/cacert.pem
sudo certtool --generate-privkey --bits 1024 --outfile /etc/ssl/private/openldap_key.pem
- Создаем шаблон для сертификата /etc/ssl/openldap.info:
organization = DTLN Company
cn = openldap.dtln.cloud
tls_www_server
encryption_key
signing_key
expiration_days = 3650
- Генерируем сам сертификат:
sudo certtool --generate-certificate --load-privkey /etc/ssl/private/openldap_key.pem --load-ca-certificate /etc/ssl/certs/cacert.pem --load-ca-privkey /etc/ssl/private/cakey.pem --template /etc/ssl/openldap.info --outfile /etc/ssl/certs/openldap.pem
- Настраиваем OpenLDAP для работы со STARTTLS. Вот что должно быть внутри файла certinfo.dif:
dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/openldap.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/openldap_key.pem
- Применяем созданный нами файл следующей командой:
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f certinfo.dif
- Назначаем права доступа на сертификат и перезапускаем наш сервис:
sudo chgrp openldap /etc/ssl/private/openldap_key.pem
sudo chmod 0640 /etc/ssl/private/openldap_key.pem
sudo gpasswd -a openldap ssl-cert
sudo systemctl restart slapd.service
- Чтобы проверить работу нашего сервиса с сертификатами, добавляем в /etc/ldap/ldap.conf строчку:
TLS_CACERT /etc/ssl/certs/cacert.pem
- Проверяем работу через STARTTLS:
ldapwhoami -H ldap://openldap.dtln.cloud -x -ZZ
В случае успеха получаем:
В случае проблемы проверяем еще раз пути до сертификатов и выполнение команд:
Описываем структуру LDAP-каталога
Теперь опишем структуру каталога, создадим группы и двуx пользователей, которые будут ходить в Kubernetes. Создаем файл content.ldif:
dn: ou=People,dc=openldap,dc=dtln,dc=cloud
objectClass: organizationalUnit
ou: People
dn: cn=jane,ou=People,dc=openldap,dc=dtln,dc=cloud
objectClass: person
objectClass: inetOrgPerson
sn: doe
cn: jane
mail: janedoe@openldap.dtln.cloud
userpassword: foo_password
dn: cn=john,ou=People,dc=openldap,dc=dtln,dc=cloud
objectClass: person
objectClass: inetOrgPerson
sn: doe
cn: john
mail: johndoe@openldap.dtln.cloud
userpassword: bar_password
# Group definitions.
dn: ou=Groups,dc=openldap,dc=dtln,dc=cloud
objectClass: organizationalUnit
ou: Groups
dn: cn=admins,ou=Groups,dc=openldap,dc=dtln,dc=cloud
objectClass: groupOfNames
cn: admins
member: cn=jane,ou=People,dc=openldap,dc=dtln,dc=cloud
dn: cn=developers,ou=Groups,dc=openldap,dc=dtln,dc=cloud
objectClass: groupOfNames
cn: developers
member: cn=jane,ou=People,dc=openldap,dc=dtln,dc=cloud
member: cn=john,ou=People,dc=openldap,dc=dtln,dc=cloud
Разворачиваем описанную структуру командой:
ldapadd -x -D cn=admin,dc=openldap,dc=dtln,dc=cloud -W -f content.ldif
Удостоверимся в наличии наших пользователей в каталоге:
ldapwhoami -vvv -h openldap.dtln.cloud -p 389 -D cn=john,ou=People,dc=openldap,dc=dtln,dc=cloud -x -w bar_password -ZZ
ldapwhoami -vvv -h openldap.dtln.cloud -p 389 -D cn=jane,ou=People,dc=openldap,dc=dtln,dc=cloud -x -w foo_password -ZZ
Подключаем поддержку OpenID Connect
Переходим к настройке кластера Kubernetes.
Домен dex.ash.dtln.cloud используем для обращения Dex к API-серверу, а login.ash.dtln.cloud – для получения доступа к кластеру.
Мы разворачивали кластер без установщиков kubeadm или kops, так что OIDC-конфигурацию можно сразу добавлять в systemd-юнит. В остальных случаях настройку лучше сделать с помощью этих утилит.
Редактируем юнит /etc/systemd/system/kube-apiserver.service и добавляем параметры запуска в секцию ExecStart:
--oidc-client-id=dex-k8s-authenticator \
--oidc-groups-claim=groups \
--oidc-username-claim=email \
--oidc-issuer-url=https://dex.ash.dtln.cloud/ \
Рестартуем наши API, проверяем, что они поднялись:
sudo systemctl daemon-reload
sudo systemctl restart kube-apiserver
sudo systemctl status kube-apiserver
Создаем мультидоменный сертификат
Теперь переходим в кластер Kubernetes.
Ставим cert-manager, используя Helm:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager --namespace kube-system jetstack/cert-manager --version v0.13.0
Описываем запрос на получение SAN-сертификата для наших доменов в cert.yml:
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-dex
spec:
acme:
email: kubernetes@dataline.ru
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-key-dex
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: auth-dex
namespace: kube-system
spec:
secretName: cert-auth-dex
issuerRef:
kind: ClusterIssuer
name: letsencrypt-dex
commonName: dex.ash.dtln.cloud
dnsNames:
- dex.ash.dtln.cloud
- login.ash.dtln.cloud
Выполняем команду:
kubectl apply -f cert.yaml
Теперь смотрим состояние нашего запроса на получение сертификата следующими командами:
kubectl get certificates --all-namespaces
kubectl get challenges --all-namespaces
Ждем подтверждения, процесс может занять некоторое время:
Устанавливаем Dex
Для Dex нам понадобятся ca.crt, ca.key c мастер-сервера. Обычно они лежат в каталоге /etc/kubernetes/pki/
Также нужен сгенерированный нами раннее CA-сертификат с OpenLDAP, расположенный по пути /etc/ssl/certs/cacert.pem
Скачиваем исходники dex-authenticator и переходим в каталог:
git clone git@github.com:mintel/dex-k8s-authenticator.git
cd dex-k8s-authenticator/
Готовим конфиг для Dex-auth, вставляем содержимое ранее скопированного ca.crt, важно соблюдать отступы:
---
global:
deployEnv: prod
dexK8sAuthenticator:
clusters:
- name: 'ash.dtln.cloud'
short_description: "k8s cluster"
description: "Kubernetes cluster"
issuer: https://dex.ash.dtln.cloud/
k8s_master_uri: https://kubernetes.dtln.cloud:6443 # url or ip
client_id: dex-k8s-authenticator
static_context_name: false
client_secret: acDEgDEcIg7RX0U7A9hlW2pGGraHDuMAZ4qFEKg2fUHHxr8
redirect_uri: https://login.ash.dtln.cloud/callback
k8s_ca_pem: |
-----BEGIN CERTIFICATE-----
# Вставляем содержимое ca.crt
-----END CERTIFICATE-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- login.ash.dtln.cloud
tls:
- secretName: cert-auth-dex
hosts:
- login.ash.dtln.cloud
Переводим содержимое cacert.pem в base64, чтобы добавить его в конфиг:
cacert.pem | base64
Готовим конфиг для Dex, также важно соблюдать отступы. По желанию добавляем статических пользователей в секцию staticPasswords и генерируем им пароль в формате bcrypt:
---
global:
deployEnv: prod
tls:
certificate: |-
-----BEGIN CERTIFICATE-----
#содержимое ca.crt
-----END CERTIFICATE-----
key: |-
-----BEGIN RSA PRIVATE KEY-----
#содержимое ca.key
-----END RSA PRIVATE KEY-----
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- dex.ash.dtln.cloud
tls:
- secretName: cert-auth-dex
hosts:
- dex.ash.dtln.cloud
serviceAccount:
create: true
name: dex-auth-sa
config:
issuer: https://dex.ash.dtln.cloud/
storage:
type: kubernetes
config:
inCluster: true
web:
http: 0.0.0.0:5556
frontend:
theme: "coreos"
issuer: "kube-dtln"
issuerUrl: "https://login.ash.dtln.cloud"
expiry:
signingKeys: "6h"
idTokens: "24h"
logger:
level: debug
format: json
oauth2:
responseTypes: ["code", "token", "id_token"]
skipApprovalScreen: true
connectors:
- type: ldap
id: ldap
name: LDAP
config:
insecureNoSSL: false
insecureSkipVerify: false
startTLS: true # Включаем безопасное соединение
rootCAData: |-
#Содержимое cacert.pem в base64
#соблюдаем отступы
host: openldap.dtln.cloud:389
usernamePrompt: Email Address
userSearch:
baseDN: ou=People,dc=openldap,dc=dtln,dc=cloud
filter: "(objectClass=person)"
username: mail
idAttr: DN
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=Groups,dc=openldap,dc=dtln,dc=cloud
filter: "(objectClass=groupOfNames)"
userMatchers:
- userAttr: DN
groupAttr: member
nameAttr: cn
staticClients:
- id: dex-k8s-authenticator
name: Kubernetes Dev Cluster
secret: 'acDEgDEcIg7RX0U7A9hlW2pGGraHDuMAZ4qFEKg2fUHHxr8'
redirectURIs:
- https://login.ash.dtln.cloud/callback
enablePasswordDB: True
staticPasswords:
# Создаем статического пользователя с паролем в base64
- email: "admin@dtln.cloud"
# bcrypt hash of the string "password"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
- Теперь ставим dex и dex-auth-чарты из текущего каталога с нашими конфигами:
helm install dex --namespace kube-system --values dex.yaml charts/dex
helm install dex-auth --namespace kube-system --values dex-auth.yml charts/dex-k8s-authenticator
Генерируем kubeconfig
Ждем, когда поднимутся pod’ы. Смотрим их логи и идем по адресу https://login.ash.dtln.cloud. Логинимся под созданной нами учеткой, для авторизации мы использовали mail-формат логина.
Для static-записи выбираем вход через mail:
После успешной аутентификации нас перенаправляют на страницу с инструкцией Dex-auth. C ее помощью мы генерируем kubeconfig-файл. В дальнейшем с ним мы будем подключаться к нашему кластеру:
Теперь попытаемся получить доступ к кластеру и получаем ошибку. Все верно, наш пользователь не имеет прав, так что переходим к настройке RBAC.
Настраиваем RBAC-авторизацию
Для примера привяжем статического пользователя к системной роли cluster-admin, а пользователей группы developers – к роли view, позволяющей только просматривать ресурсы. Тогда содержимое файла crb.yml должно быть таким:
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: dex-admin
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: User
name: "admin@dtln.cloud"
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: dex-developers
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: Group
name: "developers"
Переключаемся на основной контекст и применяем созданный yaml-файл на кластер:
kubectl config set-context default
kubectl apply -f crb.yml
Смотрим доступные контексты и переключаемся обратно на нашего пользователя:
kubectl config get-contexts
kubectl config set-context johndoe-ash.dtln.cloud
На этом настройка Dex в связке с OpenLDAP закончена.
Пара советов напоследок:
- Если возникают проблемы, первым делом нужно проверить форматирование yaml-файлов и обратить внимание на отступы.
- Слэши в адресах должны соответствовать примерам.
- Не забудьте посмотреть логи pod’ов Dex, Dex-auth, а также журналы юнитов kube-api на мастерах.