Статья
Вход в Kubernetes через Keycloak (OIDC)
Нужен был доступ в Kubernetes для инженеров без раздачи клиентских сертификатов — в тесте поднял Kubespray и OIDC через уже живой Keycloak.
Нужно было дать инженерам и разработчикам доступ в Kubernetes. Постоянно выпускать клиентские сертификаты под каждого — неудобно: ротация, отзыв, раздача файлов. В тестовой среде поднял кластер через Kubespray и повесил вход на уже используемый Keycloak по OIDC: человек логинится в realm, kubectl получает короткий ID token, права режет RBAC.
Что где стоит
| Что | Где |
|---|---|
| Keycloak 26.x | keycloak.example.com |
| Realm | k8s |
| Клиент | kubernetes, public, Standard flow, PKCE S256 |
| Пользователи | подключённый LDAP/AD к realm |
| Группа в токене | developers |
| Control plane | cp-01.example.com |
Как это связано
Keycloak только называет пользователя и группы. Что можно делать в кластере, решает RBAC:
| Поле ID token | Кто это в API |
|---|---|
preferred_username |
пользователь oidc:dev.user |
groups: ["developers"] |
группа oidc:developers |
aud |
клиент kubernetes |
iss |
https://keycloak.example.com/realms/k8s |
Группа developers — группа Keycloak, в неё добавлены пользователи из LDAP/AD.
Keycloak
В Realm settings → General → Frontend URL должен быть указан — keycloak.example.com, пример проверки issuer:
curl -s https://keycloak.example.com/realms/k8s/.well-known/openid-configuration | jq .issuer
"https://keycloak.example.com/realms/k8s"
Клиент kubernetes:
- Client type: OpenID Connect
- Client authentication: Off
- Standard flow: On
- Direct access grants: Off
- PKCE method: S256
- Valid redirect URIs:
http://localhost:8000
http://localhost:8000/*
http://127.0.0.1:8000
http://127.0.0.1:8000/*
- Web origins:
http://localhost:8000
Mapper групп: Clients → kubernetes → Client scopes → kubernetes-dedicated → Mappers → Group Membership.
- Name:
groups - Token Claim Name:
groups - Full group path: Off
- Add to ID token: On
- Add to access token: On
Проверка до изменений в Kubernetes: Clients → kubernetes → Client scopes → Evaluate. Пользователь из федерации, scope openid. Смотреть Generated ID token.
Ожидаемый токен:
{
"iss": "https://keycloak.example.com/realms/k8s",
"aud": "kubernetes",
"preferred_username": "dev.user",
"groups": ["developers"]
}
В discovery-документе в claims_supported нет groups. Это нормально: список стандартный, mapper всё равно кладёт claim в токен.
Сертификат
В --oidc-ca-file нужен корневой публичный CA сертификат. На control plane положить сертификат в виде .crt в:
/etc/kubernetes/ssl/keycloak-ca.crt
Тот же CA в base64 лежит в inventory как kube_oidc_ca_cert, чтобы следующий прогон Kubespray положил файл сам. Проверка с control plane:
curl --cacert /etc/kubernetes/ssl/keycloak-ca.crt \
https://keycloak.example.com/realms/k8s/.well-known/openid-configuration
Kubespray
В — inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml:
kube_oidc_auth: true
kube_oidc_url: https://keycloak.example.com/realms/k8s
kube_oidc_client_id: kubernetes
kube_oidc_ca_file: "{{ kube_cert_dir }}/keycloak-ca.crt"
kube_oidc_ca_cert: base64_root_ca_cert
kube_oidc_username_claim: preferred_username
kube_oidc_username_prefix: "oidc:"
kube_oidc_groups_claim: groups
kube_oidc_groups_prefix: "oidc:"
Запустить — ansible-playbook для применения изменений в кластере:
ansible-playbook -i inventory/lab/hosts.yaml cluster.yml
После смены OIDC-переменных на рабочем кластере нужно выполнить:
kubeadm init phase upload-config all --config /etc/kubernetes/kubeadm-config.yaml
kubeadm init phase control-plane apiserver --config /etc/kubernetes/kubeadm-config.yaml
Kubelet подхватывает манифест и перезапускает static pod. Для проверки можно выполнить:
grep oidc- /etc/kubernetes/manifests/kube-apiserver.yaml
Ожидаемые флаги:
--oidc-issuer-url=https://keycloak.example.com/realms/k8s
--oidc-client-id=kubernetes
--oidc-ca-file=/etc/kubernetes/ssl/keycloak-ca.crt
--oidc-username-claim=preferred_username
--oidc-groups-claim=groups
--oidc-username-prefix=oidc:
--oidc-groups-prefix=oidc:
RBAC
Подготовить пример binding-а:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developers-edit
namespace: dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: oidc:developers
Создать .yaml файл, dev пространство (namespace) и применить:
kubectl apply -f .yaml
Проверка, без логина пользователя:
kubectl auth can-i create deployments -n dev --as=oidc:dev.user --as-group=oidc:developers
kubectl auth can-i get pods -n kube-system --as=oidc:dev.user --as-group=oidc:developers
Первая команда — yes, вторая — no.
Kubeconfig пользователя
Нужен плагин int128/kubelogin: бинарник kubectl-oidc_login, в kubeconfig команда — kubectl, аргумент — oidc-login.
macOS (Homebrew):
brew install int128/kubelogin/kubelogin
Linux (бинарник с Releases):
# amd64, для arm64 нужно использовать kubelogin_linux_arm64.zip
curl -fsSL -o kubelogin.zip \
https://github.com/int128/kubelogin/releases/latest/download/kubelogin_linux_amd64.zip
unzip -o kubelogin.zip kubelogin
sudo install -m 0755 kubelogin /usr/local/bin/kubectl-oidc_login
rm -f kubelogin.zip kubelogin
Windows (бинарник с Releases):
Скачать kubelogin_windows_amd64.zip с GitHub, из архива взять/переименовать в kubectl-oidc_login.exe и положить в каталог из PATH.
Проверка на любой ОС:
kubectl oidc-login --help
Пример oidc.kubeconfig файла в ~/.kube/:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: CERT
server: https://cp-01.example.com:6443
name: lab
contexts:
- context:
cluster: lab
user: oidc
name: oidc@lab
current-context: oidc@lab
kind: Config
users:
- name: oidc
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://keycloak.example.com/realms/k8s
- --oidc-client-id=kubernetes
command: kubectl
env: null
interactiveMode: IfAvailable
provideClusterInfo: false
Проверка:
kubectl --kubeconfig ~/.kube/oidc.kubeconfig auth whoami
Первый запуск открывает браузер для аутентификации в Keycloak.
Успешный — whoami:
ATTRIBUTE VALUE
Username oidc:user
Groups [oidc:developers system:authenticated]
Extra: authentication.kubernetes.io/credential-id [JTI=e464595d-9250-1213-c46b-b45d69b6fbc7]
Итог
Новый человек в той же группе developers получает тот же oidc.kubeconfig. Отдельный файл для каждого не нужен: личность берётся из логина Keycloak. Keycloak называет человека и группу, RBAC решает, что можно в Kubernetes-кластере.