
В какой-то момент почти в каждом Kubernetes-кластере наступает день, когда kubeconfig с правами cluster-admin перестаёт быть временным решением и внезапно становится: так исторически сложилось. Пользователей становится больше, доступы плодятся, а вопрос: кто и зачем может удалить namespace в проде? повисает в воздухе без логичного ответа.
До определённого масштаба это ещё можно терпеть: сертификаты, статические токены, ручная раздача доступов. Но как только в кластере появляются: несколько команд, требования по аудитам, SSO или просто здравый смысл — становится ясно, что Kubernetes нужно подключать к нормальной системе аутентификации.
Kubernetes из коробки умеет работать с OIDC (OpenID Connect), и это, пожалуй, самый адекватный способ интегрировать его с внешним Identity Provider. В роли такого провайдера часто выступает Keycloak: open-source, self-hosted, с поддержкой групп, ролей и интеграцией с LDAP/AD. В общем, всё, что обычно уже есть в инфраструктуре, либо планируется к появлению.
В этой статье мы разберём практическую интеграцию Kubernetes с Keycloak через OIDC:
что именно происходит при аутентификации пользователя
какие настройки нужны в Keycloak и API Server
как связать группы из Keycloak с Kubernetes RBAC
и какие грабли чаще всего встречаются в процессе
Цель — чтобы после прочтения у вас было чёткое понимание, как и зачем это работает, и вы могли спокойно внедрить OIDC в своём кластере.
Зачем вообще OIDC в Kubernetes

По умолчанию Kubernetes не решает задачу управления пользователями как таковую — он лишь проверяет, кто пришёл, и дальше перекладывает всё на RBAC. Исторически это было вполне осознанным решением: в ранних версиях Kubernetes (ещё времён v1.2–v1.5) основной фокус делался на сервисы и автоматизацию, а не на людей с kubeconfig’ами. Предполагалось, что кластером управляют либо сами ноды, либо CI/CD, либо небольшая команда администраторов.
Поэтому из коробки предлагались максимально простые механизмы аутентификации: клиентские сертификаты, static token и позже — bootstrap tokens. Для первых лет экосистемы этого хватало, но по мере того как Kubernetes начал использоваться как shared-платформа для десятков и сотен инженеров, эти подходы начали трещать по швам.
Если в качестве аутентификации используются клиентские сертификаты или статические токены, кластер быстро превращается в коллекцию артефактов: кто-то ушёл из команды, но его доступ всё ещё живёт в старом kubeconfig, кто-то получил доступ на время, а время, как это часто бывает, затянулось. Централизованного контроля, аудита и нормального жизненного цикла доступов в такой схеме нет — и это уже не особенность Kubernetes, а полноценный операционный риск.
Именно на этом этапе, примерно в районе Kubernetes v1.6–v1.8, в kube-apiserver появляется поддержка аутентификации через OpenID Connect. Не как очередная фича, а как попытка честно признать: управление пользователями — это не задача кластера. Kubernetes хорош в авторизации, но аутентификацию лучше отдать тем, кто десятилетиями занимается IAM.
OIDC решает эту проблему, вынося аутентификацию за пределы Kubernetes. Кластер перестаёт быть хранилищем и начинает доверять внешнему Identity Provider’у — будь то Keycloak, Dex, Azure AD, Okta или любой другой совместимый провайдер, который уже умеет в SSO, MFA, группы и политику безопасности. Kubernetes в этой модели делает ровно то, что у него получается лучше всего: проверяет токен, извлекает username и groups и сопоставляет их с RBAC-правилами.
В результате доступы становятся управляемыми, отзыв прав — мгновенным, а интеграция с корпоративной IAM перестаёт быть экзотикой и превращается в стандартную часть инфраструктуры. А kube-apiserver, к счастью для всех, больше не притворяется системой управления пользователями.
Кратко про OIDC и место Keycloak во всей схеме

