Table of Contents

Проблема

Мне нужно сконфигурировать keycloak (версии 7.0.1), развернутый с помощью helm в кластере kubernetes, чтобы он забирал пользователей из Active Directory по протоколу LDAPS.
Проверка подключения к контроллеру домена (Test Connection) по протоколу ldaps (порт 636) проходит нормально, но тест аутентификации (Test Authentication) не проходит с ошибкой:

14:19:35,523 ERROR [org.keycloak.services] (default task-1) KC-SERVICES0055: Error when authenticating to LDAP: simple bind failed: dc.domain.local:636: javax.naming.CommunicationException: simple bind failed: dc.domain.local:636 [Root exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]

Проблема в том, что сертификат контроллера домена выдан корпоративным certification authority.
Мануалы, которые я нашел в интернете предлагают пересобрать docker image, поместив туда корневой сертификат. Но мне такой вариант не нравится. Я бы хотел, чтобы при старте podkeycloak импортировал корневой сертификат в keystore (jks) и использовател его.

Хорошее решение

Создаем ConfigMap с корпоративными корневыми сертификатами. Они должны быть в формате pem и оформлены стандартными разделителями - —–BEGIN CERTIFICATE—– и —–END CERTIFICATE—–.

curl -k https://nexus.rdleas.ru/repository/files/Root_CA_Certs/RDleas-SRV-DC02-CA.cer -o ./ca.cer 
kubectl -n keycloak create configmap ca-bundle --from-file=./ca.cer

А keycloak_values.yaml приводим к такому виду:

keycloak:
  replicas: 1
  image:
    tag: 8.0.2
  existingSecret: "keycloak-default-admin-password"

  extraVolumes: |
    - name: ca-bundle
      configMap:
        name: ca-bundle
  extraVolumeMounts: |
    - name: ca-bundle
      mountPath: /custom_certs/

  extraEnv: |
    - name: X509_CA_BUNDLE
      value: "/custom_certs/ca.cer"
    - name:  PROXY_ADDRESS_FORWARDING
      value: "true"
    - name: JAVA_OPTS
      value: |
        -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dkeycloak.profile.feature.upload_scripts=enabled

  ingress:
    enabled: true
    path: /
    annotations: 
      kubernetes.io/ingress.class: nginx
    hosts:
      - sso.domain.local
    tls:
      - hosts:
        - sso.domain.local
        secretName: sso-domain-local

  persistence:
    deployPostgres: false
    dbVendor: "postgres"
    existingSecret: "keycloak-db-password"
    dbName: keycloak
    dbHost: 10.10.10.10
    dbPort: 5432

test:
  enabled: false

Некоторые пояснения.
Созданный ConfigMap с корпоративными корневыми сертификатами монтируется в директорию /custom_certs/.
Переменная X509_CA_BUNDLE указывает на путь к файлу с сертификатами и при старте контейнера используется скриптом /opt/jboss/tools/x509.sh, который импортирует сертификаты из файла в keystore.

Нехорошее решение

Для этого редактируем StatefulSet keycloak:

В раздел env добавляем такое:

spec:
  template:
    spec:
      containers:
        env:
        - name: JAVA_OPTS
          value: -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
            -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman
            -Djava.awt.headless=true  -Djavax.net.ssl.trustStore=/opt/jboss/keycloak/standalone/configuration/keystores/truststore.jks  
            -Djavax.net.ssl.trustStorePassword=changeit 
            --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED

А command приводим к такому виду:

spec:
  template:
    spec:
      containers:
      ...
        command:
        - /bin/bash
        - -c
        args:
        - mkdir -p /opt/jboss/keycloak/standalone/configuration/keystores/ 
          && curl -k https://http.server.local/domain_ca.cer -o /opt/jboss/keycloak/standalone/configuration/ca.cer 
          && keytool -import -noprompt -keystore /opt/jboss/keycloak/standalone/configuration/keystores/truststore.jks -file /opt/jboss/keycloak/standalone/configuration/ca.cer -storepass changeit -alias domain-local-CA 
          && /scripts/keycloak.sh

В результате - до старта сервера Keycloak корневой сертификат забирается с http-сервера и помещается в ssl.trustStore, параметры которого (путь и пароль) передаются в виде переменных среды Java.