KeycloakをOpenShiftのIdentity Providerとして利用する

※ この記事は [Red Hat Advent Calendar 2023](https://qiita.com/advent-calendar/2023/redhat) の19日目の記事です。

Red Hatのソリューションアーキテクトのおおみぞです。

Red Hat Build of Keycloak（以降、Keycloak）を Red Hat OpenShift Container Platform（以降、OpenShift）の Identity Provider として利用するための設定をご紹介します。今回は Keycloak も OpenShift にデプロイするので、
2つの OpenShift 環境を用意します。1つは Keycloak をデプロイする環境でクラウド上に作成、もう一つは Keycloak によって保護される環境でローカルに作成します。

全体の流れとしては、以下のようになります。
1. （クラウド） Postgresql をデプロイ
2. （クラウド） Keycloak Operator インストール
3. （クラウド） Keycloak インスタンス作成
4. （クラウド)　KeycloakにRealm/Client/User 作成
5. （ローカル） OpenShift のアイデンティティプロバイダー設定


# Keycloakのデプロイ

`keycloak` という名前の OpenShift のプロジェクトに、DB や Keycloak をデプロイしていきます。

## データベースの準備
まずは、Keycloakのバックエンドのデータベースを準備します。
様々な[DBサーバがサポート対象](https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/22.0/html/server_guide/db-#db-supported-databases)となっていますが、今回はPostgreSQLを利用します。

PostgresqlのStatefulsetとServiceをYAMLを使ってデプロイします。
以下の内容を `kc-pgsql.yaml` というファイルに保存し、`oc apply -f kc-pgsql.yaml` コマンドで PostgreSQL をデプロイします。
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql-db
spec:
  serviceName: postgresql-db-service
  selector:
    matchLabels:
      app: postgresql-db
  replicas: 1
  template:
    metadata:
      labels:
        app: postgresql-db
    spec:
      containers:
        - name: postgresql-db
          image: postgres:latest
          volumeMounts:
            - mountPath: /data
              name: cache-volume
          env:
            - name: POSTGRES_PASSWORD
              value: testpassword
            - name: PGDATA
              value: /data/pgdata
            - name: POSTGRES_DB
              value: keycloak
      volumes:
        - name: cache-volume
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-db
spec:
  selector:
    app: postgresql-db
  type: LoadBalancer
  ports:
  - port: 5432
    targetPort: 5432
```

KeycloakがDBにアクセスするときに利用する DB のユーザとパスワードを Secret `keycloak-db-secret` として登録します。

```shell
oc create secret generic keycloak-db-secret \
  --from-literal=username=postgres \
  --from-literal=password=testpassword
```

## Keycloak の TLS 証明書と鍵の作成

自己証明書を作成します。OpenShift の Identity Provider を登録する際に SAN(Subject Alternative Name)がない証明書を利用すると、証明書が不正となって Identity Provider の登録ができないので、SANに対応した証明書を作成します。

私は [Red Hat Build of Keycloak - Operator Guide](https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/22.0/html/operator_guide/basic-deployment-#basic-deployment-tls-certificate-and-key)に記載の通りに設定していき、いざ、OpenShift の設定をするときに「証明書が登録できない！？」となってあたふたしてしまいました。

証明書に関する細かな説明は割愛しますが、以下の手順で作成した証明書を利用しました。

ルートCAの秘密鍵作成 => rootCA1.key
ルートCAの証明書作成 => rootCA1.pem
``````
openssl genrsa -out rootCA1.key -des3 2048
openssl req -new -x509 -key rootCA1.key -sha256 -days 3650 -extensions v3_ca -out rootCA1.pem -subj "/C=JP/ST=Tokyo/O=rootCA1/CN=rooCA1"
``````

中間CAの秘密鍵作成 => inCA1.key
中間CAの証明書要求作成(CSR) => inCA1.csr
中間CAの証明書作成(PEM) => inCA1.pem
``````
openssl genrsa -out inCA1.key -des3 2048
openssl req -new -key inCA1.key -sha256 -outform PEM -keyform PEM -out inCA1.csr -subj "/C=JP/ST=Tokyo/O=inCA1/CN=inCA1"
openssl x509 -extfile openssl_sign_inca.cnf -req -in inCA1.csr -sha256 -CA rootCA1.pem -CAkey rootCA1.key -set_serial 01 -extensions v3_ca -days 3650 -out inCA1.pem
``````

秘密鍵と証明書要求の作成 => server.csr
サーバ証明書作成 => server.pem
``````
openssl req -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -sha256 -config openssl_sign_server.cnf -subj "/C=JP/ST=Tokyo/O=Keycloak/CN=test.keycloak.org"
openssl x509 -req -in server.csr -sha256 -CA inCA1.pem -CAkey inCA1.key -set_serial 01 -days 3650 -extfile san.txt -out server.pem
``````

最後に内容確認します。
```
openssl x509 -in server.pem -text -noout
```

証明書を Secret として登録します。
```
oc create secret tls example-tls-secret --cert server.pem --key server.key
```


## Keycloak Operator のインストール
Operator Hub で `rhbk` というキーワードで Keycloak Operator を検索しインストールします。


![KeyCloakOperator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/7dd09005-016b-cfb6-26a4-dc5444f8d03b.png)


## Keycloak のインスタンス作成
インストール済みの Operator の中から Keycloak Operator を選択し、 Keycloak インスタンスを作成します。
Keycloak の作成画面で、YAML ビューを選択し以下の内容を貼りつけて作成をクリックします。

![KeycloakInstance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/b398fbce-6f81-8d25-d0d1-a12f9f7f1fa2.png)

``` yaml
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  instances: 1
  db:
    vendor: postgres
    host: postgres-db
    usernameSecret:
      name: keycloak-db-secret
      key: username
    passwordSecret:
      name: keycloak-db-secret
      key: password
  http:
    tlsSecret: example-tls-secret
  hostname:
    hostname: test.keycloak.org
```


Keycloak の Pod が正常に動いていることを確認するために　`oc get pods` を実行します。Operator, Postgresql, Keycloak の3つが Running となっていれば OK です。

```
oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
example-kc-0                    1/1     Running   0          2m12s
postgresql-db-0                 1/1     Running   0          80m
rhbk-operator-7975df488-9gx22   1/1     Running   0          57m
```

Keycloak の管理ポータルにアクセスするときに利用するユーザとパスワードを確認します。
```
oc get secret example-kc-initial-admin -o jsonpath='{.data.username}' | base64 --decode
oc get secret example-kc-initial-admin -o jsonpath='{.data.password}' | base64 --decode
``````

## Keycloak へアクセス
Keycloak の Hostname に設定した `test.keycloak.org` の名前解決ができるようにする必要があります。

`oc get ingress` コマンドで、Ingress Controller のアドレスを確認し、ADDRESS 列に出力された FQDN の IP アドレスを確認し、hosts (MacやLinuxの場合は /etc/hosts) にエントリを追加します。

```
% oc get ingress
NAME                 CLASS    HOSTS               ADDRESS                                                        PORTS   AGE
example-kc-ingress   <none>   test.keycloak.org   router-default.apps.cluster-000000.dynamic.redhatworkshops.io   80      13m

% dig router-default.apps.cluster-000000.dynamic.redhatworkshops.io
```

これで、ブラウザで `http://test.keycloak.org` アクセスできるようになりました。

![test.keycloak.org.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/1a5ae170-3756-5ebc-4704-62cf250645f1.png)


# Keycloak を利用した OpenShift の認証の設定

![oAuth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/95d274dd-c087-f102-34aa-b0009f47cbee.png)


## Keycloak の Realm の登録

Keycloak のポータルにアクセスして、OpenShift の認証に利用する Realm を `demo` という名前で作成し、`openshift` という名前の Client を作成します。その後、 User を追加します。

![KeycloakRealmDemo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/90aa24d2-3080-41b6-6623-232f0f8cd1ca.png)


## OpenShift の　Identity Provider の設定

OpenShift のポータルの管理メニューでクラスター設定を選択し、設定タブで `oauth` というキーワードで検索し `oAuth` を選択します。
oAuth の詳細画面の下の方のアイデンティティプロバイダーの追加リストで `OpenID Connect` を選択し、Keycloak での認証の設定をします。
アイデンティティプロバイダの追加画面で、名前、クライアントID、クライアントシークレット、発行者のURL (Issuer URL)とTSLの証明書をアップロードして保存すれば設定は完了です。

![oidc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/7ca214b9-e71f-7bc7-6161-3ea7b7a2310d.png)


反映時にエラーなどがある場合は、ClusterOperators の `authentication` の詳細でエラーなどが確認できます。


# 動作確認

ローカルで起動中の OpenShift の URL にアクセスすると、ID Provider の選択ができるのでここで `demo` を選択します。
Keycloak のユーザ認証画面にリダイレクトされるのでそこで、ユーザ/パスワードを入力して認証が OK ならば、OpenShift の画面に遷移します。
![login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151083/01d45e5b-a536-bb28-41c4-9a1674399fb6.png)

# まとめ
Keycloak を OpenShift の Identity Provider として利用するための設定方法を説明しました。　 OpenShift では、様々な Identity Provider と連携することができますので使い方に合わせて最適な方式を選択してみてください。