OpenID Connect — это тонкий слой поверх OAuth 2.0, который отвечает на простой, но важный вопрос: кто именно сейчас делает запрос. В контексте Kubernetes OIDC используется исключительно для аутентификации пользователей, а не для выдачи прав доступа. Кластеру неинтересно, как пользователь логинился — через пароль, LDAP или MFA, — ему важно получить корректно подписанный JWT-токен с понятным issuer, subject и набором claim’ов.
Аутентификация в Kubernetes на этом и заканчивается. Дальше система вообще не пытается ответить на вопрос: что этому пользователю можно — это задача авторизации. И здесь в игру вступает RBAC (Role-Based Access Control): механизм, который описывает какие действия (verbs) над какими ресурсами (resources) в каких пространствах имён разрешены конкретному пользователю или группе. RBAC не знает, откуда взялся пользователь, как он логинился и существует ли он вообще за пределами кластера — ему достаточно имени и списка групп.
В этой схеме Kubernetes API Server выступает в роли доверяющей стороны: он не хранит пользователей, не знает про пароли и не ходит ни в какие внешние системы при каждом запросе. Вместо этого он проверяет подпись токена, сверяет iss с ожидаемым issuer’ом, убеждается, что токен предназначен именно для него (client_id), и извлекает из него имя пользователя и группы. Если токен валиден — дальше в игру вступает RBAC, который решает, можно ли этому пользователю, например, делать kubectl get pods или kubectl delete namespace.
Keycloak в этой модели занимает место Identity Provider’а. Он отвечает за аутентификацию пользователя, интеграцию с LDAP/AD, управление группами и формирование OIDC-токена в нужном формате. Kubernetes доверяет Keycloak как источнику истины о пользователях, а вся логика кто ты и в каких группах состоишь остаётся за пределами кластера. В итоге получается чёткое разделение: Keycloak управляет идентичностями, Kubernetes — доступами, и каждый занимается своим делом, не пытаясь заменить IAM или систему авторизации.
Почему именно Keycloak
Keycloak часто всплывает в связке с Kubernetes и OIDC не случайно. Это зрелый open-source Identity Provider, который из коробки поддерживает OIDC, умеет работать с LDAP и Active Directory, позволяет управлять пользователями, группами и ролями, а главное — даёт полный контроль над тем, какие claim’ы и в каком виде попадают в токен. Для Kubernetes это критично: если группы не доехали до JWT, никакой RBAC вас уже не спасёт. Плюс ко всему Keycloak можно развернуть on-premise, что до сих пор важно для многих инфраструктур.
При этом важно понимать: Keycloak — необязательное условие для OIDC в Kubernetes. Кластеру, по большому счёту, всё равно, кто выпустил токен, если соблюдены стандартные требования OIDC. В роли Identity Provider’а могут выступать Azure AD, Okta, Dex, Auth0, Google Workspace и другие решения. Разница будет в деталях: где-то проще интеграция с облачной экосистемой, где-то меньше гибкости в настройке claim’ов, а где-то придётся подстраиваться под уже существующую IAM-политику компании.
Keycloak здесь — скорее удобный и универсальный пример, чем единственно правильный выбор. Он хорошо подходит для демонстрации принципов работы OIDC, потому что позволяет увидеть всю цепочку целиком: от пользователя и групп до конкретного RoleBinding в Kubernetes. Освоив эту схему на Keycloak, смена провайдера обычно сводится к корректировке claim’ов и параметров API Server, а не к переписыванию всей модели доступа с нуля.
Подготовка Keycloak
Я использую keycloak 26.5.2 версию и буду показывать примеры на ней. Предполагаем, что он уже развернут и доступен по HTTP/S — Kubernetes к HTTP-issuer’ам. Первым делом создаём отдельный realm под Kubernetes (мешать его с другими сервисами можно, но потом сложнее разбираться)

Дальше — OIDC client. Тип клиента — OpenID Connect, Access Type обычно confidential (хотя для kubectl это не всегда принципиально).
Client ID — он же будет использоваться в API Server

Valid Redirect URIs — здесь необходимо точно задать адреса всех мастеров откуда будут приходить запросы к Keycloak

Ключевой момент — группы. Kubernetes не знает ничего про роли Keycloak, ему нужны группы в JWT. Поэтому:
Создаём группы в Keycloak (k8s-admins, k8s-readonly и т.д.)
Добавляем пользователей в эти группы
Настраиваем Group Membership mapper, чтобы группы попадали в токен, например в claim groups



