Как известно в kubernetes нет нормального механизма для управления аутентификацией и авторизацией пользователей, как правило это все отдается на откуп отдельным системам и настраивают интеграцию с ними. Сегодня рассмотрим самый популярный вариант — это интеграция с keycloak. Цель будет следующая — keycloak развернем в k8s кластере, так как у меня нет AD сервера, то создадим пользователей там руками, заведем две группы admins и viewers, пользователи, добавленные в эти группы получат права в k8s либо как cluster-admin, либо как cluster-view. Это можно будет легко изменять, добавляя другие группы, меняя логику, например выдавая права на определенные нс и так далее. Но в качестве стартового базового примера, должно быть вполне достаточно и этого.
Установка keycloak
Добавляем репозиторий с чартом
helm repo add codecentric https://codecentric.github.io/helm-charts
Я заберу чарт локально, чтобы сразу настроить values под себя. Ищем добавленный чарт
helm search repo keycloak
Как видим доступно несколько версия. Я буду ставить обычный «legacy» keycloak, второй keycloakx больше предназначен для облачной архитектуры.
Забираем чарт локально:
helm pull codecentric/keycloak
tar xvf keycloak-18.4.4.tgz
rm -f keycloak-18.4.4.tgz
В values нужно добавить ингресс, сикрет с кредами для админки, а также добавить переменные
PROXY_ADDRESS_FORWARDING и KEYCLOAK_ENABLE_HTTPS, если этого не сделать, то при переходе на админ панель, будет циклическое перенаправление.
ingress:
# If `true`, an Ingress is created
enabled: true
# The name of the Ingress Class associated with this ingress
ingressClassName: nginx
# The Service port targeted by the Ingress
servicePort: http
# Ingress annotations
annotations:
## Resolve HTTP 502 error using ingress-nginx:
## See https://www.ibm.com/support/pages/502-error-ingress-keycloak-response
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
# Additional Ingress labels
labels: {}
# List of rules for the Ingress
rules:
-
# Ingress host
host: '{{ .Release.Name }}.dev.local'
# Paths for the host
paths:
- path: /
pathType: Prefix
# Example TLS configuration
tls:
- hosts:
- '{{ .Release.Name }}.dev.local'
secretName: "keycloak-tls"
extraEnv: |
- name: PROXY_ADDRESS_FORWARDING
value: "true"
- name: KEYCLOAK_ENABLE_HTTPS
value: "true"
secrets:
adminsecret:
type: Opaque
annotations: {}
labels: {}
stringData: {}
data:
KEYCLOAK_USER: YWRtaW4=
KEYCLOAK_PASSWORD: YWRtaW4=
extraEnvFrom: |
- secretRef:
name: keycloak-adminsecret
В base64 скрывается дефолтный admin/admin.
Для keycloak нужно завести свой сертификат, который будет подписан нашим самопальным CA. Это понадобится в будущем, для передачи этого сертификата в api-server, чтобы избежать ошибки x509.
Создание CA и сертификатов keycloak:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ca.key -out ca.crt -subj "/CN=DevLocal"
openssl genrsa -out keycloak.key 2048
openssl req -new -key keycloak.key -out keycloak.csr -subj "/CN=keycloak.dev.local"
openssl x509 -req -in keycloak.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out keycloak.crt -days 365 -extfile <(printf "subjectAltName=DNS:keycloak.dev.local")
kubectl create ns keycloak
kubectl create secret tls keycloak-tls --cert=keycloak.crt --key=keycloak.key -n keycloak
Устанавливаем helm релиз.
helm --kubeconfig ~/local_k8s.conf install keycloak -n keycloak -f values.yaml .
Создадим новый realm в UI.
Создадим две группы, admins и viewers.
Создадим пользователей в нужных группах. Я создам своего пользователя в группе admins и тестового в группе viewers.
И зададим пользователям пароль в их настройках.
Далее нужно настроить клиента.
После сохранения в расширенных настройках клиента:
Обязательно нужно выставить access type: confidential и прописать valid redirect url: http://localhost:8000/. Мы будем использовать плагин oidc-login для kubernetes, он поднимает веб-сервер на этом порту, соответственно после аутентификации в кейлок, он отдаст нам токен на этот адрес.
Также нам нужно добавить Client Scops — groups:
Затем в настройках клиента kubernetes указать этот скоп в Assigned Default Client Scopes.
И в Mappers, который идет следущим пунктом настроек создать маппер groups.
На этом с кейлок пока все. Переходим к настройкам api server.
На мастер ноде api server будет в виде static pod в директории /etc/kubernetes/manifest. По крайне мерее при развертывании куба с помощью kubeadm обычно это так, если не определен другой путь в вашей конфигурации kubelet. Я использую vagrant и чтобы не было заморочек с резолвингом моего DNS адреса из контейнера api server я добавлю опцию для использования кастомного DNS.
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 192.168.10.100
- --oidc-issuer-url=https://keycloak.dev.local/auth/realms/kubernetes
- --oidc-client-id=kubernetes
- --oidc-username-claim=preferred_username
- --oidc-groups-claim=groups
- --oidc-ca-file=/etc/kubernetes/pki/ca.keycloak.crt
- --oidc-username-prefix=-
Также в аргументы команды запуска api server добавляем нужные опции.
—oidc-issuer-url — адрес для аутентификации.
—oidc-client-id — клиент для kubernetes, который мы создавали выше в кейлок.
—oidc-username-claim — это указывает на то, какой именно claim из токена OIDC следует использовать в качестве имени пользователя. У нас это будет поле username, которое мы добавляли пользователю в кейлок.
—oidc-groups-claim — то, какой именно claim следует использовать при извлечении информации из токена о группах пользователя.
—oidc-ca-file — собственно путь до CA файла, который создавали ранее для keycloak.
—oidc-username-prefix — отключаем использование prefix в имени пользователя. По дефолту там будет прописан путь до кейклок, это нам не нужно.
Сохраняем файл, контейнер с api-server будет перезапущен kubelet автоматически.
Далее нужно установить oidc-login плагин для kubectl. Официальный github. Там есть инструкции по установке, которые я тут приводить не буду.
Теперь нужно сделать kubeconfig следующего вида:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJZGhnenJlTFg3Z0l3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRFd01UUXhNelU1TkRWYUZ3MHpOREV3TVRJeE5EQTBORFZhTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUN2RmZYR1JsL05aUVRBQ0JxV1R5MG91SHFLNUlVRUZvRkNKeVlCM2dvbkJFMGZzZG1hWXJMWUZWSlMKRHdETW52TUNvcWtmZXlURXZmNGRMaHNjUjN2UVpVNmI4eXAxUENiWUNMUm8xdk9lZm1ibFlsOTA5VE5LU1EzSgpwQzhSTEpFdFd5ckVCekFEcHRBZW13YlBJclBnRjRXZVNHYkRIT0xFS1l4ZXMrY2RBdFNENkd2VE41NG1vVXRxClAxSXpFV0U0T2ZDRUd6Rjh5c2xiMjh0cktDVkx4alp6UFE4L3hRa1RQN1hrb3IzaEF3dS85YWxqdmdPWlo4NEwKejlCTDRCZ0g3UXVaOVcvYzNPeDhVTzVFaHY2a0NvcEhDS1VEOXNDTmZYVDIxSWZRR1g2dTBuWVR4OGZGeVlYaQpXWUFhL0RFYkNqUzRJVml3MUJLYmhodGV4SUNwQWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJRRGQ3TUppaGY1MlhkWDdrQkxxYWhqQ2hpZXREQVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQVNKeUdKZVJMQwpqVEswczdlWFgzanRTSEJmRzUvcU9Uc1Nia1Yzbm0zWjc5VUpoNjRqZGE4cUFBbHF3WFFkN1lPZDRGYWtENjFtCk9VVVZSUURUUFlEL3BGYVhkLzA1N1RsdnY4RjUwREdPYnZiaDF3V0kzQ0xkVzBuTHkwNnNURExsaW8xOTZ4eHoKd2NVL29FZGM2WjRqU2hneWNGQVM0S3pSNDJlb2FmcDZScW9CeWdRc3N6V3FXTHNvY1FzZkhGdjlSUWYzZVpWUAp6eTBKeDF0ZHZMOFVrdmpyaTl6d3pHb2JTTExLRlZMOHdkS1BkLy9HdFdPN3Z6a1JKSkplRjVMN0p3eHkwQXpXCkREZ0t4R1JaR0N4Z1g0VVZ1YnBuTlowbkl2N2NTeVBRWVI0c0ZzVWdCMm5QREZjMTZnSitxTG9FaTg2aElNTHIKeG9EV3NRWjd1aU8rCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
server: https://192.168.10.10:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: oidc-user
name: oidc@kubernetes
current-context: oidc@kubernetes
kind: Config
preferences: {}
users:
- name: oidc-user
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://keycloak.dev.local/auth/realms/kubernetes
- --oidc-client-id=kubernetes
- --oidc-client-secret=h1HIQWMRv2ZkTc1nHk6pviFWnTJ4txts
- --oidc-extra-scope=profile
- --oidc-extra-scope=groups
- --authentication-timeout-sec=43200
command: kubectl
env: null
interactiveMode: IfAvailable
provideClusterInfo: false
—oidc-client-secret — это сикрет нашего клиента, который можно получить в настройках клиента на вкладке Credentials.
Пробуем запустить kubectl
kubectl --kubeconfig ~/keycloak_k8s.conf get pod
Нас автоматически перебросит на аутентификацию в keycloak и после ее прохождения мы получим ошибку о том, что у нас нет прав смотреть поды в ns default. Это хорошо, нас узнали, мы прошли аутентификацию, но не прошли авторизацию. Чтобы это сделать, создадим clusterrolebinding для группы admins.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: keycloak-admins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: /admins
apiGroup: rbac.authorization.k8s.io
Группа начинается со слеша. Так она создалась в кейлок, хотя явно я это не указывал. Это можно узнать если по дебажить полученный токен.
cat ~/.kube/cache/oidc-login/5811f7149864a900a0fb23dd6f7b49a4a102d476bb017289855ed82f0f6049a1
Увидим что-то вроде:
id_token можно вставить например в jwt.io и посмотреть payload. Там должно быть имя группы.
Создаем clusterrolebinding:
kubectl apply -f rbac-admin-kl.yaml
И проверяем заново любую команду с этим kubeconfig. Если что-то не работает, можно удалить кэш oidc-login или выбить пользователя из сессии в кейлоке.
Аналогично сделаем для viewes:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: keycloak-viewers
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: Group
name: /viewers
apiGroup: rbac.authorization.k8s.io
Внутри пользователя в Sessions можно закрыть сессию.
И очистим кэш oidc-login:
rm -rf ~/.kube/cache/oidc-login
Выполняем любую команду с тем же конфигом.
Видим что смотреть можем то, что разрешено кластерной роли view.
Таким образом можно базово настроить аутентификацию, авторизацию с помощью keycloak в kubernetes, адаптировать это под своей проект, CI/CD и так далее.