Небольшая инструкция о том, как используя Keycloak можно связать Kubernetes с вашим LDAP-сервером и настроить импорт пользователей и групп. Это позволит настраивать RBAC для ваших пользователей и использовать auth-proxy чтобы защитить Kubernetes Dashboard и другие приложения, которые не умеют производить авторизацию самостоятельно.
Установка Keycloak
Предположим что у вас уже есть LDAP-сервер. Это может быть Active Directory, FreeIPA, OpenLDAP или что-либо еще. Если LDAP-сервера у вас нет, то в принципе вы можете создавать пользователей прямо в интерфейсе Keycloak, либо использовать публичные oidc-провайдеры (Google, Github, Gitlab), результат получится почти такой же.
Первым делом установим сам Keycloak, установка может выполняться отдельно, так и сразу в Kubernetes-кластер, как правило если у вас имеется несколько Kubernetes-кластеров, было бы проще установить его отдельно. С другой стороны вы всегда можете использовать официальный helm-чарт и установить его прямо в ваш кластер.
Для хранения данных Keycloak вам понадобится база данных. По умолчанию используется h2
(все данные хранятся локально), но возможно также использовать postgres
, mysql
или mariadb
.
Если вы все же надумали установить Keycloak отдельно, более подробные инструкции вы найдете в официальной документации.
Настройка федерации
Первым делом создадим новый realm. Realm — это пространство нашего приложения. Каждое приложение может иметь свой realm с разными пользователями и настройками авторизации. Master realm используется самим Keycloak и использовать его для чего-нибудь еще неправильно.
Нажимаем Add realm
Option | Value |
---|---|
Name | kubernetes |
Display Name | Kubernetes |
HTML Display Name | <img src="https://kubernetes.io/images/nav_logo.svg" width="400" \> |
Kubernetes по умолчанию проверяет подтвержден у пользователя email или нет. Так как мы используем собственный LDAP-сервер, то тут эта проверка почти всегда будет возвращать false
. Давайте отключим представление этого параметра в Kubernetes:
Client scopes --> Email --> Mappers --> Email verified (Delete)
Теперь настроим федерацию, для этого перейдем в:
User federation --> Add provider... --> ldap
Приведу пример настройки для FreeIPA:
Option | Value |
---|---|
Console Display Name | freeipa.example.org |
Vendor | Red Hat Directory Server |
UUID LDAP attribute | ipauniqueid |
Connection URL | ldaps://freeipa.example.org |
Users DN | cn=users,cn=accounts,dc=example,dc=org |
Bind DN | uid=keycloak-svc,cn=users,cn=accounts,dc=example,dc=org |
Bind Credential | <password> |
Allow Kerberos authentication: | on |
Kerberos Realm: | EXAMPLE.ORG |
Server Principal: | HTTP/freeipa.example.org@EXAMPLE.ORG |
KeyTab: | /etc/krb5.keytab |
Пользователя keycloak-svc
нужно создать заранее на нашем LDAP-сервере.
В случае с Active Directory, достаточно просто выбрать Vendor: Active Directory и необходимые настройки подставятся в форму автоматически.
Нажимаем Save
Теперь перейдем:
User federation --> freeipa.example.org --> Mappers --> First Name
Option | Value |
---|---|
Ldap attribure | givenName |
Теперь включим маппинг групп:
User federation --> freeipa.example.org --> Mappers --> Create
Option | Value |
---|---|
Name | groups |
Mapper type | group-ldap-mapper |
LDAP Groups DN | cn=groups,cn=accounts,dc=example,dc=org |
User Groups Retrieve Strategy | GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE |
На этом настройка федерации закончена, перейдем к настройке клиента.
Настройка клиента
Создадим нового клиента (приложение которое будет получать пользователей из Keycloak). Переходим:
Clients --> Create
Option | Value |
---|---|
Client ID | kubernetes |
Access Type | confidential |
Root URL | http://kubernetes.example.org/ |
Valid Redirect URIs | http://kubernetes.example.org/* |
Admin URL | http://kubernetes.example.org/ |
Так же создадим scope для групп:
Client Scopes --> Create
Option | Value |
---|---|
Template | No template |
Name | groups |
Full group path | false |
И настроим mapper для них:
Client Scopes --> groups --> Mappers --> Create
Option | Value |
---|---|
Name | groups |
Mapper Type | Group membership |
Token Claim Name | groups |
Теперь нам нужно включить маппинг груп в нашем client scope:
Clients --> kubernetes --> Client Scopes --> Default Client Scopes
Выбираем groups в Available Client Scopes, нажимаем Add selected
Теперь настроим аутентификацию нашего приложения, переходим:
Clients --> kubernetes
Option | Value |
---|---|
Authorization Enabled | ON |
Нажимем save и на этом настройка клиента завершена, теперь на вкладке
Clients --> kubernetes --> Credentials
вы сможете получить Secret который мы будем использовать в дальнейшем.
Настройка Kubernetes
Настройка Kubernetes для OIDC-авторизации достаточно тривиальна и не является чем-то очень сложным. Все что вам нужно это положить CA-сертификат вашего OIDC-сервера в /etc/kubernetes/pki/oidc-ca.pem
и добавить необходимые опции для kube-apiserver.
Для этого обновите /etc/kubernetes/manifests/kube-apiserver.yaml
на всех ваших мастерах:
...
spec:
containers:
- command:
- kube-apiserver
...
- --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.pem
- --oidc-client-id=kubernetes
- --oidc-groups-claim=groups
- --oidc-issuer-url=https://keycloak.example.org/auth/realms/kubernetes
- --oidc-username-claim=email
...
А так-же обновите kubeadm конфиг в кластере, что бы не потерять эти настройки при обновлении:
kubectl edit -n kube-system configmaps kubeadm-config
...
data:
ClusterConfiguration: |
apiServer:
extraArgs:
oidc-ca-file: /etc/kubernetes/pki/oidc-ca.pem
oidc-client-id: kubernetes
oidc-groups-claim: groups
oidc-issuer-url: https://keycloak.example.org/auth/realms/kubernetes
oidc-username-claim: email
...
На этом настройка Kubernetes завершена. Вы можете повторить данные действия во всех ваших Kubernetes-кластерах.
Начальная авторизация
После данных действий вы уже будете иметь Kubernetes-кластер с настроенной OIDC-авторизацией. Единственный момент, что ваши пользователи пока что не имеют настроенного клиента как и собственного kubeconfig. Что бы эту проблему решить нужно настроить автоматическую выдачу kubeconfig пользователям после успешной авторизации.
Для этого можно использовать специальные web-приложения, которые позволяют провести аутентификацию пользователя а затем скачать готовый kubeconfig. Одно из самых удобных — это Kuberos, он позволяет в одном конфиге описать все Kubernetes-кластеры и легко переключаться между ними.
Для настройки Kuberos достаточно описать template для kubeconfig и запустить со следующими параметрами:
kuberos https://keycloak.example.org/auth/realms/kubernetes kubernetes /cfg/secret /cfg/template
Для более детальной информации смотрите Usage на Github.
Так же возможно использовать kubelogin если вы хотите производить авторизацию непосредственно на компьютере пользователя. В этом случае пользователю откроется браузер с формой авторизации на localhost.
Полученный kubeconfig можно проверить на сайте jwt.io. Просто скопируейте значение users[].user.auth-provider.config.id-token
из вашего kubeconfig в форму на сайте и сразу получите расшифровку.
Настройка RBAC
При настройке RBAC можно ссылаться как на имя пользователя (поле name
в jwt-токене), так и на группу пользователей (поле groups
в jwt-токене). Вот пример настройки прав для группы kubernetes-default-namespace-admins
:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: default-admins
namespace: default
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kubernetes-default-namespace-admins
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: default-admins
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: kubernetes-default-namespace-admins
Больше примеров для RBAC можно найти в официальной документации Kubernetes
Настройка auth-proxy
Есть замечательный проект keycloak-gatekeeper, который позволяет защитить любое приложение, предоставляя пользователю возможность аутентифицироваться на OIDC-сервере. Я покажу как можно настроить его на примере Kubernetes Dashboard:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kubernetes-dashboard-proxy
spec:
replicas: 1
template:
metadata:
labels:
app: kubernetes-dashboard-proxy
spec:
containers:
- args:
- --listen=0.0.0.0:80
- --discovery-url=https://keycloak.example.org/auth/realms/kubernetes
- --client-id=kubernetes
- --client-secret=<your-client-secret-here>
- --redirection-url=https://kubernetes-dashboard.example.org
- --enable-refresh-tokens=true
- --encryption-key=ooTh6Chei1eefooyovai5ohwienuquoh
- --upstream-url=https://kubernetes-dashboard.kube-system
- --resources=uri=/*
image: keycloak/keycloak-gatekeeper
name: kubernetes-dashboard-proxy
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /oauth/health
port: 80
initialDelaySeconds: 3
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /oauth/health
port: 80
initialDelaySeconds: 3
timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard-proxy
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: kubernetes-dashboard-proxy
type: ClusterIP