В итоге access token должен содержать что-то вроде:
{ "iss": "http://127.0.0.1:8080/realms/kubernetes", "sub": "e3a1b0...", "preferred_username": "test", "email": "test@gmail.com", "groups": ["k8s-admins", "k8s-readonly"] }
Если на этом этапе группы не попали в токен — дальше можно даже не продолжать, RBAC просто не сработает. Каждый scope у клиента определяет, какие именно claims Keycloak положит в токен. В контексте Kubernetes это обычно groups, preferred_username, email, sub и иногда кастомные claims. Kubernetes смотрит только на содержимое токена и ему совершенно безразлично, почему этих полей там нет — если нужных claims нет, значит, пользователя как бы и не существует.
Настройка Kubernetes API Server
Теперь самое ответственное место — Kubernetes API Server. Именно здесь кластер учится доверять токенам, выпущенным Keycloak, и именно здесь чаще всего что-то идёт не так. Для self-hosted Kubernetes необходимые параметры передаются через флаги API Server. В минимальном варианте они выглядят так:
spec: containers: - command: - --oidc-issuer-url=http://127.0.0.1:8080/realms/kubernetes - --oidc-client-id=kubernetes - --oidc-username-claim=preferred_username - --oidc-groups-claim=groups
Часть параметров обязательная (issuer-url, client-id), часть — опциональная, но на практике почти всегда используется, если вы не хотите видеть в Kubernetes пользователей вида f8b1c3a4-...
Для кластеров, развернутых через kubeadm, изменения вносятся прямо на master-ноде в манифест:
/etc/kubernetes/manifests/kube-apiserver.yaml
После правки файл автоматически перечитается kubelet’ом, и API Server перезапустится. Да, это тот самый момент, когда одна ошибка — и kubectl больше никуда не ходит
HTTPS и собственные сертификаты
Если используется HTTPS в Keycloak и самоподписанный сертификат, то необходимо добавить флаг:
--oidc-ca-file=/etc/kubernetes/pki/keycloak-ca.crt
И, разумеется, добавить соответствующий volume и volumeMount в манифест kube-apiserver. Kubernetes сам этот файл не придумает — если забыть примонтировать CA, API Server просто откажется стартовать.
volumes: - hostPath: path: /etc/kubernetes/ssl type: DirectoryOrCreate name: keycloak-cert volumeMounts: - mountPath: /etc/kubernetes/pki name: keycloak-cert readOnly: true
На что стоит обратить внимание
Здесь несколько критически важных моментов, которые лучше проверить заранее:
oidc-issuer-urlдолжен в точности совпадать со значением iss в токене. Совпадать должно всё: схема, домен, путь и даже слэши в конце.При старте API Server обращается к
/.well-known/openid-configuration. Если Keycloak недоступен, сертификат невалиден или URL указан неверно — API Server не поднимется вовсе. Это не деградация, а fail fast, так что будьте готовы к весёлым рестартам.username-claimиgroups-claimдолжны реально существовать в JWT. Проверяйтеaccess tokenруками, прежде чем обвинять RBAC или kube-apiserver. Ниже можно узнать, как это сделать правильно:
curl -k -d "grant_type=password" -d "scope=openid" -d "client_id=" -d "client_secret=" -d "username=" -d "password=" http://127.0.0.1:8080/realms/kubernetes/protocol/openid-connect/token | jq .
Что происходит дальше
После включения OIDC Kubernetes перестаёт принимать пользователей в принципе и начинает доверять только тем, кто пришёл с валидным OIDC-токеном от указанного issuer’а. Кто этот пользователь, в какие группы он входит и что ему разрешено делать — Kubernetes не решает. Этим занимается RBAC, но это уже следующий этап, где можно сломать ещё больше и гораздо изящнее.
Генерация kubeconfig с OIDC
После настройки API Server Kubernetes уже умеет принимать OIDC-токены, но kubectl сам по себе в Identity Provider ходить не умеет. Ему нужен помощник, который возьмёт на себя процесс логина, откроет браузер и положит токен туда, где kubectl сможет его использовать. Чаще всего для этого используют kubelogin или oidc-login — принцип работы у них одинаковый.
Пример настройки пользователя в kubeconfig с kubelogin выглядит так:
apiVersion: v1 clusters: - cluster: certificate-authority-data: cluster_ca_certificate server: server_path name: cluster_name contexts: - context: cluster: cluster_name user: oidc name: oidc-context current-context: oidc-context kind: Config preferences: {} users: - name: oidc user: exec: apiVersion: client.authentication.k8s.io/v1beta1 args: - oidc-login - get-token - --oidc-issuer-url=<issuer_url> - --oidc-client-id=<client_id> - --oidc-client-secret=<client_secret> - --certificate-authority=<oidc_ca_certificate> command: kubectl
При выполнении команды вроде kubectl get pods происходит следующее:
kubectl вызывает kubelogin
Пользователь проходит аутентификацию в Keycloak (через браузер)
Keycloak выдаёт JWT-токен
Токен подставляется в запрос к API Server
Kubernetes проверяет подпись и issuer — и только потом переходит к RBAC
Важно, что kubeconfig в этой схеме не содержит никаких секретов долгого хранения. Токен живёт ограниченное время, а отзыв доступа в Keycloak начинает действовать сразу, без перевыпуска конфигов и рассылки новых файлов по чатам.
Интеграция с RBAC
Когда аутентификация работает, Kubernetes знает о пользователе две вещи: его имя и группы. Всё остальное — задача RBAC. Самый здравый подход здесь — привязывать права к группам, а не к конкретным пользователям. Но можно конечно и привязаться к полю kind: User
Пример ClusterRoleBinding, который даёт полный доступ группе k8s-admins из Keycloak:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: k8s-admins subjects: - kind: Group name: k8s-admins apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
Для более ограниченных сценариев используются Role и RoleBinding на уровне namespace — принцип тот же самый, меняется только область действия. Пользователь добавляется в группу в Keycloak, получает токен с нужным groups claim, и Kubernetes автоматически применяет соответствующие правила доступа. Также существует стандартный набор ролей (view, admin, edit, cluster-admin) которые можно использовать в любом кластере — они покрывают большинство потребностей, но если вдруг чего-то не хватает, например: через роль view можно было смотреть CRD, но не изменять, то их всегда можно расширить.
Типичные проблемы и как их диагностировать
Даже при аккуратной настройке OIDC интеграция редко заводится с первого раза. Хорошая новость — почти все проблемы типовые и неплохо диагностируются, если понимать, куда смотреть.
1) Unauthorized или forbidden при любом запросе. Чаще всего означает, что аутентификация прошла, а авторизация — нет.
Проверяем:
пришли ли группы в токене
совпадает ли имя группы с тем, что указано в RoleBinding / ClusterRoleBinding
П��лезная команда для проверки прав доступа:
kubectl auth can-i get pods -n kube-system
Она сразу показывает, вопрос в RBAC или в чём-то ещё. Ответ: yes или no
2) Invalid bearer token
Здесь Kubernetes даже не дошёл до RBAC. Возможные причины:
oidc-issuer-url не совпадает с iss в токене
API Server не может достучаться до /.well-known/openid-configuration
проблемы с TLS-сертификатом Keycloak если включен HTTPS
В таких случаях стоит:
декодировать JWT и проверить iss, aud, exp
посмотреть логи API Server — они обычно достаточно разговорчивы
3) Группы не приходят в токене
Обычно проблема в mapper’ах Keycloak:
неправильный claim name
выключена опция добавления в access token
пользователь не состоит ни в одной группе
Пока группы не видны в JWT, Kubernetes будет считать пользователя одиночкой без прав
4) Всё работает… иногда
Если доступ то появляется, то исчезает — стоит проверить:
время жизни токена, можно через jwt.io
синхронизацию времени (NTP) между API Server и Keycloak
OIDC плохо относится к путешествиям во времени
Security-заметки

