Стадия жизненного цикла модуля: General Availability
Как защитить мое приложение?
Чтобы включить аутентификацию через Dex для приложения, выполните следующие шаги:
-
Создайте ресурс DexAuthenticator.
При создании DexAuthenticator в кластере создаётся экземпляр oauth2-proxy, подключённый к Dex. В указанном неймспейсе будут созданы объекты Deployment, Service, Ingress, Secret.
Пример ресурса DexAuthenticator:
apiVersion: deckhouse.io/v1 kind: DexAuthenticator metadata: # Префикс имени подов Dex authenticator. # Например, если префикс имени `app-name`, то поды Dex authenticator будут вида `app-name-dex-authenticator-7f698684c8-c5cjg`. name: app-name # Неймспейс, в котором будет развернут Dex authenticator. namespace: app-ns spec: # Домен вашего приложения. Запросы на него будут перенаправляться для прохождения аутентификации в Dex. applicationDomain: "app-name.kube.my-domain.com" # Отправлять ли заголовок `Authorization: Bearer` приложению. Полезно в связке с auth_request в NGINX. # При значении sendAuthorizationHeader: true добавьте заголовок Authorization в аннотацию nginx.ingress.kubernetes.io/auth-response-headers Ingress приложения или в аннотацию alb.network.deckhouse.io/auth-response-headers ресурса HTTPRoute. sendAuthorizationHeader: false # Имя секрета с SSL-сертификатом. applicationIngressCertificateSecretName: "ingress-tls" # Название Ingress-класса, которое будет использоваться в создаваемом для Dex authenticator Ingress-ресурсе. applicationIngressClassName: "nginx" # Время, на протяжении которого пользовательская сессия будет считаться активной. keepUsersLoggedInFor: "720h" # Список групп, пользователям которых разрешено проходить аутентификацию. allowedGroups: - everyone - admins # Список адресов и сетей, с которых разрешено проходить аутентификацию. whitelistSourceRanges: - 1.1.1.1/32 - 192.168.0.0/24 -
Подключите приложение к Dex.
Для этого добавьте в ресурс, через который публикуется приложение аннотации. Набор аннотаций зависит от того, каким способом публикуется приложение. Выберите подходящий вариант:
- Через Ingress-ресурс
- Через ALBInstance или ClusterALBInstance
Добавьте в Ingress-ресурс приложения следующие аннотации:
nginx.ingress.kubernetes.io/auth-signin: https://$host/dex-authenticator/sign_innginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Emailnginx.ingress.kubernetes.io/auth-url: https://<SERVICE_NAME>.<NS>.svc.{{ C_DOMAIN }}/dex-authenticator/auth, где:SERVICE_NAME— имя сервиса (Service) аутентификатора. Как правило, оно соответствует формату<NAME>-dex-authenticator(<NAME>— этоmetadata.nameресурса DexAuthenticator);NS— значение параметраmetadata.namespaceресурса DexAuthenticator;C_DOMAIN— домен кластера (параметр clusterDomain ресурса ClusterConfiguration).
Если имя DexAuthenticator (<NAME>) слишком длинное, имя сервиса (Service) может быть сокращено. Чтобы найти корректное имя сервиса, воспользуйтесь следующей командой (укажите имя неймспейса и аутентификатора):
d8 k get service -n <NS> -l "deckhouse.io/dex-authenticator-for=<NAME>" -o jsonpath='{.items[0].metadata.name}'
Пример аннотаций Ingress-ресурса приложения для подключения к Dex:
annotations:
nginx.ingress.kubernetes.io/auth-signin: https://$host/dex-authenticator/sign_in
nginx.ingress.kubernetes.io/auth-url: https://app-name-dex-authenticator.app-ns.svc.cluster.local/dex-authenticator/auth
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email
Для приложения, в котором указан только applicationIngressClassName, HTTPRoute не создаётся. Если публикация через Ingress отключена параметром global.modules.ingress.enabled, не создаётся и Ingress. Само приложение остаётся доступным через тот маршрут, которым вы его публикуете, а вот его эндпоинты /dex-authenticator — нет: редирект на вход попадает в само приложение, и войти невозможно. На остальные приложения это не влияет.
Если приложение публикуется через ресурс ALBInstance или ClusterALBInstance (подробнее — в документации модуля alb), добавьте в HTTPRoute-ресурс приложения следующие аннотации:
alb.network.deckhouse.io/auth-signin: https://<домен-приложения>/dex-authenticator/sign_in— в отличие от nginx, контроллерalbне поддерживает переменную$host, поэтому домен приложения нужно указать явно;alb.network.deckhouse.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email;alb.network.deckhouse.io/auth-url: https://<SERVICE_NAME>.<NS>.svc.<C_DOMAIN>/dex-authenticator/auth, гдеSERVICE_NAME,NSиC_DOMAINопределяются так же, как для Ingress-ресурса.
Пример аннотаций ресурса HTTPRoute для подключения приложения к Dex:
annotations:
alb.network.deckhouse.io/auth-signin: https://app-name.kube.my-domain.com/dex-authenticator/sign_in
alb.network.deckhouse.io/auth-url: https://app-name-dex-authenticator.app-ns.svc.cluster.local/dex-authenticator/auth
alb.network.deckhouse.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email
Также укажите в ресурсе DexAuthenticator тот же ListenerSet, через который опубликован домен приложения:
spec:
gatewayAPI:
applicationHTTPRouteListenerSetName: my-listenerset
Создаваемый для DexAuthenticator HTTPRoute подключается к этому же ListenerSet, поэтому ListenerSet должен принимать маршруты из неймспейса, в котором находится DexAuthenticator.
Для приложения, в котором указан только gatewayAPI, Ingress не создаётся. Если Gateway API отключён параметром global.modules.gatewayAPI.enabled либо в кластере нет API HTTPRoute, не создаётся и HTTPRoute — с тем же результатом: приложение остаётся доступным, его эндпоинты /dex-authenticator — нет, войти невозможно. На остальные приложения это не влияет.
При включении sendAuthorizationHeader: true в Ingress (или в HTTPRoute, если используется модуль alb) укажите все необходимые заголовки в соответствующей аннотации, поскольку заголовок Authorization по умолчанию не передаётся:
Подробнее о том, что передаётся в заголовке Authorization и как указать его в аннотации, читайте в разделе «Как передать приложению логин и группы пользователя».
Ingress приложения должен иметь настроенный TLS. DexAuthenticator не поддерживает Ingress-ресурсы, работающие только по HTTP.
Настройка ограничений на основе CIDR
В DexAuthenticator нет встроенной системы управления разрешением аутентификации на основе IP-адреса пользователя. Вместо этого вы можете воспользоваться аннотациями для Ingress-ресурсов:
-
Если нужно ограничить доступ по IP и оставить прохождение аутентификации в Dex, добавьте аннотацию с указанием разрешенных CIDR через запятую:
nginx.ingress.kubernetes.io/whitelist-source-range: 192.168.0.0/32,1.1.1.1 -
Чтобы разрешить доступ без аутентификации в Dex для пользователей из указанных сетей, а для остальных оставить обязательную аутентификацию, добавьте аннотацию:
nginx.ingress.kubernetes.io/satisfy: "any"
Как передать приложению логин и группы пользователя?
По умолчанию DexAuthenticator передаёт приложению только два заголовка: X-Auth-Request-User (значение основано на непрозрачном claim’е sub) и X-Auth-Request-Email. Заголовок с группами пользователя не передаётся. Он неограниченно растёт при большом количестве групп, поэтому в DP отключён, и включить его нельзя.
Чтобы приложение получило полную информацию о пользователе, включая группы, включите параметр sendAuthorizationHeader:
apiVersion: deckhouse.io/v1
kind: DexAuthenticator
metadata:
name: app-name
namespace: app-ns
spec:
applicationDomain: "app-name.kube.my-domain.com"
applicationIngressClassName: "nginx"
applicationIngressCertificateSecretName: "ingress-tls"
sendAuthorizationHeader: true
В этом случае приложению передаётся заголовок Authorization: Bearer <id_token>, где <id_token> — подписанный Dex JWT (подробнее о содержимом токена и его обработке в приложении — в разделе «Содержимое токена JWT и особенности его обработки»).
Независимо от того, каким способом публикуется приложение, этот заголовок не передается в приложение автоматически. Его нужно явно указать в списке передаваемых с помощью аннотации ресурса, через который публикуется приложение. Выберите подходящий вариант, в зависимости от того, каким способом публикуется приложение:
- Через Ingress-ресурс
- Через ALBInstance или ClusterALBInstance
Если приложение публикуется через Ingress-ресурс (подробнее — в документации модуля ingress-nginx), при включении sendAuthorizationHeader: true необходимо:
- указать заголовки, которые нужно передавать в приложение, в аннотации
nginx.ingress.kubernetes.io/auth-response-headers; - увеличить размер буфера с помощью аннотации
nginx.ingress.kubernetes.io/proxy-buffer-size, поскольку JWT с большим числом групп не помещается в буфер по умолчанию.
Пример указания заголовков и размера буфера с помощью соответствующих аннотаций:
annotations:
nginx.ingress.kubernetes.io/auth-signin: https://$host/dex-authenticator/sign_in
nginx.ingress.kubernetes.io/auth-url: https://app-name-dex-authenticator.app-ns.svc.cluster.local/dex-authenticator/auth
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email,Authorization
nginx.ingress.kubernetes.io/proxy-buffer-size: 32k
Если заголовок Authorization не указан в auth-response-headers, приложение его не получит. Если не увеличить proxy-buffer-size, запросы будут завершаться ошибкой 500, а в логах контроллера Ingress появится сообщение upstream sent too big header while reading response header from upstream.
Если приложение публикуется через ресурс ALBInstance или ClusterALBInstance (подробнее — в документации модуля alb), при включении sendAuthorizationHeader: true укажите заголовки, которые нужно передавать в приложение, в аннотации alb.network.deckhouse.io/auth-response-headers ресурса HTTPRoute:
annotations:
alb.network.deckhouse.io/auth-signin: https://app-name.kube.my-domain.com/dex-authenticator/sign_in
alb.network.deckhouse.io/auth-url: https://app-name-dex-authenticator.app-ns.svc.cluster.local/dex-authenticator/auth
alb.network.deckhouse.io/auth-response-headers: Authorization
В аннотации alb.network.deckhouse.io/auth-response-headers, достаточно указать только Authorization, так как она уже передаваемый по умолчанию базовый набор заголовков.
Также в ресурсе DexAuthenticator укажите тот же ListenerSet, через который опубликован домен приложения:
spec:
gatewayAPI:
applicationHTTPRouteListenerSetName: my-listenerset
Содержимое токена JWT и особенности его обработки
Пример полезной нагрузки JWT для статического пользователя (ресурсы User и Group):
{
"iss": "https://dex.kube.my-domain.com/",
"sub": "Cg1qb2huLmRvZUBleGFtcGxlEgVsb2NhbA",
"aud": "app-name-app-ns-dex-authenticator",
"exp": 1757000600,
"iat": 1757000000,
"email": "john.doe@example.com",
"email_verified": true,
"name": "john-doe",
"preferred_username": "",
"groups": ["everyone", "developers"]
}
При использовании данных из токена в приложении обращайте внимание на следующее:
- Для идентификации пользователя используйте поле
email. Полеsubнепрозрачно (не является предсказуемым и не может быть использовано как осмысленный идентификатор пользователя вне контекста конкретной системы), аpreferred_usernameдля статических пользователей пуст (внешние провайдеры аутентификации могут его заполнять). - Поле
nameсодержит имя объекта (из поляmetadata.nameобъекта User), а не отображаемое имя пользователя. - Поле
audсодержит идентификатор клиента аутентификатора (<name>-<namespace>-dex-authenticator), а не идентификатор OIDC-клиента вашего приложения. Приложение, проверяющееaudпо собственномуclient_id, отклонит такой токен. - Подпись проверяйте по JWKS
https://dex.<modules.publicDomainTemplate>/keys. - Время жизни токена определяется параметром
idTokenTTL(по умолчанию 10 минут). DexAuthenticator обновляет токен самостоятельно, поэтому приложение всегда получает актуальный токен.
Если приложение поддерживает OIDC самостоятельно, вместо DexAuthenticator используйте ресурс DexClient: приложение само запросит необходимый объём полномочий и получит refresh_token в дополнение к id_token.
Как работает аутентификация с помощью DexAuthenticator
DexAuthenticator работает только по HTTPS. Ingress-ресурсы, настроенные только на HTTP, не поддерживаются.
Аутентификационные cookie устанавливаются с атрибутом Secure, что означает их передачу только через зашифрованные HTTPS-соединения.
Убедитесь, что для Ingress вашего приложения настроен TLS, прежде чем интегрировать его с DexAuthenticator.
-
Dex в большинстве случаев перенаправляет пользователя на страницу входа провайдера и ожидает, что пользователь будет перенаправлен на его
/callbackURL. Однако такие провайдеры, как LDAP или Atlassian Crowd, не поддерживают этот вариант. Вместо этого пользователь должен ввести логин и пароль в форму входа в Dex, и Dex сам проверит учётные данные, выполнив запрос к API провайдера. -
DexAuthenticator устанавливает cookie с полным токеном обновления (вместо выдачи тикета, как для ID-токена), потому что Redis не сохраняет данные на диск. Если по тикету в Redis не найден ID-токен, пользователь сможет запросить новый ID-токен, предоставив токен обновления из cookie.
-
DexAuthenticator выставляет HTTP-заголовок
Authorization, равный значению ID-токена из Redis. Это необязательно для сервисов вродеupmeter, так как права доступа кupmeterменее детальные. Для Kubernetes Dashboard это критичная функциональность: Dashboard передаёт ID-токен дальше для доступа к API Kubernetes.
Как сгенерировать kubeconfig для доступа к Kubernetes API?
Для генерации kubeconfig воспользуйтесь разделом «Как сгенерировать kubeconfig для доступа к Kubernetes API?» документации модуля control-plane-manager.
Настройка kube-apiserver
С помощью функций модуля control-plane-manager DP автоматически настраивает kube-apiserver, выставляя следующие флаги так, чтобы в кластере работала аутентификация через OIDC.
Как работает подключение к Kubernetes API с помощью сгенерированного kubeconfig
-
До начала работы
kube-apiserverнеобходимо запросить конфигурационный эндпоинт OIDC провайдера (в нашем случае — Dex), чтобы получить issuer и настройки JWKS-эндпоинта. -
Kubeconfig generator сохраняет ID-токен и Refresh-токен в файл
kubeconfig. -
После получения запроса с ID-токеном
kube-apiserverпроверяет, что токен подписан провайдером, настроенным на первом шаге, с помощью ключей, полученных с JWKS-эндпоинта. Затем сравнивает значения claimissиaudиз токена со значениями из конфигурации.
Как сменить секрет OAuth2-клиента kubernetes?
Секрет привилегированного OAuth2-клиента kubernetes хранится в Secret kubernetes-dex-client-app-secret в неймспейсе d8-user-authn. То же значение используют OAuth2-клиенты kubeconfig-generator, kubeconfig-publish-api и kubeconfig-<slug>, а также компонент basic-auth-proxy, которому секрет передаётся с помощью параметра --ldap-client-secret.
Удаление Secret не приводит к смене секрета: пока значение сохраняется во внутренних параметрах модуля, хук восстановит Secret с прежним значением.
Чтобы сменить секрет, выполните следующие действия:
-
Если неймспейс
d8-user-authnуправляется с помощью GitOps-инструмента, исключите Secretkubernetes-dex-client-app-secretиз синхронизации. Иначе GitOps-инструмент восстановит прежнее значение. -
Очистите поле
secret:d8 k -n d8-user-authn patch secret kubernetes-dex-client-app-secret --type merge -p '{"data":{"secret":""}}' -
Перезапустите DP, чтобы хук зарегистрировал пустое поле и сгенерировал новый секрет:
d8 k -n d8-system rollout restart deployment/deckhouse -
Убедитесь, что значение секрета изменилось:
d8 k -n d8-user-authn get secret kubernetes-dex-client-app-secret -o jsonpath='{.data.secret}'Если значение не изменилось, повторите шаги 2 и 3. Модуль мог восстановить прежнее значение до перезапуска DP.
После смены секрета конфигурация использующих его компонентов в кластере обновится автоматически, а их поды будут перезапущены.
Ранее загруженные файлы kubeconfig содержат прежний клиентский секрет и больше не смогут обновлять токены. Скачайте такие файлы заново. Уже выданные ID-токены продолжат действовать до истечения их срока действия, определяемого параметром settings.idTokenTTL (по умолчанию — 10 минут).
Как включить SSO по Kerberos (SPNEGO) для LDAP?
Если на стороне клиента настроено доменное SSO (браузер доверяет домену Dex), Dex может принимать Kerberos‑билеты по заголовку Authorization: Negotiate и выполнять аутентификацию без отображения формы ввода логина/пароля.
Включение SSO по Kerberos (SPNEGO) для LDAP:
- В инфраструктуре клиента должен быть задан SPN
HTTP/<fqdn-dex>для сервисного аккаунта и сгенерированkeytab. - В кластере создайте секрет в неймспейсе
d8-user-authnс ключомkrb5.keytab. - В ресурсе DexProvider (тип LDAP) включите блок
spec.ldap.kerberosи настройте в нём параметры:enabled: true;keytabSecretName: <имя секрета>;- опционально:
expectedRealm,usernameFromPrincipal,fallbackToPassword.
Dex автоматически смонтирует keytab и начнёт принимать SPNEGO. krb5.conf на сервере не обязателен — билеты проверяются по keytab.
Как настроить базовую аутентификацию для доступа к Kubernetes API через LDAP?
Используйте настройки модуля control-plane-manager:
- Включите параметр
apiserver.publishAPIв конфигурации модуляcontrol-plane-manager. - Создайте ресурс DexProvider типа
LDAPи установите параметрenableBasicAuth: true. - Настройте RBAC для групп, получаемых из LDAP.
- Передайте пользователям kubeconfig с настроенными параметрами базовой аутентификации (логин и пароль LDAP).
В кластере может быть только один провайдер аутентификации со включенным параметром enableBasicAuth.
Подробный пример описан в разделе Примеры конфигурации.
Как Dex защищен от подбора логина и пароля?
Каждому пользователю разрешено не более 20 попыток входа. После исчерпания лимита одна дополнительная попытка добавляется каждые 6 секунд.
UserOperation в статусе Failed — что делать?
Проверьте поле status.message ресурса UserOperation, чтобы узнать описание ошибки:
d8 k get useroperation <имя> -o jsonpath='{.status.message}'
Устраните причину (например, неверный хеш пароля или несуществующий пользователь), затем создайте новый UserOperation. UserOperation неизменяем — его спецификацию нельзя изменить после создания.
Как разблокировать пользователя?
Используйте команду:
d8 iam user unlock <имя>
Либо создайте новый ресурс UserOperation с type: Unlock. Учтите, что операции ResetPassword, Reset2FA и Lock завершают все активные сессии пользователя.
Пользователь заблокирован автоматически — почему?
Количество неудачных попыток входа превысило passwordPolicy.lockout.maxAttempts. Пользователь блокируется на время, указанное в passwordPolicy.lockout.lockDuration, после чего разблокируется автоматически. Администратор может также разблокировать пользователя вручную командой d8 iam user unlock <имя> или создав UserOperation с type: Unlock.
Можно ли отменить операцию UserOperation?
Нет. UserOperation — одноразовый неизменяемый объект. Чтобы отменить эффект операции, нужно создать обратную — например, создать UserOperation с type: Unlock после операции Lock.