Я не безопасник и не претендую на знание всех тонкостей IAM и threat modeling’а, но несколько практических вещей, которые стоит учитывать при использовании OIDC в Kubernetes, всё же можно выделить. OIDC действительно сильно упрощает жизнь, но при неосторожной настройке может упростить её и злоумышленнику, поэтому пару моментов полезно держать в голове.
Время жизни токенов
Не стоит делать access token вечным, потому что так удобнее. Короткий TTL заметно снижает риски при утечке kubeconfig: даже если файл утёк, окно для злоупотреблений будет ограниченным. Плюс это позволяет быстрее отзывать доступы. Для пользователя kubectl это почти незаметно — обновление токена происходит автоматически, а значит удобство не страдает, в отличие от безопасности.
Refresh token: нужен ли он вообще
Для интерактивного доступа через kubectl в большинстве случаев можно обойтись без длинноживущих refresh token’ов. Если нет необходимости поддерживать бесконечные сессии, лучше не плодить сущности с большим сроком жизни. Общее правило здесь простое: чем меньше долгоживущих секретов в системе — тем спокойнее спится дежурному инженеру ИБ.
MFA в Keycloak
Если уж у вас есть централизованная аутентификация, не включать MFA выглядит странно. Kubernetes от этого никак не страдает, kubectl не начинает работать медленнее, а уровень защиты аккаунтов растёт вполне ощутимо. Особенно это актуально для доступов с правами cluster-admin — один скомпрометированный аккаунт без MFA может стоить сильно дороже, чем пара дополнительных кликов при логине.
Доступность Keycloak
Здесь важно чётко понимать модель отказа. Если Keycloak недоступен, новые логины выполнить нельзя, но уже выданные токены продолжают работать до истечения своего TTL. Это ещё один аргумент в пользу разумного времени жизни токенов и нормального мониторинга самого IdP. Keycloak внезапно становится критической частью control plane, даже если формально Kubernetes от него не зависит.
В целом, OIDC — мощный инструмент, но относиться к нему стоит как к продакшен-компоненту, а не как к галочке для удобства. Немного дисциплины на этапе настройки обычно экономит много времени на разбор инцидентов потом.
Сервисные аккаунты, роли и согласование claims
После включения OIDC почти у всех возникает один и тот же вопрос:
А что теперь будет с ServiceAccount’ами? Их тоже надо заводить в Keycloak?
Нет. OIDC в Kubernetes касается только внешней аутентификации пользователей. ServiceAccount’ы — это внутренний механизм кластера. Их токены:
создаются самим Kubernetes
подписываются его ключами
проверяются тем же kube-apiserver
У них другой issuer (kubernetes/serviceaccount) и другой механизм валидации. OIDC-аутентификация к ним вообще не применяется.
То есть после включения OIDC архитектура выглядит так:
Тип субъекта | Кто выпускает токен | Кто проверяет |
|---|---|---|
Пользователь | Keycloak (OIDC) | kube-apiserver |
ServiceAccount | Kubernetes API Server | kube-apiserver |
Это два параллельных механизма. Они не конфликтуют и не зависят друг от друга. Если у вас внезапно перестали работать поды после включения OIDC — проблема точно не в ServiceAccount.
Kubernetes не знает про роли Keycloak
В предыдущем разделе мы создавали группы в Keycloak, потому что Kubernetes ожидает claim groups в токене. Но здесь есть важный архитектурный момент: Kubernetes не интересует, как именно вы моделируете доступ в Keycloak. Его интересует только содержимое JWT.
Если в kube-apiserver указано:
--oidc-username-claim=preferred_username --oidc-groups-claim=groups
Значит, он будет искать именно эти поля. И всё. Дальше начинается самое интересное — в Keycloak вы не обязаны использовать именно группы как сущность.
Можно:
использовать роли realm’а
использовать client roles
собирать composite roles
делать кастомные мапперы
преобразовывать роли в claim groups
Например, вы можете:
назначать пользователю роль
k8s-adminчерез mapper складывать её в claim
groupsи на стороне Kubernetes это будет выглядеть как обычная группа
Для Kubernetes нет разницы, откуда взялось значение "groups": ["k8s-admin"] — из группы, из роли или из кастомного маппера — он читает только JWT.
Итоги
В этой статье мы прошли весь путь интеграции Kubernetes с внешней системой аутентификации через OIDC на примере Keycloak: от понимания, зачем вообще выносить аутентификацию за пределы кластера, до практической настройки API Server, генерации kubeconfig и привязки групп к RBAC. В результате Kubernetes перестаёт быть местом, где как-то живут пользователи и разрабы, и начинает работать как часть общей IAM-инфраструктуры.
Такая схема решает сразу несколько эксплуатационных проблем. Доступы становятся централизованными и управляемыми, отзыв прав происходит мгновенно, без перевыпуска конфигов и ручной зачистки, а RBAC наконец начинает использоваться так, как задумывался — через группы, а не через бесконечные исключения. Плюс ко всему появляется нормальная точка для внедрения MFA, аудита и единого SSO, без изобретения собственных костылей вокруг kubectl.
Keycloak в этом процессе — удобный и наглядный пример, но не догма. Освоив OIDC-интеграцию один раз, вы сможете применять тот же подход с любым другим Identity Provider’ом, не меняя саму модель доступа. Kubernetes в этой схеме делает ровно то, что должен: доверяет проверенному источнику идентичностей и строго применяет правила авторизации.
Если коротко — OIDC превращает доступ к Kubernetes из набора исторических артефактов в предсказуемый и масштабируемый механизм. А это как раз тот редкий случай, когда инфраструктура становится проще не за счёт ограничений, а за счёт правильной архитектуры.
