1. ガイドの概要

このガイドの目的は、Keycloakサーバーの初回起動の前に完了する必要があるいくつかの手順について説明することです。Keycloakをテストしたいだけの場合は、組み込みのローカル専用のデータベースでそのまま実行する方がよいでしょう。実際にプロダクション環境にデプロイしたい場合は、まずどのようにランタイム(スタンドアローン・モードまたはドメインモード)でサーバー設定を管理するか決定して、Keycloakの共有データベースを設定し、暗号化とHTTPSの設定を行い、最後にKeycloakをセットアップしてクラスター内で起動する必要があります。このガイドは、Keycloakサーバーをデプロイする前に、起動に先んじて定義と設定が必要なすべての手順を、ひとつひとつ説明していきます。

ただし、KeycloakはWildFlyアプリケーション・サーバーに基づいていて、Keycloakの設定はWildFlyの設定項目と密接に関わっています。そのためこのガイドでは、詳細な説明について、このチュートリアルとは別の外部ドキュメントを紹介することがあります。その点は留意ください。

1.1. 推奨される追加の外部ドキュメント

KeycloakはWildFlyアプリケーション・サーバーに追加構築されたもので、Infinispan(キャッシュのため)およびHibernate(永続化のため)のようなサブプロジェクトのひとつです。このガイドでは、基本的なインフラ構築レベルの設定のみ説明します。したがって、是非WildFlyおよびそのサブプロジェクトのドキュメントを熟読することをお勧めします。ドキュメントへのリンクは以下のとおりです。

2. インストール

Keycloakのインストールは、ダウンロードして解凍するだけです。この章では、配布物のディレクトリー構造とシステム要件について説明します。

2.1. システム要件

Keycloak認証サーバーを実行するための要件は、以下のとおりです。

  • Javaが実行可能なオペレーティング・システム

  • Java 8 JDK

  • zipまたはgzip、およびtar

  • RAM512M以上

  • ディスクスペース1G以上

  • PostgreSQL、MySQL、Oracleなどの共有外部データベース。Keycloakをクラスターで実行する場合は、外部共有データベースが必要です。詳細については、このガイドのデータベース設定のセクションを参照してください。

  • クラスターを構成する場合はマルチキャスト・ネットワークのサポート。Keycloakは マルチキャストなしでもクラスターを構成できますが、多くの設定変更が必要になります。詳しくは、このガイドのクラスタリングセクションを参照してください。

  • Linuxにおいては、セキュリティー・ポリシーにより /dev/random の使用が義務付けられている場合を除き、ランダムデータのソースとして /dev/urandom を使用することをお勧めします。エントロピー不足が原因でKeycloakが固まるのを防ぐことができます。Oracle JDK 8およびOpenJDK 8でこれを使用するには、 起動時の java.security.egd システム・プロパティーを file:/dev/urandom に設定してください。

2.2. 配布ファイルのインストール

Keycloakサーバーには、ダウンロード可能な3つの配布物があります。

  • 'keycloak-6.0.1.[zip|tar.gz]'

  • 'keycloak-overlay-6.0.1.[zip|tar.gz]'

  • 'keycloak-demo-6.0.1.[zip|tar.gz]'

'keycloak-6.0.1.[zip|tar.gz]'ファイルはサーバーのみの配布物です。Keycloakサーバーを実行するスクリプトとバイナリー以外は含んでいません。このファイルを開くには、動作システムの unzip または gunzip および tar のユーティリティーを実行します。

'keycloak-overlay-6.0.1.[zip|tar.gz]'ファイルは、WildFlyの配布物に追加してKeycloakサーバーをインストールするWildFlyのアドオンです。アプリケーションとKeycloakを同じサーバー・インスタンスで実行したい場合は、サポートされません。Keycloakサービスパックをインストールするには、WildFlyの配布物のルート・ディレクトリー内で解凍し、シェルでbinディレクトリーを開いて、 ./jboss-cli.[sh|bat] --file=keycloak-install.cli を実行します。

'keycloak-demo-6.0.1.[zip|tar.gz]' には、サーバー・バイナリー、ドキュメントおよびサンプルが含まれています。OIDCとSAML、両方のクライアント・アプリケーション・アダプターがあらかじめ設定されており、サンプルをそのままデプロイすることができます。特に何か設定する必要はありません。しかし、プロダクション環境でデモ配布物の実行はサポートされないため、この配布物はKeycloakをテストする場合にのみ使用することをお勧めします。

これらのファイルを解凍するには、unzip または gunzip および tar ユーティリティーを実行します。

2.3. 配布物のディレクトリー構造

この章では、サーバー配布物のディレクトリー構造について説明します。

配布物のディレクトリー構造

distribution

いくつかのディレクトリーの目的について学んでいきましょう。

bin/

サーバーの起動またはサーバー上でその他管理操作を行う、さまざまなスクリプトが含まれています。

domain/

Keycloakをドメインモードで実行する場合の、設定ファイルとワーキング・ディレクトリーが含まれています。

modules/

サーバー上で使用されるすべてのJavaライブラリーです。

providers/

Keycloakの拡張を作成する場合、ここに配置することができます。詳しくは、Server Developer Guideを参照してください。

standalone/

スタンドアローン・モードでKeycloakを実行する場合、設定ファイルとワーキング・ディレクトリーは含まれません。

themes/

このディレクトリーには、サーバーによって表示されるUI画面を表示するために使用されるすべてのHTML、スタイルシート、JavaScriptファイル、および画像が含まれます。ここでは、既存のテーマを変更したり、独自のテーマを作成したりすることができます。詳細については、 Server Developer Guide を参照してください。

3. 動作モードの選択

プロダクション環境でKeycloakをデプロイする前に、どのタイプの動作モードを使用するか決定する必要があります。クラスター内でKeycloakを実行しますか?サーバー設定を一元管理しますか?どの動作モードを選択するかによって、データベースやキャッシュをどのように設定するか、さらにはサーバーをどのように起動するかさえ変わってきます。

KeycloakはWildFlyアプリケーション・サーバー上に構築されています。このガイドでは、基本的な特定のモードでのデプロイメントについて説明します。詳しくは、WildFly 16 Documentationを参照してください。

3.1. スタンドアローン・モード

スタンドアローン動作モードは、サーバーに1つのKeycloakサーバー・インスタンスを起動する場合にのみ有効です。クラスター構成には使用できません。また、キャッシュは分散されておらずローカル専用です。スタンドアローン・モードは単一障害点となりえるので、プロダクション環境での使用はお勧めしません。スタンドアローン・モードのサーバーがダウンした場合、ユーザーはログインできなくなります。そのため、このモードはテストおよびKeycloakの機能を試す目的にのみ有効です。

3.1.1. スタンドアローン起動スクリプト

スタンドアローン・モードでサーバーを実行する場合、オペレーティング・システム固有の起動スクリプトを実行する必要があります。これらのスクリプトはサーバー配布物の bin/ ディレクトリーにあります。

スタンドアローン起動スクリプト

standalone boot files

To boot the server:

Linux/Unix
$ .../bin/standalone.sh
Windows
> ...\bin\standalone.bat

3.1.2. スタンドアローン設定

このガイドの大半は、Keycloakの基盤レベルの設定について説明します。このレベルの設定は、Keycloakが構築されたアプリケーション・サーバーに特化した設定ファイル内に定義されます。スタンドアローン動作モードでは、このファイルは …​/standalone/configuration/standalone.xml にあります。また、このファイルはKeycloakのコンポーネントに特化した非基盤レベルの設定にも使用されます。

スタンドアローン設定ファイル

standalone config file

サーバーの実行中にこのファイルに変更を加えても、反映されず、サーバーに上書きされる可能性があります。その場合は、代わりにWildFlyのwebコンソールまたはコマンドライン・スクリプトを使用します。詳しくは、WildFly 16 Documentationを参照してください。

3.2. スタンドアローン・クラスター・モード

スタンドアローン・クラスター動作モードは、クラスター内でKeycloakを実行するためのものです。このモードでは、サーバー・インスタンスを実行する各マシンにKeycloakの配布物のコピーが保存されている必要があります。このモードは、最初は非常に簡単にデプロイできますが、後でかなり煩雑になる可能性があります。設定を変更するには、各マシンの配布物を修正する必要があります。大規模なクラスターの場合、時間がかかり、エラーも発生しやすくなります。

3.2.1. スタンドアローン・クラスター設定

この配布物には、クラスター内で実行するためのアプリケーション・サーバーの設定ファイルが含まれており、その大半は設定が済んでいます。ネットワーク、データベース、キャッシュ、およびディスカバリーのための基盤設定がすべて含まれています。このファイルは …​/standalone/configuration/standalone-ha.xml にあります。しかし、この設定だけでは足りません。共有データベース接続を設定せずに、クラスター内でKeycloakを実行することはできません。また、クラスターのフロントにいくつかの種類のロードバランサーをデプロイする必要があります。このガイドのクラスタリングデータベースのセクションでは、これらのことを説明します。

スタンドアローンHA設定

standalone ha config file

サーバーの実行中にこのファイルに変更を加えても、反映されず、サーバーに上書きされる可能性があります。その場合は、代わりにWildFlyのwebコンソールまたはコマンドライン・スクリプトを使用します。詳しくは、WildFly 16 Documentationを参照してください。

3.2.2. スタンドアローン・クラスター起動スクリプト

Keycloakを起動するには、スタンドアローン・モードで実行したのと同じ起動スクリプトを使用します。スタンドアローン・モードとの違いは、HA設定ファイルを指し示す追加フラグを渡すという点になります。

スタンドアローン・クラスター起動スクリプト

standalone boot files

To boot the server:

Linux/Unix
$ .../bin/standalone.sh --server-config=standalone-ha.xml
Windows
> ...\bin\standalone.bat --server-config=standalone-ha.xml

3.3. ドメイン・クラスター・モード

ドメインモードとは、サーバーの設定を一元管理し、クラスター内の各サーバーに反映させる方法です。

標準モードでクラスターを実行すると、クラスターが大きくなり、煩雑化する可能性があります。クラスター内の各ノードにおいて、都度設定を変更する必要がでてきてしまいます。一方、ドメインモードの場合は、設定を保存しパブリッシュするために一元管理する場所を提供することで、この問題を解決できます。セットアップにはかなり手間がかかりますが、最終的にはその工数は見合うことになります。この機能はKeycloakが構成されるWildFlyアプリケーション・サーバーに組み込まれています。

このガイドでは、ドメインモードの初歩的なところを説明します。クラスター内でのドメインモードのセットアップ手順について、詳しくは、WildFly 16 Documentationを参照してください。

ドメインモードを実行する基本的なコンセプトは、以下のとおりです。

ドメイン・コントローラー

ドメイン・コントローラーとは、クラスター内の各ノードの一般的な設定を保存、管理、パブリッシュする役割をもつ、プロセスのことです。また、クラスター内の各ノードが取得する設定を一元管理するデータベースでもあります。

ホスト・コントローラー

ホスト・コントローラーの役割は、特定のマシン内のサーバー・インスタンスを管理することです。1つ以上のサーバー・インスタンスを実行できるよう、ホスト・コントローラーを設定することになります。また、ドメイン・コントローラーはクラスターを管理するために、各マシン内のホスト・コントローラーと連携します。実行プロセスを減らすために、ドメイン・コントローラーに、特定のマシン内のホスト・コントローラーとしての役割を担わせることもできます。

ドメイン・プロファイル

ドメイン・プロファイルとは、サーバーを起動するために使用する、名前付きの設定セットです。ドメイン・コントローラーで、複数のドメイン・プロファイルを定義することができます。そして、さまざまなサーバーがこのドメイン・プロファイルを使うことになります。

サーバーグループ

サーバーグループとは、サーバーの集合体です。ひとつの集合体として管理、設定されます。サーバーグループに、ドメイン・プロファイルを割り当てることができます。そして、そのドメイン・プロファイルはサーバーグループ内のサービスの設定として使用されます。

ドメインモードでは、マスターノード上でドメイン・コントローラーが起動されます。クラスターの設定は、ドメイン・コントローラー内にあります。次に、ホスト・コントローラーがクラスター内の各マシンで起動されます。各ホスト・コントローラーのデプロイ設定では、そのマシンで起動するKeycloakサーバー・インスタンスの数を指定します。ホスト・コントローラーが起動すると、指定された数のKeycloakサーバー・インスタンスが起動します。これらのサーバー・インスタンスは、ドメイン・コントローラーから設定を取得します。

3.3.1. ドメイン設定

このガイドの各章では、データベース、HTTPネットワーク接続、キャッシュ、およびその他の基盤関連のさまざまな設定について説明します。これらを設定するために、スタンドアローン・モードでは standalone.xml ファイルを使用するのに対して、ドメインモードでは …​/domain/configuration/domain.xml を使用します。Keycloakサーバーのドメイン・プロファイルとサーバーグループは、以下で定義されます。

domain.xml

domain file

ドメイン・コントローラーの実行中にこのファイルに変更を加えても、変更されず、サーバーに上書きされる可能性があります。その場合は、代わりにWildFlyのwebコンソールまたはコマンドライン・スクリプトを使用します。詳しくは、WildFly 16 Documentationを参照してください。

この domain.xml ファイルの特徴を確認していきましょう。 auth-server-standaloneauth-server-clusteredprofile のXMLブロックでは、どのような設定にするかの大半を定義します。そして、ネットワーク接続、キャッシュ、データベース接続などを設定します。

認証サーバー・プロファイル
    <profiles>
        <profile name="auth-server-standalone">
            ...
        </profile>
        <profile name="auth-server-clustered">
            ...
        </profile>

auth-server-standalone プロファイルは、非クラスター構成のセットアップ用です。一方 auth-server-clustered プロファイルは、クラスター構成のセットアップ用です。

スクロールダウンしていくと、定義済みの socket-binding-groups が表示されます。

socket-binding-groups
    <socket-binding-groups>
        <socket-binding-group name="standard-sockets" default-interface="public">
           ...
        </socket-binding-group>
        <socket-binding-group name="ha-sockets" default-interface="public">
           ...
        </socket-binding-group>
        <!-- load-balancer-socketsはプロダクション環境では取り除かれ、高度なソフトウェアまたはハードウェアに取り換えられます -->
        <socket-binding-group name="load-balancer-sockets" default-interface="public">
           ...
        </socket-binding-group>
    </socket-binding-groups>

この設定では、各Keycloakサーバー・インスタンスによって開かれる、さまざまなコネクターのデフォルトのポートマッピングを定義します。 ${…​} を含む値はコマンドラインの -D スイッチで上書きできる値です。すなわち、以下のように上書きできます。

$ domain.sh -Djboss.http.port=80

Keycloakのサーバーグループの定義は、 server-groups のXMLブロックにあります。ホスト・コントローラーがインスタンスを起動する場合、この定義により、 default で使用されているドメイン・プロファイル、およびJava VMのデフォルト起動引数が指定されます。また、 socket-binding-group はサーバーグループにバインドされます。

サーバーグループ
    <server-groups>
        <!-- load-balancer-groupはプロダクション環境では取り除かれ、高度なソフトウェアまたはハードウェアに取り換えられます -->
        <server-group name="load-balancer-group" profile="load-balancer">
            <jvm name="default">
                <heap size="64m" max-size="512m"/>
            </jvm>
            <socket-binding-group ref="load-balancer-sockets"/>
        </server-group>
        <server-group name="auth-server-group" profile="auth-server-clustered">
            <jvm name="default">
                <heap size="64m" max-size="512m"/>
            </jvm>
            <socket-binding-group ref="ha-sockets"/>
        </server-group>
    </server-groups>

3.3.2. ホスト・コントローラー設定

Keycloakには、 …​/domain/configuration/ ディレクトリーにある、 host-master.xmlhost-slave.xml の2つのホスト・コントローラー設定ファイルが付属しています。 host-master.xml はドメイン・コントローラー、ロードバランサー、および1つのKeycloakサーバー・インスタンスを起動するように設定されています。一方、 host-slave.xml はドメイン・コントローラーと通信し、1つのKeycloakサーバー・インスタンスを起動するように設定されています。

ロードバランサーは必須サービスではありません。これは、開発マシン上でクラスタリングを簡単にテストできるようにするものです。プロダクション環境で使用可能ですが、使いたい別のハードウェアまたはソフトウェア・ベースのロードバランサーがあるなら、置き換えるかどうかを選択できます。
ホスト・コントローラー設定

host files

ロードバランサー・サーバー・インスタンスを無効にするには、 host-master.xml を編集し、 "load-balancer" エントリーをコメントアウトまたは削除します。

    <servers>
        <!-- 次の行を削除またはコメントアウト -->
        <server name="load-balancer" group="loadbalancer-group"/>
        ...
    </servers>

このファイルに関してもう1つ興味深い点は、認証サーバー・インスタンスの宣言です。これには port-offset が設定されています。 domain.xmlsocket-binding-group またはサーバーグループで定義されたネットワークポートには、port-offsetの値が加算されます。このサンプルでは、ロードバランサー・サーバーによって開かれたポートが、起動中の認証サーバー・インスタンスと競合しないように設定しています。

    <servers>
        ...
        <server name="server-one" group="auth-server-group" auto-start="true">
             <socket-bindings port-offset="150"/>
        </server>
    </servers>

3.3.3. サーバー・インスタンス・ワーキング・ディレクトリー

ホスト・ファイルに定義されている各Keycloakサーバー・インスタンスにより、 …​/domain/servers/{SERVER NAME} の下に作業ディレクトリーが作成されます。そこに追加の設定を保存でき、サーバー・インスタンスが必要とする、または作成する一時ファイル、ログファイル、データファイルも保存することができます。これらのサーバー・ディレクトリーごとの構造は、他のWildFlyブートサーバーと同じようなものになります。

ワーキング・ディレクトリー

domain server dir

3.3.4. ドメイン起動スクリプト

ドメインモードでサーバーを実行する場合、オペレーティング・システム固有の起動スクリプトを実行する必要があります。これらのスクリプトは、サーバー配布物の bin/ ディレクトリーにあります。

ドメイン起動スクリプト

domain boot files

To boot the server:

Linux/Unix
$ .../bin/domain.sh --host-config=host-master.xml
Windows
> ...\bin\domain.bat --host-config=host-master.xml

起動スクリプトを実行する場合、 --host-config スイッチ経由で、使用するホスト制御設定ファイルを渡す必要があります 。

3.3.5. クラスター構成ドメインのサンプル

そのまま利用可能な domain.xml 設定を使用してクラスタリングをテストできます。このサンプルドメインは、1台のマシンで実行され、以下を起動します。

  • ドメイン・コントローラー

  • HTTPロードバランサー

  • 2つのKeycloakサーバー・インスタンス

2台のマシンでのクラスターの実行をシミュレートするには、 domain.sh スクリプトを2回実行して2つの別々のホスト・コントローラーを起動させます。はじめに、ドメイン・コントローラー、HTTPロード・バランサー、および1つのKeycloak認証サーバー・インスタンスを起動するマスター・ホスト・コントローラーを起動させます。次に、認証サーバー・インスタンスを起動するだけのスレーブ・ホスト・コントローラーを起動させます。

ドメイン・コントローラーへのスレーブ接続セットアップ

ホスト・コントローラーを起動する前に、スレーブ・ホスト・コントローラーをドメイン・コントローラーと安全に通信できるように設定する必要があります。これを設定しなかった場合、スレーブホストはドメイン・コントローラーから一元管理された設定を取得できなくなります。安全な接続をセットアップするには、マスターとスレーブの間で共有されるサーバー管理ユーザーとシークレットを作成する必要があります。これを作成するには、 …​/bin/add-user.sh スクリプトを実行します。

スクリプトを実行する場合は Management User を選び、新規ユーザーがASプロセスを別のプロセスに接続するかどうかを尋ねるメッセージが表示された場合は yes と回答します。これにより、 …​/domain/configuration/host-slave.xml ファイルにカット・アンド・ペーストする必要があるシークレットが生成されます。

アプリケーション・サーバー管理者の追加
$ add-user.sh
 What type of user do you wish to add?
  a) Management User (mgmt-users.properties)
  b) Application User (application-users.properties)
 (a): a
 Enter the details of the new user to add.
 Using realm 'ManagementRealm' as discovered from the existing property files.
 Username : admin
 Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.
  - The password should not be one of the following restricted values {root, admin, administrator}
  - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
  - The password should be different from the username
 Password :
 Re-enter Password :
 What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]:
 About to add user 'admin' for realm 'ManagementRealm'
 Is this correct yes/no? yes
 Added user 'admin' to file '/.../standalone/configuration/mgmt-users.properties'
 Added user 'admin' to file '/.../domain/configuration/mgmt-users.properties'
 Added user 'admin' with groups to file '/.../standalone/configuration/mgmt-groups.properties'
 Added user 'admin' with groups to file '/.../domain/configuration/mgmt-groups.properties'
 Is this new user going to be used for one AS process to connect to another AS process?
 e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
 yes/no? yes
 To represent the user add the following to the server-identities definition <secret value="bWdtdDEyMyE=" />
add-user.shにより、Keycloakサーバーにではなく、基盤となるJBoss Enterprise Application Platformにユーザーが追加されます。上記のスクリプトで使用、生成された証明書は、あくまでサンプルです。お使いのシステムで生成されたものを使用してください。

…​/domain/configuration/host-slave.xml ファイルにシークレットの値をカット・アンド・ペーストすると、以下のようになります。

     <management>
         <security-realms>
             <security-realm name="ManagementRealm">
                 <server-identities>
                     <secret value="bWdtdDEyMyE="/>
                 </server-identities>

作成したユーザーの username…​/domain/configuration/host-slave.xml ファイルに追加する必要があります。

     <remote security-realm="ManagementRealm" username="admin">
起動スクリプトの実行

1つの開発マシンで2つのノードクラスターをシミュレートするので、以下のとおり、起動スクリプトを2回実行します。

マスターの起動
$ domain.sh --host-config=host-master.xml
スレーブの起動
$ domain.sh --host-config=host-slave.xml

これを試すには、ブラウザーを開いてhttp://localhost:8080/authに移動してください。

3.4. クロスデータセンター・レプリケーション・モード

クロスデータセンター・レプリケーション・モードは、複数のデータセンターを横断してクラスター内でKeycloakを実行する必要がある時のためのもので、地理的に異なる地域にあるデータセンター・サイトが一般的には最も使用されます。このモードを使用する場合、各データセンターにはKeycloakサーバーの独自のクラスターがあります。

このドキュメントでは、次のアーキテクチャー図の例を参照して、単純なクロスデータセンター・レプリケーションのユースケースを図解および説明します。

アーキテクチャー図の例

cross dc architecture

3.4.1. 前提条件

これは高度なトピックのため、最初に以下を読み、背景にある重要な知識を身につけておくことをお勧めします。

  • Keycloakによるクラスタリング クロスデータセンター・レプリケーションを設定する場合、より独立したKeycloakクラスターを使用するため、クラスターの仕組みや、ロード・バランシング、共有データベース、マルチキャストなどの基本的な概念と要件を理解する必要があります。

  • JBoss Data Grid Cross-Datacenter Replication Keycloakでは、JBoss Data Grid(JDG)を使用して、データセンター間でInfinispanデータをレプリケーションします。

3.4.2. 技術的な詳細

このセクションでは、Keycloakクロスデータセンター・レプリケーションの仕組みについて概念と詳細について説明します。

データ

Keycloakはステートフルなアプリケーションです。データソースとして以下のものを使用します。

  • データベースは、ユーザー情報などの永続的なデータを保持するために使用されます。

  • Infinispanキャッシュは、永続化データをデータベースからキャッシュし、短期間で頻繁に変化するメタデータ(ユーザー・セッションなど)を節約するためにも使用されます。Infinispanは通常、データベースよりもはるかに高速ですが、Infinispanを使用して保存されたデータは永続的ではなく、クラスターを再起動後も維持できるとは限りません。

このアーキテクチャーの例では、 site1site2 と呼ばれる2つのデータセンターがあります。クロスデータセンター・レプリケーションでは、両方のデータソースが確実に動作し、 site2 のKeycloakサーバーによって保存されたデータを site1 のKeycloakサーバーが最終的に読み取ることができるようにする必要があります。

環境に基づいて、次の選択肢から決める必要があります。

  • 信頼性 - 通常はアクティブ/アクティブモードで使用されます。 site1 で記述されたデータは site2 ですぐに表示される必要があります。

  • パフォーマンス - 通常はアクティブ/パッシブモードで使用されます。 site1 で記述されたデータはすぐに site2 で表示される必要はありません。状況によって、 site2 でデータが表示されないこともあります。

詳細は、モードを参照してください。

3.4.3. リクエスト処理

エンドユーザーのブラウザーは、HTTPリクエストをフロント・エンド・ロードバランサーに送信します。このロードバランサーは、通常mod_cluster、NGINX、HA Proxy、またはその他のソフトウェアかハードウェア・ロードバランサーを使用するHTTPDまたはWildFlyです。

ロードバランサーは、基になるKeycloakインスタンスに、受け取ったHTTPリクエストを転送します。このインスタンスは、複数のデータセンターに分散させることができます。ロードバランサーは通常、スティッキー・セッションをサポートしています。つまり、ロードバランサーは、同じデータセンターの同じKeycloakインスタンスに、同じユーザーのすべてのHTTPリクエストを常に転送することができます。

クライアント・アプリケーションからロードバランサーに送られたHTTPリクエストは バックチャネル・リクエスト と呼ばれます。これらはエンドユーザーのブラウザーからは見えないので、ユーザーとロードバランサー間でスティッキー・セッションの一部になることはできません。バックチャネル・リクエストの場合、ロードバランサーは、HTTPリクエストを任意のデータセンター内のいずれかのKeycloakインスタンスに転送することができます。これは、いくつかのOpenID ConnectといくつかのSAMLフローがユーザーとアプリケーションの両方から複数のHTTPリクエストを必要とするため、難しい問題です。関連するすべてのリクエストを同じデータセンター内の同じKeycloakインスタンスに送信するのにスティッキー・セッションに完全に依存することはできないため、代わりに、データセンター間で一部のデータをレプリケートする必要があります。それにより、データは特定のフロー中の後続のHTTPリクエストによって表示されます。

3.4.4. モード

要件に応じて、クロスデータセンター・レプリケーションには2つの基本的な動作モードがあります。

  • アクティブ/パッシブ - ユーザーとクライアント・アプリケーションは、単一のデータセンター内のKeycloakノードにのみリクエストを送信します。第2のデータセンターは、データを保存するための バックアップ としてのみ使用されます。メインのデータセンターに障害が発生した場合、通常は第2のデータセンターからデータを復旧します。

  • アクティブ/アクティブ - ユーザーとクライアント・アプリケーションは、両方のデータセンターのKeycloakノードにリクエストを送信します。つまり、両方のサイトですぐにデータを表示し、 両方のサイトのKeycloakサーバーからデータをすぐに使用できるようにする必要があります。これは、Keycloakサーバーが site1 に何かしらのデータを書き込む場合に特に当てはまります。また、 site1 への書き込みが完了した直後に、 site2 のKeycloakサーバーがデータをすぐに読み取ることができるようにする必要があります。

アクティブ/パッシブモードは、パフォーマンスに優れています。いずれかのモードでキャッシュを設定する方法の詳細については、 SYNCまたはASYNCバックアップ を参照してください。

3.4.5. データベース

Keycloakは、リレーショナル・データベース・マネジメント・システム(RDBMS)を使用して、レルム、クライアント、ユーザーなどのメタデータを保持します。詳細については、サーバーインストールガイドのこの章を参照してください。クロスデータセンター・レプリケーションのセットアップでは、両方のデータセンターが同じデータベースと通信するか、すべてのデータセンターに独自のデータベース・ノードがあり、両方のデータベース・ノードがデータセンター間で同期レプリケートされると想定しています。どちらの場合でも、 site1 のKeycloakサーバーがデータを保持してトランザクションをコミットすると、 site2 の後続のDBトランザクションによって、それらのデータがすぐに表示される必要があります。

DBのセットアップの詳細については、Keycloakの範囲外ですが、MariaDBやOracleなどのRDBMSベンダーの多くは、レプリケートされたデータベースと同期レプリケーションを提供しています。これらのベンダーを使用して、Keycloakをテストしています。

  • Oracle Database 12c Release 1 (12.1) RAC

  • Galera 3.12 cluster for MariaDB server version 10.1.19-MariaDB

3.4.6. Infinispanキャッシュ

このセクションでは、Infinispanキャッシュの概要を説明していきます。キャッシュの設定の詳細は以下のとおりです。

認証セッション

Keycloakには、認証セッションの概念があります。 authenticationSessions と呼ばれる別のInfinispanキャッシュがあり、特定のユーザーの認証時にデータを保存するのに使用されます。このキャッシュからのリクエストには通常、ブラウザーとKeycloakサーバーのみが関与し、アプリケーションは関与しません。ここでは、アクティブ/アクティブモードであっても、スティッキー・セッションに依存することができ、 authenticationSessions キャッシュ・コンテンツをデータセンター間でレプリケートする必要はありません。

アクション・トークン

アクション・トークンの概念もあります。アクション・トークンは、通常、ユーザーが電子メールでアクションを非同期で確認する必要があるシナリオで使用されます。たとえば、 forget password フローの間に、 actionTokens Infinispanキャッシュは、どのアクション・トークンがすでに使用されているかなどの関連するアクション・トークンに関するメタデータを追跡するために使用されるため、2度目は再利用できません。これは通常、データセンター間でレプリケートする必要があります。

永続データのキャッシングと無効化

KeycloakはInfinispanを使用して永続化データをキャッシュし、データベースへの不要なリクエストを多く回避します。キャッシュによりパフォーマンスは改善されますが、さらなる問題が加わります。一部のKeycloakサーバーがデータを更新した場合、すべてのデータセンターの他のすべてのKeycloakサーバーはそのことに気づく必要があるため、それらのキャッシュから特定のデータを無効にします。Keycloakは、 realmsusers 、および authorization と呼ばれるローカルInfinispanキャッシュを使用して、永続化データをキャッシュします。

すべてのデータセンターでレプリケートされる、別のキャッシュ work を使用します。workキャッシュ自体は実際のデータをキャッシュしません。クラスターノードとデータセンター間で無効化メッセージを送信する場合にのみ使用されます。つまり、データ(ユーザー john のようなデータ)が更新されると、Keycloakノードは、同じデータセンター内の他のすべてのクラスターノード、および他のすべてのデータセンターに無効化メッセージを送信します。すべてのノードは、無効通知を受信した後、ローカル・キャッシュから適切なデータを無効にします。

ユーザー・セッション

sessionsclientSessionsofflineSessions 、および offlineClientSessions と呼ばれるInfinispanキャッシュがあり、それらのすべては通常、データセンター間でレプリケートされる必要があります。これらのキャッシュは、ユーザー・セッションに関するデータを保存するために使用され、ユーザーのブラウザー・セッションの長さに対して有効です。キャッシュは、エンドユーザーとアプリケーションからのHTTPリクエストを処理する必要があります。前述のとおり、このインスタンスではスティッキー・セッションを信頼性をもって使用することはできませんが、後続のHTTPリクエストが最新のデータを確認できるようにする必要があります。このため、データは通常、データセンター間でレプリケートされます。

ブルートフォース保護

最後に、 loginFailures キャッシュは、ユーザー john が不正なパスワードを入力した回数など、ログイン失敗に関するデータを追跡するために使用されます。詳細は、こちらを参照してください。このキャッシュをデータセンター間でレプリケートするかどうかは、管理者次第です。正確なログイン失敗の回数を取得するには、レプリケーションが必要です。一方、このデータをレプリケートしないことで、パフォーマンスをよくできます。したがって、パフォーマンスがログイン失敗の正確な回数よりも重要な場合は、レプリケーションを避けるという手もあります。

キャッシュの設定方法の詳細については、JDGキャッシュ設定のチューニングを参照してください。

3.4.7. コミュニケーションの詳細

Keycloakは、Infinispanキャッシュの分割されたクラスターを複数使用します。各Keycloakノードは、同じデータセンター内の他のKeycloakノードと共にクラスター内にありますが、異なるデータセンターのKeycloakノードはそのクラスター内にありません。Keycloakノードは、異なるデータセンターのKeycloakノードと直接通信できません。Keycloakノードは、データセンター間の通信に外部JDG (実際にはInfinispanサーバー)を使用します。これは、Infinispan HotRodプロトコルを使用して行われます。

Keycloak側のInfinispanキャッシュは、データがリモート・キャッシュに保存されていることを確認するために、remoteStoreを使用して設定する必要があります。JDGサーバー間に分割されたInfinispanクラスターがあるので、 site1 のJDG1に保存されていたデータは site2 のJDG2にレプリケートされます。

最後に、受信JDGサーバーは、HotRodプロトコルの機能であるクライアント・リスナーを介して、クラスター内のKeycloakサーバーに通知します。次に、 site2 のKeycloakノードがInfinispanキャッシュを更新し、特定のユーザー・セッションが site2 のKeycloakノードにも表示されます。

詳細は、アーキテクチャー図の例を参照してください。

3.4.8. 基本設定

この例では、 site1site2 という2つのデータセンター使用して説明します。各データセンターは、1つのInfinispanサーバーと2つのKeycloakサーバーで構成されています。合計すると、2つのInfinispanサーバーと4つのKeycloakサーバーになります。

  • Site1 は、Infinispanサーバーの jdg1 と2つのKeycloakサーバーの node11 および node12 で構成されています。

  • Site2 は、Infinispanサーバーの jdg2 と2つの Keycloakサーバーの node21 および node22 で構成されています。

  • Infinispanサーバーである jdg1jdg2 は、 JDGのドキュメント で記載されているのと同様の方法で、RELAY2プロトコルと backup ベースのInfinispanキャッシュを介して相互に接続されています。

  • Keycloakサーバーである node11node12 は、お互いにクラスターを形成しますが、 site2 内のサーバーとは直接通信はしません。それらはHotRodプロトコル (リモート・キャッシュ)を使用して、Infinispanサーバー jdg1 と通信します。詳細については、コミュニケーションの詳細を参照してください。

  • 同じ説明が node21node22 にも当てはまります。それらはお互いにクラスター化し、HotRodプロトコルを使用して、 jdg2 サーバーとのみ通信します。

この設定の例では、4つのKeycloakサーバーすべてが同じデータベースと通信することを前提としています。プロダクション環境では、データベースで説明されているとおり、データセンター間で別々の同期レプリケートされたデータベースを使用することをお勧めします。

Infinispanサーバーの設定

Infinispanサーバーの設定は、以下の手順に沿って行います。

  1. Infinispan 9.4.8サーバーをダウンロードし、選択したディレクトリーに解凍します。このロケーションは、 JDG1_HOME として後ほど参照することになります。

  2. JGroupsサブシステムの設定で、 JDG1_HOME/standalone/configuration/clustered.xml 内にあるこれらを変更します。

    1. xsite チャネルを追加して、 channels 要素の下にある tcp スタックを使用します。

      <channels default="cluster">
          <channel name="cluster"/>
          <channel name="xsite" stack="tcp"/>
      </channels>
    2. relay 要素を udp スタックの最後尾に追加します。自身のサイトは site1 で、バックアップする他のサイトは site2 というように設定します。

      <stack name="udp">
          ...
          <relay site="site1">
              <remote-site name="site2" channel="xsite"/>
              <property name="relay_multicasts">false</property>
          </relay>
      </stack>
    3. MPING の代わりに、 tcp スタックを設定して TCPPING プロトコルを使用します。 MPING 要素を削除して TCPPING に置き換えます。 initial_hosts 要素は、ホスト jdg1jdg2 を指します。

      <stack name="tcp">
          <transport type="TCP" socket-binding="jgroups-tcp"/>
          <protocol type="TCPPING">
              <property name="initial_hosts">jdg1[7600],jdg2[7600]</property>
              <property name="ergonomics">false</property>
          </protocol>
          <protocol type="MERGE3"/>
          ...
      </stack>
      これは、単にすばやく実行するための設定例です。プロダクション環境では、JGroups RELAY2tcp スタックを使用する必要はなく、他のどのスタックを設定しても構いません。たとえば、データセンター間のネットワークがマルチキャストをサポートできる場合は、デフォルトのudpスタックを使用することができます。InfinispanとKeycloakクラスターがお互いを見つけることはできないという点だけは確認してください。同じように、 TCPPING を検出プロトコルとして使用する必要はありません。また、 TCPPING は静的な性質であるため、使用することはないでしょう。最後に、サイト名も設定することができます。この設定のより詳細な内容については、Keycloakドキュメントの範囲外になります。詳細については、InfinispanのドキュメントおよびJGroupsのドキュメントを参照してください。
  3. JDG1_HOME/standalone/configuration/clustered.xmlclustered という名前のcache-containerの下に次の設定を追加します。

    <cache-container name="clustered" default-cache="default" statistics="true">
            ...
            <replicated-cache-configuration name="sessions-cfg" mode="SYNC" start="EAGER" batching="false">
                <locking acquire-timeout="0" />
                <backups>
                    <backup site="site2" failure-policy="FAIL" strategy="SYNC" enabled="true">
                        <take-offline min-wait="60000" after-failures="3" />
                    </backup>
                </backups>
            </replicated-cache-configuration>
    
            <replicated-cache name="work" configuration="sessions-cfg"/>
            <replicated-cache name="sessions" configuration="sessions-cfg"/>
            <replicated-cache name="clientSessions" configuration="sessions-cfg"/>
            <replicated-cache name="offlineSessions" configuration="sessions-cfg"/>
            <replicated-cache name="offlineClientSessions" configuration="sessions-cfg"/>
            <replicated-cache name="actionTokens" configuration="sessions-cfg"/>
            <replicated-cache name="loginFailures" configuration="sessions-cfg"/>
    
    </cache-container>
    replicated-cache-configuration 内の設定オプションについての詳細は、JDGキャッシュ設定のチューニングで説明しています。これには、これらのオプションのいくつかを調整するための情報が含まれています。
    以前のバージョンとは異なり、Infinispanサーバーの replicated-cache-configurationtransaction 要素なしで設定する必要があります。詳細はトラブルシューティングを参照してください。
  4. 一部のInfinispanサーバーリリースでは、ネットワーク経由で保護されたキャッシュにアクセスする前に認可が必要です。

    推奨されたInfinispan 9.4.8サーバーを使用している場合は、何も問題はありません。この手順は無視しても構いません(無視すべきです)。認可に関連する問題は、他のバージョンのInfinispanサーバーにのみ存在する可能性があります。

    Keycloakはスクリプトを含む ___script_cache キャッシュへの更新が必要です。このキャッシュにアクセスする際にエラーが発生した場合、下記のように clustered.xml 設定で認可を設定する必要があります。

    1. <management> のセクションの中で、次のようにセキュリティー・レルムを追加します。

      <management>
          <security-realms>
              ...
              <security-realm name="AllowScriptManager">
                  <authentication>
                      <users>
                          <user username="___script_manager">
                              <password>not-so-secret-password</password>
                          </user>
                      </users>
                  </authentication>
              </security-realm>
          </security-realms>
    2. サーバー・コア・サブシステムで、以下のように <security> を追加します。

      <subsystem xmlns="urn:infinispan:server:core:8.4">
          <cache-container name="clustered" default-cache="default" statistics="true">
              <security>
                  <authorization>
                      <identity-role-mapper/>
                      <role name="___script_manager" permissions="ALL"/>
                  </authorization>
              </security>
              ...
    3. エンドポイント・サブシステムで、Hot Rodコネクターに認証設定を追加します。

      <subsystem xmlns="urn:infinispan:server:endpoint:8.1">
          <hotrod-connector cache-container="clustered" socket-binding="hotrod">
              ...
              <authentication security-realm="AllowScriptManager">
                  <sasl mechanisms="DIGEST-MD5" qop="auth" server-name="keycloak-jdg-server">
                      <policy>
                          <no-anonymous value="false" />
                      </policy>
                  </sasl>
              </authentication>
  5. 2つ目のロケーションにサーバーをコピーします。これは、 JDG2_HOME として後ほど参照することになります。

  6. JDG2_HOME/standalone/configuration/clustered.xml では、 site1site2 に置き換えると、JGroupsサブシステム内の relay の設定とcache-subsystem内の backups の設定の両方において、逆の動作をします。次に例を示します。

    1. relay 要素は、以下のように表示されます。

      <relay site="site2">
          <remote-site name="site1" channel="xsite"/>
          <property name="relay_multicasts">false</property>
      </relay>
    2. backups 要素は、以下のように表示されます。

                  <backups>
                      <backup site="site1" ....
                      ...

      現時点では、Infinispanサブシステムはサイト名を式によって置き換えることはサポートしていないため、両方のサイトのJDGサーバーに異なる設定ファイルを用意する必要があります。詳細については、この課題を参照してください。

  7. jdg1 サーバーを起動します。

    cd JDG1_HOME/bin
    ./standalone.sh -c clustered.xml -Djava.net.preferIPv4Stack=true \
      -Djboss.default.multicast.address=234.56.78.99 \
      -Djboss.node.name=jdg1 -b PUBLIC_IP_ADDRESS
  8. jdg2 サーバーを起動します。異なるマルチキャスト・アドレスがあるため、 jdg1jdg2 サーバーは互いに直接クラスター化されません。むしろ、それらはRELAY2プロトコルを介して接続されており、TCP JGroupsスタックはそれらの間の通信に使用されます。起動コマンドは次のようになります。

    cd JDG2_HOME/bin
    ./standalone.sh -c clustered.xml -Djava.net.preferIPv4Stack=true \
      -Djboss.default.multicast.address=234.56.78.100 \
      -Djboss.node.name=jdg2 -b PUBLIC_IP_ADDRESS
  9. この時点でチャネルが動作していることを検証するには、JConsoleを使用し、実行中の JDG1 または JDG2 サーバーに接続する必要があります。MBean jgroups:type=protocol,cluster="cluster",protocol=RELAY2 および printRoutes オペレーションを使用すると、以下のような出力が表示されます。

    site1 --> _jdg1:site1
    site2 --> _jdg2:site2

    MBean jgroups:type=protocol,cluster="cluster",protocol=GMS を使用すると、属性メンバーには単一のメンバーしか含まれていないということがわかります。

    1. JDG1 では、以下のように表示されます。

      (1) jdg1
    2. JDG2 では、以下のように表示されます。

      (1) jdg2
      プロダクション環境では、すべてのデータセンターにさらに多くのInfinispanサーバーを持つことができます。同じデータセンター内のInfinispanサーバーが同じマルチキャスト・アドレスを使用していること(つまり、起動中に同じ jboss.default.multicast.address を使用していること)を確認するだけです。そうすると、 GMS プロトコルビューのjconsoleに、現在のクラスターのメンバーがすべて表示されます。
Keycloakサーバーの設定
  1. Keycloakサーバー配布物を選択した場所に解凍します。これは NODE11 として後ほど参照することになります。

  2. KeycloakDSデータソースの共有データベースを設定します。テストを目的とする場合は、MySQLまたはMariaDBの使用をお勧めします。詳細はデータベースを参照してください。

    プロダクション環境では、すべてのデータセンターにおいて別々のデータベース・サーバーを用意する必要があり、両方のデータベース・サーバーを互いに同期してレプリケートする必要があります。設定例では、単一のデータベースを使用し、4つのKeycloakサーバーすべてに接続します。

  3. NODE11/standalone/configuration/standalone-ha.xml を以下のように編集してください。

    1. site 属性をJGroups UDPプロトコルに追加します。

                        <stack name="udp">
                            <transport type="UDP" socket-binding="jgroups-udp" site="${jboss.site.name}"/>
    2. 名前が keycloakcache-container 要素の下に、この module 属性を追加します。

      <cache-container name="keycloak"
      module="org.keycloak.keycloak-model-infinispan">
    3. work キャッシュの下に remote-store を追加します。

      <replicated-cache name="work">
          <remote-store cache="work" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </replicated-cache>
    4. session キャッシュの下に remote-store を追加します。

      <distributed-cache name="sessions" owners="1">
          <remote-store cache="sessions" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
    5. offlineSessionsclientSessionsofflineClientSessionsloginFailuresactionTokens キャッシュでも同じことをします( sessions キャッシュとの唯一の違いは、 cache プロパティー値が異なることです)。

      <distributed-cache name="offlineSessions" owners="1">
          <remote-store cache="offlineSessions" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
      
      <distributed-cache name="clientSessions" owners="1">
          <remote-store cache="clientSessions" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
      
      <distributed-cache name="offlineClientSessions" owners="1">
          <remote-store cache="offlineClientSessions" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
      
      <distributed-cache name="loginFailures" owners="1">
          <remote-store cache="loginFailures" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="false" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
      
      <distributed-cache name="actionTokens" owners="2">
          <object-memory size="-1"/>
          <expiration max-idle="-1" interval="300000"/>
          <remote-store cache="actionTokens" remote-servers="remote-cache" passivation="false" fetch-state="false" purge="false" preload="true" shared="true">
              <property name="rawValues">true</property>
              <property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
          </remote-store>
      </distributed-cache>
    6. リモートストアのアウトバウンド・ソケット・バインディングを socket-binding-group 要素の設定に追加します。

      <outbound-socket-binding name="remote-cache">
          <remote-destination host="${remote.cache.host:localhost}" port="${remote.cache.port:11222}"/>
      </outbound-socket-binding>
    7. 分散キャッシュ設定の authenticationSessions と他のキャッシュは変更されません。

    8. オプションで、 logging サブシステムの下でDEBUGログを有効にします。

      <logger category="org.keycloak.cluster.infinispan">
          <level name="DEBUG"/>
      </logger>
      <logger category="org.keycloak.connections.infinispan">
          <level name="DEBUG"/>
      </logger>
      <logger category="org.keycloak.models.cache.infinispan">
          <level name="DEBUG"/>
      </logger>
      <logger category="org.keycloak.models.sessions.infinispan">
          <level name="DEBUG"/>
      </logger>
  4. NODE11 を後述する NODE12NODE21NODE22 という3つのディレクトリーにコピーしてください。

  5. 次のように、 NODE11 を起動してください。

    cd NODE11/bin
    ./standalone.sh -c standalone-ha.xml -Djboss.node.name=node11 -Djboss.site.name=site1 \
      -Djboss.default.multicast.address=234.56.78.1 -Dremote.cache.host=jdg1 \
      -Djava.net.preferIPv4Stack=true -b PUBLIC_IP_ADDRESS
  6. 次のように、 NODE12 を起動してください。

    cd NODE12/bin
    ./standalone.sh -c standalone-ha.xml -Djboss.node.name=node12 -Djboss.site.name=site1 \
      -Djboss.default.multicast.address=234.56.78.1 -Dremote.cache.host=jdg1 \
      -Djava.net.preferIPv4Stack=true -b PUBLIC_IP_ADDRESS

    クラスター・ノードを接続する必要があります。このようなものは、NODE11とNODE12のどちらのログにも残っていなければなりません。

    Received new cluster view for channel keycloak: [node11|1] (2) [node11,
    node12]
    ログにあるチャネル名とは異なっている可能性があります。
  7. 次のように、 NODE21 を起動してください。

    cd NODE21/bin
    ./standalone.sh -c standalone-ha.xml -Djboss.node.name=node21 -Djboss.site.name=site2 \
      -Djboss.default.multicast.address=234.56.78.2 -Dremote.cache.host=jdg2 \
      -Djava.net.preferIPv4Stack=true -b PUBLIC_IP_ADDRESS

    NODE11NODE12 を使用してクラスターに接続するのではなく、クラスターを分離する必要があります。

    Received new cluster view for channel keycloak: [node21|0] (1) [node21]
  8. 次のように、 NODE22 を起動してください。

    cd NODE22/bin
    ./standalone.sh -c standalone-ha.xml -Djboss.node.name=node22 -Djboss.site.name=site2 \
      -Djboss.default.multicast.address=234.56.78.2 -Dremote.cache.host=jdg2 \
      -Djava.net.preferIPv4Stack=true -b PUBLIC_IP_ADDRESS

    NODE21 をクラスター化する必要があります。

    Received new cluster view for channel keycloak: [node21|1] (2) [node21,
    node22]
    ログにあるチャネル名とは異なっている可能性があります。
  9. 次のように、テストを行ってください。

    1. http://node11:8080/auth/ に移動し、最初の管理者ユーザーを作成します。

    2. http://node11:8080/auth/admin に移動し、管理者として管理コンソールにログインします。

    3. 2つ目のブラウザーを開き、 http://node12:8080/auth/admin または http://node21:8080/auth/admin もしくは http://node22:8080/auth/admin のいずれかのノードに移動します。ログイン後、4つのすべてのサーバー上の特定のユーザー、クライアントまたはレルムの Sessions タブで同じセッションを表示できます。

    4. Keycloakの管理コンソールで変更(たとえば、ユーザーやレルムの更新など)を加えた後、その変更は、すぐに4つのノードのいずれかで表示され、キャッシュがどこでも適切に無効化される必要があります。

    5. 必要に応じてserver.logsを確認してください。ログインまたはログアウト後、以下のようなメッセージがすべての NODEXY/standalone/log/server.log ノードに表示される必要があります。

      2017-08-25 17:35:17,737 DEBUG [org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheSessionListener] (Client-Listener-sessions-30012a77422542f5) Received event from remote store.
      Event 'CLIENT_CACHE_ENTRY_REMOVED', key '193489e7-e2bc-4069-afe8-f1dfa73084ea', skip 'false'

3.4.9. Cross DCデプロイメントの管理

このセクションでは、クロスデータセンター・レプリケーションに関するオプションとヒントについて説明します。

  • Keycloakサーバーをデータセンター内で実行する場合、 KeycloakDS データソース内で参照されたデータベースがすでに実行され、そのデータセンター内で使用可能である必要があります。また、 Infinispanキャッシュの remote-store 要素から参照される outbound-socket-binding によって参照されたInfinispanサーバーがすでに実行されてることが必要です。そうしないと、Keycloakサーバーは起動に失敗します。

  • データベース・フェイルオーバーと高い信頼性をサポートする必要がある場合、すべてのデータセンターでより多くのデータベース・ノードを持たせます。データベース側での設定方法とKeycloak側の KeycloakDS データソースで必要な設定方法の詳細については、JDBCドライバーのドキュメントを参照してください。

  • すべてのデータセンターで、より多くのInfinispanサーバーをクラスター内で実行することができます。これは、フェイルオーバーとフォールト・トレランスを強化する場合に便利です。InfinispanサーバーとKeycloakサーバー間の通信で使用されるHotRodプロトコルには、InfinispanサーバーがInfinispanクラスターの変更について、Keycloakサーバーに新しいトポロジーを自動的に送信する機能を備えています。したがって、Keycloak側のリモートストアは、どのInfinispanサーバーに接続できるのかが分かります。詳細については、InfinispanとWildFlyドキュメントを参照してください。

  • どんな サイトのKeycloakサーバーも起動する前に、すべてのサイトでマスターInfinispanサーバーを実行することを強くお勧めします。この例では、すべてのKeycloakサーバーの前に、 jdg1jdg2 の両方を最初に起動します。Keycloakサーバーをそれでも実行する必要があり、バックアップ・サイトがオフラインである場合は、サイトをオフラインおよびオンラインにするでの説明のとおり、サイトのInfinispanサーバー上のバックアップ・サイトを手動でオフラインに切り替えることをお勧めします。使用できない状態のサイトをオフラインに手動で切り替えることができない場合、初回起動に失敗するか、起動時にいくつか例外が発生する可能性があります。これは、失敗した操作の設定数によってバックアップ・サイトが自動的にオフラインになるまでです。

3.4.10. サイトをオフラインおよびオンラインにする

たとえば、以下のようなシナリオを想定します。

  1. サイト site1 から見たサイト site2 は、完全にオフラインです。これは、site2 のすべてのInfinispanサーバーがオフである、 またはsite1site2 の間のネットワークが切断されていることを意味します。

  2. サイト site1 でKeycloakサーバーとInfinispanサーバー jdg1 を実行します。

  3. いずれかのユーザーが site1 のKeycloakサーバーにログインします。

  4. site1 のKeycloakサーバーは、site2jdg2 サーバーにデータをバックアップすることを想定し、 jdg1 サーバー上のリモート・キャッシュにセッションを書き込もうとします。詳細については、コミュニケーションの詳細を参照してください。

  5. jdg2 サーバーがオフラインであるか、 jdg1 から到達できない状態なので、 jdg1 から jdg2 へのバックアップは失敗します。

  6. 例外は jdg1 のログにスローされます。デフォルトの FAIL バックアップ失敗ポリシーが設定されているため、その失敗は jdg1 サーバーからKeycloakサーバーにも伝播されます。バックアップ・ポリシーの詳細については、バックアップ失敗ポリシーを参照してください。

  7. このエラーはKeycloak側でも発生し、ユーザーはログインを完了できない可能性もあります。

使用している環境に応じて、サイト間のネットワークが利用できないか、一時的に壊れている(スプリット・ブレイン)可能性があります。これが起こった場合、 site1 のInfinispanサーバーは、 site2 のInfinispanサーバーが利用できないことに気付き、 jdg2 サイトのサーバーへアクセスしようとするのを止めるため、バックアップの失敗は発生しません。これは サイトをオフラインにする と呼ばれています。

サイトをオフラインにする

サイトをオフラインにするには2つの方法があります。

管理者による手作業 - 管理者は jconsole または他のツールを使用して、いくつかのJMX操作を実行することで、手動で特定のサイトをオフラインにすることができます。これは、特に停止が計画されている場合に役立ちます。 jconsole またはCLIを使用すると、 jdg1 サーバーに接続し、 site2 をオフラインにすることができます。この詳細については、 JDGドキュメント を参照してください。

SYNCまたはASYNCバックアップで述べた他のすべてのKeycloakキャッシュについても、通常はこれらの手順を実行する必要があります。

自動的作業 - バックアップに失敗した後、通常は site2 が自動的にオフラインになります。これは、Infinispanサーバーの設定で設定されたキャッシュ設定内の take-offline 要素の設定によって行われます。

<take-offline min-wait="60000" after-failures="3" />

この例では、少なくとも3回連続して失敗したバックアップがあり、60秒以内にバックアップが成功しなかった場合、特定のシングルキャッシュに対してサイトが自動的にオフラインになることを示しています。

自動的にサイトをオフラインにすることは、特に、サイト間の切断されたネットワークが計画外である場合に便利です。欠点は、ネットワークの停止が検出されるまで何らかのバックアップが失敗し、アプリケーション側で障害が起きてる可能性があります。たとえば、一部のユーザーのログインに失敗したり、大きなログインタイムアウトが発生したりします。 特に、値が FAILfailure-policy が使用されている場合です。

サイトがオフラインであるかどうかの追跡は、キャッシュごとに個別に行われます。
サイトをオンラインにする

一旦ネットワークが復旧し、 site1site2 がお互いに通信することができたら、サイトをオンラインにする必要があります。これは、サイトをオフラインにするのと同じように、JMXまたはCLIを使用して手動で行う必要があります。再度キャッシュをすべてチェックし、オンラインにする必要があります。

サイトをオンラインにしたら、通常は次のようにするのが良いです。

3.4.11. ステート・トランスファー

ステート・トランスファーは、手動で行う必要があります。Infinispanサーバーはこれを自動的には行いません。たとえば、スプリット・ブレイン中は、誰がどのサイトを優先するかを管理者のみが決定することができます。したがって、ステート・トランスファーが両方のサイト間で双方向に、または site1 から site2 へのみ単方向で行われる必要がありますが、site2 から site1 への単方向は行われません。

双方向のステート・トランスファーによって、スプリット・ブレインの 後に site1 で作成されたエンティティーが site2 に確実に転送されます。これは site2 ではまだ発生していないので問題ありません。同じように、スプリット・ブレインの 後に site2 で作成されたエンティティーは site1 に転送されます。おそらく問題のある部分は、両方のサイトのスプリット・ブレインの 前に 存在し、両方のサイトのスプリット・ブレイン中に更新されたエンティティーです。これが発生すると、サイトの1つが 勝ち 、2番目のサイトがスプリット・ブレイン中に行った更新を上書きします。

残念ながら、これに対する広く一般的な解決方法はありません。スプリット・ブレインやネットワークの停止は状態に過ぎず、サイト間で100%の一貫性のあるデータで100%正確に処理することは通常不可能です。Keycloakの場合、これは特に重大な問題ではありません。最悪の場合、ユーザーがクライアントに再度ログインするか、loginFailuresの不正カウント数をブルートフォース保護のために追跡する必要があります。スプリット・ブレインに対処するためのヒントについては、Infinispan/JGroupsドキュメントを参照してください。

ステート・トランスファーは、JMXを介してInfinispanサーバー側でも行われます。操作名は pushState です。状態をモニタリングしたり、プッシュ状態をキャンセルしたりするその他の操作はほとんどありません。ステート・トランスファーの詳細については、 Infinispan docs を参照してください。

3.4.12. キャッシュのクリア

スプリット・ブレイン後は、Keycloakの管理コンソールで手動でキャッシュをクリアするのが安全です。これは、 site1 のデータベースで変更されたデータがあり、イベントのために無効にする必要のあるキャッシュが、スプリット・ブレイン中に site2 へ転送されなかった可能性があるためです。したがって、 site2 のKeycloakノードでは、キャッシュ内に古いデータがまだ残っている可能性があります。

キャッシュをクリアするには、 サーバー・キャッシュのクリア を参照してください。

ネットワークが復旧したら、いずれかのサイトの1つのKeycloakノードでキャッシュをクリアするだけで十分です。キャッシュの無効化イベントは、それぞれのサイトの他のKeycloakノードすべてに送られます。ただし、すべてのキャッシュ(レルム、ユーザー、鍵)に対して実行する必要があります。詳しくは、サーバー・キャッシュのクリアを参照してください。

3.4.13. JDGキャッシュ設定のチューニング

このセクションでは、JDGキャッシュを設定するためのヒントとオプションについて説明します。

バックアップ失敗ポリシー

デフォルトでは、JDGの clustered.xml ファイル内にあるInfinispanキャッシュ設定のバックアップの設定 failure-policyFAIL に設定されています。必要に応じて WARN または IGNORE に変更できます。

FAILWARN の違いは、 FAIL が使用され、Infinispanサーバーが別のサイトにデータのバックアップを試みたときに、バックアップが失敗すると、その失敗が呼び出し側(Keycloakサーバー)に伝播されるという点です。2番目のサイトが一時的に到達不能になったり、同じエンティティーの更新を試みる同時トランザクションが発生した場合は、バックアップが失敗する可能性があります。この場合、Keycloakサーバーは複数回再試行します。ただし、その再試行が失敗した場合、より長いタイムアウト後、ユーザーにはエラーが表示されます。

WARN を使用すると、失敗したバックアップは、InfinispanサーバーからKeycloakサーバーに伝播されません。失敗したバックアップは無視され、ユーザーにはエラーが表示されません。バックアップのデフォルトのタイムアウトは10秒間であるため短いです。これは、 backup 要素の timeout 属性によって変更することができます。タイムアウトにおける再試行はありません。タイムアウト時は、InfinispanサーバーのログにWARNINGメッセージが表示されます。

潜在的な課題としては、いくつかのケースで、再試行( FAIL ポリシーの使用)が役に立つサイト間で短いネットワーク停止が発生する可能性があるため、 WARN (再試行なし)では、サイト間でデータの不整合が発生します。これは、両方のサイトで同時に同じエンティティーを更新しようとする際にも発生します。

これらの不整合はどれほど悪いことなのでしょうか。通常は、ユーザーが再認証する必要があることだけを意味します。

WARN ポリシーを使用すると、 actionTokens キャッシュにより提供され、その特定のキーを処理する、使い捨てのキャッシュが実際に1回使用されますが、同じキーが2回"正常に"書き込まれる可能性があります。しかし、たとえば、OAuth2 仕様では、コードは使い捨てでなければならないと言及されています。 WARN ポリシーでは、これは厳密には保証されておらず、両方のサイトで同時に書き込まれる試みがあった場合、同じコードが2回書き込まれる可能性があります。

より長いネットワーク停止またはスプリット・ブレインが起きた場合、 FAILWARN を使用すると、サイトをオフラインおよびオンラインにするで説明したとおり、少し時間を置いて失敗した後、他のサイトはオフラインになります。デフォルトの1分のタイムアウトでは、関連するキャッシュがすべてオフラインになるまで、通常は1~3分かかります。その後、エンドユーザーの観点から、動作はすべて問題なく進みます。サイトをオフラインおよびオンラインにするで説明したとおり、オンラインに戻ったときに手動でサイトを復元する必要があります。

要約すると、サイト間で頻繁により長い停止が発生する可能性があり、データの不整合と100%正確ではない使い捨てのキャッシュについては許容されますが、エラーや長いタイムアウトがエンドユーザーに表示されないようにする場合には、 WARN に切り替えます。

WARNIGNORE の違いは、 IGNORE では警告がJDGログに書き込まれていないことです。詳細については、Infinispanのドキュメントを参照してください。

ロックの取得タイムアウト

デフォルト設定では、NON_DURABLE_XAモードでトランザクションを取得タイムアウト0で使用しています。これは、同じキーに対して進行中の別のトランザクションがある場合、トランザクションはすぐに失敗することを意味します。

デフォルトの10秒ではなく0に切り替えるのは、デッドロックの可能性を避けるためです。Keycloakでは、同じエンティティー(通常はセッション・エンティティーまたはloginFailure)が両方のサイトから同時に更新されることがあります。これにより、状況によってはデッドロックが発生し、トランザクションが10秒間ブロックされる可能性があります。 詳細については、このJIRAレポートを参照してください。

タイムアウト0の場合、トランザクションは直ちに失敗し、値 FAIL のバックアップ failure-policy が設定されていれば、Keycloakから再試行されます。2番目の同時トランザクションが終了するまで、通常は再試行が成功し、エンティティーは両方の同時トランザクションから更新を適用します。

この設定での同時トランザクションは、非常に良い一貫性と結果が得られるため、そのまま使用することをお勧めします。

唯一の(機能しない)問題は、Infinispanサーバーログの例外です。これは、ロックがすぐに利用できなくなるたびに発生します。

3.4.14. SYNCまたはASYNCバックアップ

backup 要素の重要な部分は strategy 属性です。 SYNCASYNC のどちらが必要かを決める必要があります。クロスデータセンター・レプリケーションを認識できる7つのキャッシュがあり、これらは、クロスデータセンターに対する次の3つの異なるモードで設定できます。

  1. SYNCバックアップ

  2. ASYNCバックアップ

  3. バックアップを全くしない

SYNC バックアップが使用された場合、バックアップが同期され、バックアップが2番目のサイトで処理されると、呼び出し側(Keycloakサーバー)で操作が完了したとみなされます。これは ASYNC よりもパフォーマンスが劣りますが、一方で、 site2 のユーザー・セッションなど特定のエンティティーの後続の読み込みによって、 site1 からの更新が確実に確認されます。また、データの一貫性が必要な場合は、これは必須になります。 ASYNC の場合と同様に、他のサイトへのバックアップが失敗した場合は、呼び出し側には全く通知されません。

キャッシュによっては、バックアップをまったく取らず、Infinispanサーバーへのデータ書き込みを完全にスキップすることも可能です。これを設定するために、 Keycloak側( KEYCLOAK_HOME/standalone/configuration/standalone-ha.xml ファイル)の特定のキャッシュに remote-store 要素を使用しないでください。また、特定の replicated-cache 要素もInfinispanサーバー側では必要ありません。

デフォルトでは、7つのキャッシュすべてが最も安全なオプションである SYNC バックアップで設定されています。考慮すべき点は次のとおりです。

  • アクティブ/パッシブモード(すべてのKeycloakサーバーが単一のサイト site1 にあり、 site2 にあるInfinispanサーバーが純粋にバックアップとしてのみ使用されます。詳しくは モードを参照してください)を使用している場合、パフォーマンスを低下させないよう、すべてのキャッシュに ASYNC 方式を使用するのが通常は望ましいです。

  • work キャッシュは、主に、キャッシュ無効化イベントなどのいくつかのメッセージを他のサイトに送信するために使用されます。また、userStorageの同期化などの特別なイベントが単一のサイトでのみ発生するようにするためにも使用されます。これを SYNC に設定することを推奨します。

  • actionTokens キャッシュは、使い捨てのキャッシュとして使用され、トークンまたはチケットが1回だけ使用されたということを追跡します。たとえば、アクショントークンまたはOAuth2のコードです。これを ASYNC に設定して、パフォーマンスをわずかに向上させることは可能ですが、特定のチケットが実際に1回だけ使用され使い捨てになるかは保証されていません。たとえば、両方のサイトで同じチケットの同時リクエストがある場合、 ASYNC 方式によって両方のリクエストが成功する可能性があります。そのため、ここでの設定は、セキュリティー( SYNC 方式)を優先するか、パフォーマンス( ASYNC 方式)を優先するかによります。

  • loginFailures キャッシュは、3つのモードのいずれかで使用できます。バックアップがまったくない場合、ユーザーのログイン失敗のカウントがサイトごとに個別にカウントされることを意味します(詳しくはInfinispanキャッシュを参照してください)。これにはいくつかのセキュリティー上の意味がありますが、パフォーマンス上の利点があります。また、サービス拒否(DoS)攻撃のリスクを軽減します。たとえば、攻撃者が両方のサイトでユーザーのユーザー名とパスワードを使用して1000件の同時リクエストをシミュレートすると、サイト間で多くのメッセージが渡され、ネットワークの混雑が発生する可能性があります。 ASYNC 方式は、攻撃者のリクエストが他のサイトへのバックアップを待つことによってブロックされず、潜在的にさらに混雑したネットワーク・トラフィックを招くため、状況は悪化する可能性があります。ログイン失敗のカウントも ASYNC 方式では正確ではありません。

データセンター間のネットワークが遅く、DoSの確率が高い環境では、 loginFailures キャッシュをまったくバックアップしないことが推奨されます。

  • sessionsclientSessions キャッシュを SYNC で保持しておくことをお勧めします。ユーザーのリクエストとバックチャネル・リクエスト(リクエスト処理で説明したクライアント・アプリケーションからKeycloakへのリクエスト)が常に同じサイトで処理されることが確かな場合にのみ、それらを ASYNC へ切り替えることが可能になります。たとえば、次のような場合はこれが当てはまります。

    • モードで説明した通り、アクティブ/パッシブモードを使用しています。

    • クライアント・アプリケーションはすべてKeycloak JavaScriptアダプターを使用しています。JavaScriptアダプターは、ブラウザー内でバックチャネル・リクエストを送信するため、それらはブラウザーのスティッキー・セッションに参加し、このユーザーの別のブラウザー・リクエストと同じクラスターノード(したがって同じサイト)で終了します。

    • ロードバランサーは、クライアントIPアドレス(ロケーション)に基づいてリクエストを処理でき、クライアント・アプリケーションは両方のサイトにデプロイされています。

      たとえば、ロンドンとニューヨークの2つのサイトがあります。 アプリケーションがロンドンサイトとニューヨークサイトの両方に配備されている場合は、ロンドンユーザーからのすべてのユーザー・リクエストがロンドンサイトのアプリケーションとロンドンサイトのKeycloakサーバーにリダイレクトされるようにすることができます。 ロンドンサイトのクライアント・デプロイメントからのバックチャネル・リクエストは、ロンドンサイトのKeycloakサーバーでも終了します。 一方、米国のユーザーの場合、すべてのKeycloakリクエスト、アプリケーション・リクエスト、バックチャネル・リクエストはニューヨークサイトで処理されます。

  • offlineSessionsofflineClientSessions に対して、それは類似していますが、クライアント・アプリケーションのいずれに対してもオフライン・トークンを使用する予定ががない場合、それらをバックアップする必要はまったくありません。

一般的に、懸念があり、パフォーマンスがブロッカーではない場合は、キャッシュを SYNC 方式に保持する方が安全です。

SYNC/ASYNCバックアップへの切り替えに関しては、 backup 要素の strategy 属性を編集してください。たとえば、次のようになります。
<backup site="site2" failure-policy="FAIL" strategy="ASYNC" enabled="true">

cache-configuration要素の mode 属性に注意してください。

3.4.15. トラブルシューティング

以下のヒントは、トラブルシューティングが必要な場合に役立ちます。

  • 基本設定を行い、最初にこの機能を有効にして、どのように動作するかを理解することをお勧めします。物事を理解するためにこの文書全体を読むことも賢明です。

  • Infinispanサーバーの設定で説明されているように、jconsoleクラスターのステータス(GMS)とInfinispanのJGroupsのステータス(RELAY)をチェックインします。状況が期待どおりに見えない場合、問題はInfinispanサーバーの設定にある可能性があります。

  • Keycloakサーバーの場合、サーバーの起動時に次のようなメッセージが表示されます。

    18:09:30,156 INFO  [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (ServerService Thread Pool -- 54)
    Node name: node11, Site name: site1

    Keycloakサーバーの起動時に、サイト名とノード名が想定どおりに表示されていることを確認してください。

  • 同じデータセンターのKeycloakサーバーだけが互いにクラスター化されていることを含め、Keycloakサーバーが期待どおりにクラスター内にあることを確認してください。これは、GMSビューを介してJConsoleでチェックすることもできます。詳細については、クラスターのトラブルシューティングを参照してください。

  • Keycloakサーバーの起動時に次のような例外が発生した場合、

    17:33:58,605 ERROR [org.infinispan.client.hotrod.impl.operations.RetryOnFailureOperation] (ServerService Thread Pool -- 59) ISPN004007: Exception encountered. Retry 10 out of 10: org.infinispan.client.hotrod.exceptions.TransportException:: Could not fetch transport
    ...
    Caused by: org.infinispan.client.hotrod.exceptions.TransportException:: Could not connect to server: 127.0.0.1:12232
    	at org.infinispan.client.hotrod.impl.transport.tcp.TcpTransport.<init>(TcpTransport.java:82)

    これは通常、Keycloakサーバーが自身のデータセンター内のInfinispanサーバーにアクセスできないことを意味します。ファイアウォールが期待どおりに設定され、Infinispanサーバーが接続可能であることを確認してください。

  • Keycloakサーバーの起動時に次のような例外が発生した場合、

    16:44:18,321 WARN  [org.infinispan.client.hotrod.impl.protocol.Codec21] (ServerService Thread Pool -- 57) ISPN004005: Error received from the server: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
     ...

    サイトの該当するInfinispanサーバーのログをチェックし、他のサイトへのバックアップに失敗したかどうかを確認します。バックアップ・サイトが利用できない場合は、Infinispanサーバーがオフラインサイトにバックアップしようとしないようにオフラインに切り替えて、Keycloakサーバー側で正常に操作が成功するようにすることをお勧めします。詳細については、Cross DCデプロイメントの管理を参照してください。

  • JMXを介して利用可能なInfinispanの統計を確認してください。たとえば、ログインして、新しいセッションがInfinispanサーバーの両方に正常に書き込まれたかどうかを確認し、そこの sessions キャッシュで利用可能かどうかを確認します。これは、MBean jboss.datagrid-infinispan:type=Cache,name="sessions(repl_sync)",manager="clustered",component=StatisticsnumberOfEntries 属性の sessions キャッシュ内の要素の数をチェックすることによって間接的に行うことができます。ログイン後、両方のサイトのそれぞれのInfinispanサーバーに numberOfEntries の1つ以上のエントリーが存在するはずです。

  • Keycloakサーバーの設定の説明に従って、DEBUGロギングを有効にします。たとえば、ログインして2番目のサイトで新しいセッションが利用できないと思われる場合は、Keycloakサーバーログをチェックし、Keycloakサーバーの設定で説明したようにリスナーがトリガーされていることを確認してください。keycloak-userメーリングリストに質問したい場合は、電子メール内の両方のデータセンターのKeycloakサーバーからログファイルを送信すると便利です。ログスニペットをメールに追加するか、ログをどこかに置いて電子メールで参照してください。

  • site1 のKeycloakサーバーで user などのエンティティーを更新しても、 site2 のKeycloakサーバーで更新したエンティティーが表示されない場合は、同期したデータベース自体のレプリケーションか、Keycloakキャッシュが適切に無効化されなかったかのどちらかに問題があります。問題がデータベースのレプリケーション・レベルにある場合は、ここで説明するように一時的にKeycloakキャッシュを無効化してください。また、データベースに手動で接続し、データが期待どおりに更新されるかどうかを確認するのに役立ちます。これはすべてのデータベースに固有のものなので、データベースのドキュメントを参照する必要があります。

  • 場合によっては、Infinispanサーバーログに次のようなロックに関する例外が表示されることがあります。

    (HotRodServerHandler-6-35) ISPN000136: Error executing command ReplaceCommand,
    writing keys [[B0x033E243034396234..[39]]: org.infinispan.util.concurrent.TimeoutException: ISPN000299: Unable to acquire lock after
    0 milliseconds for key [B0x033E243034396234..[39] and requestor GlobalTx:jdg1:4353. Lock is held by GlobalTx:jdg1:4352

    これらの例外は必ずしも問題ではありません。両方のデータセンターで同じエンティティーの同時編集がトリガーされると、いつでも発生する可能性があります。これは、デプロイメントの多くの場合に当てはまります。たいてい、Keycloakサーバーは、失敗した操作について通知を受けて再試行するため、ユーザーの観点からは通常問題はありません。

  • Keycloakサーバーの起動時に次のような例外が発生した場合。

    16:44:18,321 WARN  [org.infinispan.client.hotrod.impl.protocol.Codec21] (ServerService Thread Pool -- 55) ISPN004005: Error received from the server: java.lang.SecurityException: ISPN000287: Unauthorized access: subject 'Subject with principal(s): []' lacks 'READ' permission
     ...

    これらのログエントリーはKeycloakの結果で、Infinispanで認証が必要かどうかを自動的に検出し、認証が必要であることを意味します。この時点で、サーバーが正常に起動され、これらを無視しても問題ないか、またはサーバーの起動に失敗していることに気づきます。サーバーの起動に失敗した場合は、Infinispanが Infinispanサーバーの設定 で説明されているように認証のために正しく設定されていることを確認してください。このログエントリーが含まれないように、 spi=connectionsInfinispan/provider=default の設定で remoteStoreSecurityEnabled プロパティーを true に設定して認証を強制できます。

    <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
        ...
        <spi name="connectionsInfinispan">
            ...
            <provider name="default" enabled="true">
                <properties>
                    ...
                    <property name="remoteStoreSecurityEnabled" value="true"/>
                </properties>
            </provider>
        </spi>
  • Keycloakでアプリケーションに認証しようとすると、ブラウザーの無限のリダイレクトで認証に失敗し、Keycloakサーバーログに次のようなエラーが表示されます。

    2017-11-27 14:50:31,587 WARN [org.keycloak.events] (default task-17)
    type=LOGIN_ERROR, realmId=master, clientId=null, userId=null,
    ipAddress=aa.bb.cc.dd, error=expired_code, restart_after_timeout=true

    おそらく、スティッキー・セッションをサポートするようにロードバランサーを設定する必要があることを意味します。Keycloakサーバー起動中に使用される指定されたルート名(プロパティー jboss.node.name )に、 ロードバランサー・サーバーが現在のサーバーを識別するために使用する正しい名前が含まれていることを確認してください。

  • Infinispanの work キャッシュが無期限に増加した場合、 このInfinispanの問題 が発生している可能性があります(キャッシュが正しく期限切れになっていないことに起因する問題)。その場合、キャッシュ宣言を次のような空の <expiration /> タグで更新してください。

        <replicated-cache name="work" configuration="sessions-cfg">
            <expiration />
        </replicated-cache>
  • Infinispanサーバーのログに次のような警告が表示された場合は、

    18:06:19,687 WARN  [org.infinispan.server.hotrod.Decoder2x] (HotRod-ServerWorker-7-12) ISPN006011: Operation 'PUT_IF_ABSENT' forced to
      return previous value should be used on transactional caches, otherwise data inconsistency issues could arise under failure situations
    18:06:19,700 WARN  [org.infinispan.server.hotrod.Decoder2x] (HotRod-ServerWorker-7-10) ISPN006010: Conditional operation 'REPLACE_IF_UNMODIFIED' should
      be used with transactional caches, otherwise data inconsistency issues could arise under failure situations

    それらを無視することができます。この警告を回避するために、Infinispanサーバー側のキャッシュをトランザクション・キャッシュに変更することができますが、バグ https://issues.jboss.org/browse/ISPN-9323 によって引き起こされるいくつかの他の問題が発生する可能性があるため、これはお勧めしません。したがって、今のところこの警告は無視する必要があります。

  • Infinispanサーバーのログに次のようなエラーが表示された場合は、

    12:08:32,921 ERROR [org.infinispan.server.hotrod.CacheDecodeContext] (HotRod-ServerWorker-7-11) ISPN005003: Exception reported: org.infinispan.server.hotrod.InvalidMagicIdException: Error reading magic byte or message id: 7
    	at org.infinispan.server.hotrod.HotRodDecoder.readHeader(HotRodDecoder.java:184)
    	at org.infinispan.server.hotrod.HotRodDecoder.decodeHeader(HotRodDecoder.java:133)
    	at org.infinispan.server.hotrod.HotRodDecoder.decode(HotRodDecoder.java:92)
    	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:411)
    	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248)

    また、Keycloakログに類似のエラーが表示された場合は、互換性のないバージョンのHotRodプロトコルが使用されていることを示しています。これは、KeycloakをJDG 7.2サーバーまたは古いバージョンのInfinispanサーバーで使用しようとした場合に発生する可能性があります。Keycloak設定ファイルの remote-store 要素に、追加のプロパティーとして protocolVersion プロパティーを追加すると役に立ちます。たとえば次のように設定します。

    <property name="protocolVersion">2.6</property>

4. サブシステム設定の管理

Keycloakの低レベルな設定は、配布物に含まれている standalone.xmlstandalone-ha.xml 、または domain.xml ファイルを編集して行います。このファイルの場所は動作モードによって異なります。

ここで設定可能な設定は無限にありますが、このセクションでは keycloak-server サブシステムの設定について説明します。どの設定ファイルを使用していても、 keycloak-server サブシステムの設定は同じです。

keycloak-serverサブシステムは通常、以下のようにファイルの末尾で宣言されています。

<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
   <web-context>auth</web-context>
   ...
</subsystem>

このサブシステム内での変更はサーバーが再起動されるまで有効になりませんので、注意してください。

4.1. SPIプロバイダーの設定

各設定の詳細については、その設定と関連する別の箇所で説明します。ただし、SPIプロバイダー設定の宣言に使用される形式について学ぶことをお勧めします。

Keycloakは、柔軟性に優れた、高度なモジュールシステムです。50以上のサービス・プロバイダー・インターフェイス(いわゆるSPI)があり、各SPIの実装を交換することができます。SPIの実装は provider によって行われます。

SPI宣言のすべての要素は任意で選択できるものですが、完全なSPI宣言は以下のようになります。

<spi name="myspi">
    <default-provider>myprovider</default-provider>
    <provider name="myprovider" enabled="true">
        <properties>
            <property name="foo" value="bar"/>
        </properties>
    </provider>
    <provider name="mysecondprovider" enabled="true">
        <properties>
            <property name="foo" value="foo"/>
        </properties>
    </provider>
</spi>

ここでは、 myspi というSPIに2つのプロバイダーが定義されています。 default-providermyprovider と表示されています。しかし、この設定をどのように処理するかはSPIが決定します。SPIによって複数のプロバイダーを許可するものもあれば、そうでないものもあります。そのため、 default-provider はSPIの選択の手助けになります。

また、各プロバイダーが独自の設定プロパティーを定義することについても注意してください。上記の両方のプロバイダーが foo というプロパティーを持っている点は単なる偶然です。

プロパティー値のタイプはそれぞれプロバイダーによって解釈されます。ただし、1つ例外があります。 eventsStore のSPIの jpa プロバイダーについて確認してみましょう。

<spi name="eventsStore">
    <provider name="jpa" enabled="true">
        <properties>
            <property name="exclude-events" value="[&quot;EVENT1&quot;,
                                                    &quot;EVENT2&quot;]"/>
        </properties>
    </provider>
</spi>

この値は角括弧で始まり、角括弧で終わることが分かります。これは、値がリストとしてプロバイダーに渡されることを意味します。このサンプルでは、2つの要素値 EVENT1EVENT2 のリストはシステムからプロバイダーに渡されます。リストに値を追加するには、各リスト要素をカンマで区切ります。残念ながら、各リスト要素を囲む引用符を &quot; にエスケープする必要があります。

4.2. WildFlyCLIの起動

設定変更は、手作業で編集するだけでなく、 jboss-cli ツールでコマンドを発行して行うこともできます。CLIを使用すると、サーバーをローカルでもリモートでも設定することができます。スクリプティングと組み合わせると特に便利です。

WildFlyCLIを起動するには、 jboss-cli を実行する必要があります。

Linux/Unix
$ .../bin/jboss-cli.sh
Windows
> ...\bin\jboss-cli.bat

これを実行すると、以下のようにプロンプトが表示されます。

Prompt
[disconnected /]

実行中のサーバーでコマンドを実行する場合、まず connect コマンドを実行します。

接続
[disconnected /] connect
connect
[standalone@localhost:9990 /]

ここでユーザー名もパスワードも入力していないと思うかもしれませんが、 jboss-cli を実行中のスタンドアローン・サーバーまたはドメイン・コントローラーと同じマシンで実行し、アカウントに適切なファイル・アクセス権限がある場合は、管理者のユーザー名とパスワードを設定または入力する必要はありません。この設定では心配で、よりセキュアにしたい場合の詳細は、WildFly 16 Documentationを参照してください。

4.3. CLI組み込みモード

サーバーがアクティブではない間にスタンドアローン・サーバーと同じマシン上にコマンドが発行された場合は、サーバーをCLIに組み込み、受信要求を許可しない特殊モードに変更することができます。これを行うには、まず、変更したい設定ファイルで embed-server コマンドを実行します。

embed-server
[disconnected /] embed-server --server-config=standalone.xml
[standalone@embedded /]

4.4. CLI GUIモード

CLIはGUIモードでも実行できます。GUIモードでは、実行中のサーバーの管理モデル全体をグラフィカルに表示および編集できるSwingアプリケーションが起動します。GUIモードは、CLIコマンドの書式を設定したり、オプションを調べる際のヘルプとして特に便利です。GUIは、ローカルまたはリモートサーバーからサーバーログを取得することもできます。

GUIモードの起動
$ .../bin/jboss-cli.sh --gui

Note: リモートサーバーに接続するには、 --connect オプションも渡します。詳しくは、 --helpオプションを参照してください。

GUIモードを起動したら、スクロールダウンすると subsystem=keycloak-server ノードが見つかります。ノードを右クリックして Explore subsystem=keycloak-server をクリックすると、keycloak-serverサブシステムのみを表示する新しいタブが表示されます。

cli gui

4.5. CLIスクリプト

CLIには、さまざまなスクリプト機能があります。スクリプトはCLIコマンドを含む単なるテキストファイルです。テーマとテンプレートのキャッシュをオフにする簡単なスクリプトを見ていきましょう。

turn-off-caching.cli
/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheThemes,value=false)
/subsystem=keycloak-server/theme=defaults/:write-attribute(name=cacheTemplates,value=false)

スクリプトを実行するには、CLI GUI の Scripts メニューに従う、または以下のようにコマンドラインからスクリプトを実行します。

$ cd bin
$ ./jboss-cli.sh --file=adapter-install-offline.cli

4.6. CLIレシピ

ここでは、設定タスクおよびそれらをCLIコマンドで実行する方法について説明します。代入すべきという意味で、またはkeycloak-serverサブシステムという意味でワイルドカード・パス ** を使用しています。その点は注意してください。

スタンドアローンの場合、これは単に以下の意味になります。

** = /subsystem=keycloak-server

ドメインモードの場合、これは以下のような意味になります。

** = /profile=auth-server-clustered/subsystem=keycloak-server

4.6.1. サーバーのwebコンテキストの変更

/subsystem=keycloak-server/:write-attribute(name=web-context,value=myContext)

4.6.2. グローバルなデフォルトテーマの設定

**/theme=defaults/:write-attribute(name=default,value=myTheme)

4.6.3. 新しいSPIとプロバイダーの追加

**/spi=mySPI/:add
**/spi=mySPI/provider=myProvider/:add(enabled=true)

4.6.4. プロバイダーの無効化

**/spi=mySPI/provider=myProvider/:write-attribute(name=enabled,value=false)

4.6.5. SPIのデフォルトプロバイダーの変更

**/spi=mySPI/:write-attribute(name=default-provider,value=myProvider)

4.6.6. dblock SPIの設定

**/spi=dblock/:add(default-provider=jpa)
**/spi=dblock/provider=jpa/:add(properties={lockWaitTimeout => "900"},enabled=true)

4.6.7. プロバイダーのシングル・プロパティー値の追加または変更

**/spi=dblock/provider=jpa/:map-put(name=properties,key=lockWaitTimeout,value=3)

4.6.8. プロバイダーからのシングル・プロパティーの削除

**/spi=dblock/provider=jpa/:map-remove(name=properties,key=lockRecheckTime)

4.6.9. List 型のプロバイダー・プロパティー値の設定

**/spi=eventsStore/provider=jpa/:map-put(name=properties,key=exclude-events,value=[EVENT1,EVENT2])

5. Profiles

Keycloakには、デフォルトでは有効とされていない機能があり、完全にはサポートされていないものがあります。さらに、デフォルトで有効とされている機能を無効にすることもできます。

有効および無効にできる機能は、以下のとおりです。

Name Description デフォルトで有効 サポートレベル

account2

新しいアカウント管理コンソール

No

Experimental

account_api

アカウント管理REST API

No

Preview

admin_fine_grained_authz

きめ細かい管理権限

No

Preview

authz_drools_policy

認可サービス用のDroolsポリシー

No

Preview

docker

Dockerレジストリーのプロトコル

No

Supported

impersonation

管理者がユーザーに成り代わる機能

Yes

Supported

openshift_integration

OpenShiftを保護するための拡張

No

Preview

script

JavaScriptを使用したカスタム認証の記述

Yes

Preview

token_exchange

Token Exchangサービス

No

Preview

すべてのプレビュー機能を有効にするには、次のようにサーバーを起動します。

bin/standalone.sh|bat -Dkeycloak.profile=preview

standalone/configuration/profile.properties (またはドメインモード内の server-one 用の domain/servers/server-one/configuration/profile.properties )ファイルを作成することにより、これを永続的に設定することができます。以下をファイルに追加します。

profile=preview

特定の機能を有効にするには、以下のようにサーバーを起動します。

bin/standalone.sh|bat -Dkeycloak.profile.feature.<feature name>=enabled

サンプルとして、Dockerを有効にするには -Dkeycloak.profile.feature.docker=enabled を使用します。

profile.properties ファイルに以下を追加することにより、永続的に設定することができます。

feature.docker=enabled

特定の機能を無効にするには、以下のようにサーバーを起動します。

bin/standalone.sh|bat -Dkeycloak.profile.feature.<feature name>=disabled

サンプルとして、代理ログイン機能を無効にするには -Dkeycloak.profile.feature.impersonation=disabled を使用します。

profile.properties ファイルに以下を追加することにより、永続的に設定することができます。

feature.impersonation=disabled

6. リレーショナル・データベースの設定

Keycloakには、H2というJavaベースのリレーショナル・データベースが組み込まれています。これは、Keycloakがデータを保存するために使用するデフォルトのデータベースで、単に認証サーバーをそのまま実行できるように組み込まれただけのものです。そのため、プロダクション用の外部データベースに置き換えることを強くお勧めします。H2データベースは並行性の高いシチュエーションではあまり実用的ではないので、クラスター内でも使用しないでください。この章では、Keycloakをより成熟度の高いデータベースに接続する方法を説明します。

Keycloakでは、リレーショナル・データを保存するために2つの階層化技術が使用されます。最下層の技術はJDBCです。JDBCは、RDBMSへの接続に使用されるJavaAPIです。データベース・ベンダーによって提供されるデータベース・タイプ毎に、さまざまなJDBCドライバーがあります。この章では、これらのベンダー固有のドライバーのいずれかを使用するようにKeycloakを設定する方法について説明します。

データを保存するための最上層の技術は、Hibernate JPAです。これは、Javaオブジェクトをリレーショナル・データにマップするORマッピングAPIの1つです。Keycloakのデプロイメントで、Hibernateの設定に触れる必要はほとんどありませんが、そのまれなケースに遭遇した場合どうなるかを説明します。

データソースの設定については、 WildFly 16 Documentation の中のデータソース設定の章the datasource configuration chapterで詳しく説明しています。

6.1. RDBMS設定のチェックリスト

Keycloak用にRDBMSを設定するための手順は、以下のとおりです。

  1. データベース用のJDBCドライバーの検索とダウンロード

  2. ドライバーのJARをモジュールにパッケージ化し、このモジュールをサーバーにインストール

  3. サーバーの設定プロファイルでJDBCドライバーを宣言

  4. データベースのJDBCドライバーを使用するようにデータソース設定を変更

  5. データベースへの接続パラメーターを定義するようにデータソース設定を変更

この章では、すべてのサンプルにおいてPostgreSQLを使用します。他のデータベースも同じ手順でインストールできます。

6.2. JDBCドライバーのパッケージ化

RDBMS用のJDBCドライバーのJARを検索してダウンロードします。このドライバーを使用する前に、モジュールにパッケージ化してサーバーにインストールする必要があります。モジュールによって、KeycloakのクラスパスにロードされるJAR、およびこれらのJARが他のモジュールに持つ依存関係が定義されます。設定は非常に簡単です。

Keycloakの配布物の …​/modules/ ディレクトリー内で、モジュール定義を保持するディレクトリー構造を作成する必要があります。規約では、ディレクトリー構造の名前にJDBCドライバーのJavaパッケージ名を使用します。PostgreSQLの場合、 org/postgresql/main ディレクトリーを作成します。このディレクトリーにデータベース・ドライバーのJARをコピーし、その中に空の module.xml ファイルも作成します。

モジュール・ディレクトリー

db module

これを行った後、 module.xml ファイルを開き、以下のXMLを作成します。

モジュールXML
<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.3" name="org.postgresql">

    <resources>
        <resource-root path="postgresql-9.4.1212.jar"/>
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>

モジュール名は、モジュールのディレクトリー構造と一致する必要があります。したがって、 org/postgresqlorg.postgresql にマッピングされます。ドライバーのJARファイル名は、 resource-rootpath の属性によって指定される必要があります。残りの部分は、JDBCドライバーのJARが持つ通常の依存関係になります。

6.3. JDBCドライバーの宣言とロード

次に、新しくパッケージ化されたJDBCドライバーをデプロイメント・プロファイルに宣言して、ロードし、サーバーの起動時に使用できるようにする必要があります。この操作を行う場所は、動作モードによって異なります。標準モードでデプロイしている場合は、 …​/standalone/configuration/standalone.xml を編集します。ドメインモードでデプロイしている場合は、 …​/domain/configuration/domain.xml を編集します。ドメインモードでは、 auth-server-standalone または auth-server-clustered のどちらか使用している方のプロファイルを必ず編集する必要があります。

プロファイル内で、 datasources サブシステム内の drivers のXMLブロックを検索すると、H2 JDBCドライバー用に宣言された定義済みのドライバーが見つかります。ここで、外部データベース用のJDBCドライバーを宣言します。

JDBCドライバー
  <subsystem xmlns="urn:jboss:domain:datasources:5.0">
     <datasources>
       ...
       <drivers>
          <driver name="h2" module="com.h2database.h2">
              <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
          </driver>
       </drivers>
     </datasources>
  </subsystem>

drivers のXMLブロック内で、追加のJDBCドライバーを宣言する必要があります。それに name を付けて、必要時に選択できるようにします。ドライバーのJAR用に以前作成した module パッケージを指す、 module 属性を指定します。最後に、ドライバーのJavaクラスを指定します。サンプルとして、この章で以前定義したモジュールのサンプル内にある、PostgreSQLドライバーをインストールすると下記のとおりになります。

JDBCドライバーの宣言
  <subsystem xmlns="urn:jboss:domain:datasources:5.0">
     <datasources>
       ...
       <drivers>
          <driver name="postgresql" module="org.postgresql">
              <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
          </driver>
          <driver name="h2" module="com.h2database.h2">
              <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
          </driver>
       </drivers>
     </datasources>
  </subsystem>

6.4. Keycloakデータソースの変更

JDBCドライバーを宣言したら、Keycloakが新しい外部データベースに接続するために使用する既存のデータソース設定を変更する必要があります。これは、JDBCドライバーを登録したのと同じ設定ファイルとXMLブロック内で行います。サンプルとして、新しいデータベースへの接続を設定すると以下のとおりになります。

JDBCドライバーの宣言
  <subsystem xmlns="urn:jboss:domain:datasources:5.0">
     <datasources>
       ...
       <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
           <connection-url>jdbc:postgresql://localhost/keycloak</connection-url>
           <driver>postgresql</driver>
           <pool>
               <max-pool-size>20</max-pool-size>
           </pool>
           <security>
               <user-name>William</user-name>
               <password>password</password>
           </security>
       </datasource>
        ...
     </datasources>
  </subsystem>

KeycloakDS 用の datasource 定義を検索します。まず connection-url を変更する必要があります。この接続URL値の形式は、ベンダーのJDBC実装のドキュメントで指定されています。

次に、使用する driver を定義します。これは、この章の前のセクションで宣言したJDBCドライバーの論理名になります。

トランザクションを実行するたびにデータベースへの新しい接続を開くのは処理コストがかかります。これを補うために、データソース実装は開いた接続のプールを維持します。 max-pool-size によって、プールする接続の最大数が指定されます。システムの負荷に応じて、この値を変更することができます。

最後に、少なくともPostgreSQLでは、データベースへの接続に必要なデータベースのユーザー名とパスワードを定義する必要があります。このサンプルでは、これが簡単なテキストであることを心配されるかもしれませんが、難読化する方法は複数あります。しかし、それはこのガイドの範囲外となります。

データソース機能について、詳しくは WildFly 16 Documentation 内のthe datasource configuration chapterを参照してください。

6.5. データベース設定

配布物内の standalone.xmlstandalone-ha.xmldomain.xml のいずれかの中にこのコンポーネント用の設定があります。このファイルの場所は動作モードによって異なります。

データベース設定
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
    ...
    <spi name="connectionsJpa">
     <provider name="default" enabled="true">
         <properties>
             <property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
             <property name="initializeEmpty" value="false"/>
             <property name="migrationStrategy" value="manual"/>
             <property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
         </properties>
     </provider>
    </spi>
    ...
</subsystem>

設定可能なオプションは、以下のとおりです。

dataSource

データソースのJNDI名

jta

データソースがJTA対応かどうか指定するbooleanプロパティー

driverDialect

データベース・ダイアレクトの値。ほとんどの場合、ダイアレクトはHibernateによって自動検出されるため、このプロパティーを指定する必要はありません。

initializeEmpty

空の場合はデータベースを初期化します。falseに設定する場合、データベースを手動で初期化する必要があります。データベースを手動で初期化するには、migrationStrategyを manual に設定し、データベース初期化用のSQLコマンドが書かれたファイルを作成します。デフォルトはtrueになっています。

migrationStrategy

データベースの移行に使用する戦略。有効な値は updatemanualvalidate です。updateは、自動的にデータベース・スキーマを移行します。manualは、データベースに対して手動実行可能な、必要な変更のSQLコマンドが書かれたファイルをエクスポートします。validateは、データベースが最新であるかどうかを単にチェックするものです。

migrationExport

手動によるデータベース初期化または移行用ファイルを書き込む場所のパス。

showSql

Hibernateがコンソール内にすべてのSQLコマンドを表示するかどうかを指定します(デフォルトはfalse)。これは非常に冗長な出力となります。

formatSql

HibernateがSQLコマンドをフォーマットするかどうかを指定します(デフォルトはtrue)。

globalStatsInterval

実行されたDBクエリーなどに関する、Hibernateのグローバル統計情報をログに出力します。統計情報は、秒単位で指定された間隔でサーバーログに常にレポートされ、各レポート後にクリアされます。

schema

使用するデータベース・スキーマを指定します。

これらの設定スイッチなどについては、WildFly 16 Development Guideで説明します。

6.6. データベースのユニコードに関する考慮事項

Keycloakのデータベース・スキーマは、以下の特別なフィールドにだけユニコード文字列の使用を許可します。

  • レルム:表示名、HTML表示名

  • フェデレーション・プロバイダー:表示名

  • ユーザー:ユーザー名、氏名、属性名、値

  • グループ:名前、属性名、値

  • ロール:名前

  • オブジェクトの説明

上記以外の場合は、8ビットのデータベース・エンコーディングに含まれる文字に制限されます。ただし、データベース・システムによっては、ユニコード文字のUTF-8エンコーディングを有効にし、すべてのテキストフィールド内で完全なユニコード文字セットを使用することができます。これは8ビットエンコーディングの場合よりも文字列の最大長を短くすることによって相殺されます。

データベースによっては、ユニコード文字を処理できるようにデータベースとJDBCドライバーの両方、またはいずれかに特別な設定をする必要があります。以下のデータベースの設定を確認してください。データベースがここにリストされている場合、データベースとJDBCドライバーの両方のレベルでUTF-8エンコーディングが適切に処理されていれば、正常に動きます。この点には注意してください。

技術的には、すべてのフィールド内のユニコード・サポートの重要な基準は、データベースによって VARCHAR フィールドと CHAR フィールドにユニコード文字セットが設定できるかどうかになります。設定できる場合は、通常はフィールド長を犠牲にすることで、ユニコードは妥当となります。 NVARCHAR フィールドと NCHAR フィールド内のユニコードしかサポートされていない場合は、Keycloakスキーマは VARCHAR フィールドと CHAR フィールドを広範囲にわたって使用するため、すべてのテキストフィールドのユニコード・サポートはほとんどありません。

6.6.1. Oracle Database

VARCHAR フィールドと CHAR フィールド内でユニコード・サポートを使用してデータベースを作成した場合は、ユニコード文字は適切に処理されます(例: AL32UTF8 文字セットがデータベース文字セットとして使用された場合)。JDBCドライバーに特別な設定は必要ありません。

データベース文字セットがユニコードではない場合、ユニコード文字を特別なフィールド内で使用するために、JDBCドライバーを設定して接続プロパティー oracle.jdbc.defaultNChartrue に設定する必要があります。厳密には必須ではないのですが、 oracle.jdbc.convertNcharLiterals 接続プロパティーも true にしておいた方が賢明です。これらのプロパティーは、システム・プロパティーまたは接続プロパティーとして設定することができます。しかし oracle.jdbc.defaultNChar の設定はパフォーマンスに悪影響を与える可能性がありますので、この点は注意してください。詳しくは、Oracle JDBCドライバーの設定ドキュメントを参照してください。

6.6.2. Microsoft SQL Server Database

ユニコード文字は、特別なフィールドに対してのみ適切に処理されます。JDBCドライバーやデータベースの特別な設定は必要ありません。

6.6.3. MySQL Database

CREATE DATABASE コマンドで、 VARCHARCHAR のフィールド内のユニコード・サポートを使用してデータベースが作成される場合は、ユニコード文字は適切に処理されます(例:MySQL 5.5でデフォルトのデータベース文字セットとして utf8 文字セットを使用する場合。 utf8 文字セットに求められるさまざまなストレージ要件により、 utf8mb4 文字セットは動作しませんので注意してください[1])。この場合、バイト数ではなく文字数に合わせて列が作成されるため、通常のフィールドには長さ制限は適用されないことに注意が必要です。データベースのデフォルト文字セットでユニコードの保存が許可されていない場合、特別なフィールドでのみユニコード値の保存が許可されることになります。

JDBCドライバー設定については、JDBC接続設定に接続プロパティー characterEncoding=UTF-8 を追加する必要があります。

6.6.4. PostgreSQL Database

データベース文字セットが UTF8 である場合、ユニコードはサポートされます。この場合、ユニコード文字はどのフィールドでも使用することができますが、通常フィールドではフィールド長は短縮されません。JDBCドライバーの特別な設定は必要ありません。

7. ネットワーク設定

Keycloakは、いくつかのネットワーク制限付きですぐに実行することができます。まず1つ目の制限は、すべてのネットワーク・エンドポイントが localhost にバインドされることにより、Keycloakサーバーは1台のローカルマシンでしか使用できないことです。2つ目の制限は、HTTPベースの接続には、80、443のようなデフォルトのポートが使用できないことです。HTTPS/SSLはそのまま使用できるように設定されておらず、その設定がされていないと、Keycloakはセキュリティー上非常に脆弱です。最後の制限は、Keycloakが外部サーバーに対するセキュアなSSLとHTTPS接続を必要とすることです。そのため、トラストストアを設定してエンドポイントを正しく検証する必要があります。この章では、これらの設定についてすべて説明します。

7.1. バインドアドレス

デフォルトでは、Keycloakはローカルホストのループバック・アドレス 127.0.0.1 にバインドされています。しかし、利用中のネットワーク上で認証サーバーを使用するには、これはあまり便利なデフォルトではありません。通常は、リバース・プロキシーまたはロードバランサーをパブリック・ネットワークにデプロイし、トラフィックをプライベート・ネットワーク上の個々のKeycloakサーバー・インスタンスにルーティングすることをお勧めします。どちらの場合でも、ネットワーク・インターフェイスを設定して localhost 以外のものにバインドする必要があります。

バインドアドレスの設定は簡単です。動作モードの選択の章で説明した standalone.sh または domain.sh 起動スクリプトを使用して、コマンドライン上で設定することができます。

$ standalone.sh -b 192.168.0.5

-b スイッチにより、パブリック・インターフェイス用のIPバインドアドレスは設定されます。

また、コマンドラインでバインドアドレスを設定したくない場合はその代わりの方法として、プロファイル設定を編集することもできます。動作モードに応じて standalone.xml または domain.xml になりますが、そのいずれかのプロファイル設定ファイルを開き、XMLブロックの interfaces を検索します。

    <interfaces>
        <interface name="management">
            <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
        </interface>
        <interface name="public">
            <inet-address value="${jboss.bind.address:127.0.0.1}"/>
        </interface>
    </interfaces>

public インターフェイスは、公開用ソケットを作成するサブシステムに対応しています。これらのサブシステムのサンプルとしては、Keycloakの認証エンドポイントを提供するwebレイヤーというものがあります。一方、 management インターフェイスは、WildFlyのmanagementレイヤーによって開かれたソケットに対応しています。具体的には、 jboss-cli.sh コマンドライン・インターフェイスとWildFlyのwebコンソールを使用できるソケットになります。

public インターフェイスを確認すると、特別な文字列 ${jboss.bind.address:127.0.0.1} が表示されています。この文字列は、 127.0.0.1 という値を示していますが、コマンドラインでJavaシステム・プロパティーを設定して上書きすることができます。

$ domain.sh -Djboss.bind.address=192.168.0.5

-b は、このコマンドの簡略表記です。したがって、このバインドアドレス値は、プロファイル設定で直接変更、または起動時にコマンドラインで変更することができます。

interface 定義を設定すると、さらに多くのオプションを使用できます。詳しくは、 WildFly 16 Documentation 内の network interface を参照してください。

7.2. ソケットポート・バインディング

各ソケット用に開けられたポートには、コマンドラインまたは設定内で上書きできるデフォルト値があらかじめ定義されています。この設定方法を学ぶために、スタンドアローン・モードで実行していると仮定し、 …​/standalone/configuration/standalone.xml を開いてみましょう。そして socket-binding-group を検索します。

    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
        <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
        <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
        <socket-binding name="http" port="${jboss.http.port:8080}"/>
        <socket-binding name="https" port="${jboss.https.port:8443}"/>
        <socket-binding name="txn-recovery-environment" port="4712"/>
        <socket-binding name="txn-status-manager" port="4713"/>
        <outbound-socket-binding name="mail-smtp">
            <remote-destination host="localhost" port="25"/>
        </outbound-socket-binding>
    </socket-binding-group>

socket-bindings によってソケット接続が定義され、サーバーで開かれます。これらのバインディングは、 interface のバインドアドレスとポート番号を指定します。最も重要な項目は、以下のとおりです。

http

KeycloakのHTTP接続に使用されるポートの定義

https

KeycloakのHTTPS接続に使用されるポートの定義

ajp

このソケット・バインディングは、AJPプロトコルに使用されるポートを定義します。このプロトコルは、Apache HTTPDサーバーをロードバランサーとして使用している場合、ApacheHTTPDサーバーによって mod-cluster と共に使用されます。

management-http

WildFly CLIとwebコンソールで使用されるHTTP接続を定義します。

ドメインモードで実行する場合、 domain.xml ファイルに複数の socket-binding-groups が定義されているサンプルと同様に、ソケット設定は少し難しくなります。 server-group の定義までスクロールダウンすると、各 server-group にどの socket-binding-group が使用されているのか確認することができます。

ドメイン・ソケット・バインディング
    <server-groups>
        <server-group name="load-balancer-group" profile="load-balancer">
            ...
            <socket-binding-group ref="load-balancer-sockets"/>
        </server-group>
        <server-group name="auth-server-group" profile="auth-server-clustered">
            ...
            <socket-binding-group ref="ha-sockets"/>
        </server-group>
    </server-groups>
socket-binding-group を定義すると、さらに多くのオプションを使用できるようになります。詳しくは、 WildFly 16 Documentation 内の socket binding group を参照してください。

7.3. HTTPS/SSLの設定

Keycloakは、デフォルトではSSL/HTTPSを処理するように設定されていません。Keycloakサーバー上、またはKeycloakサーバーのフロントにあるリバース・プロキシー上のいずれかでSSLを有効にすることを強くお勧めします。

このデフォルトの動作は、各KeycloakレルムのSSL/HTTPSモードによって定義されています。これについて詳しくはServer Administration Guideで説明しますが、これらのモードの関連事項と簡単な概要についてはここで示します。

external requests

SSLが無効でも、 localhost127.0.0.110.0.x.x192.168.x.x172.16.x.x のようなプライベートIPアドレスであれば、Keycloakをそのまま実行することは可能です。サーバーにSSL/HTTPSが設定されていない場合、またはプライベートIPアドレス以外からHTTP経由でKeycloakにアクセスする場合はエラーになります。

none

KeycloakはSSLを要求しません。この設定は、開発段階でいろいろと検証している時にのみ使用すべきです。

all requests

KeycloakはすべてのIPアドレスに対してSSLを要求します。

各レルム用のSSLモードは、Keycloak管理コンソール内で設定できます。

7.3.1. Keycloakサーバー用SSL/HTTPSの有効化

HTTPSトラフィックを処理するためにリバース・プロキシーまたはロードバランサーを使用していない場合、Keycloakサーバー用にHTTPSを有効にする必要があります。これには下記が含まれます。

  1. SSL/HTTPトラフィック用の秘密鍵と証明書を含むキーストアの取得または生成

  2. このキーペアと証明書を使用するためのKeycloakサーバー設定

証明書とJavaキーストアの作成

HTTPS接続が許可されるには、Keycloakサーバーがデプロイされているwebコンテナー内でHTTPSを有効にする前に、自己署名証明書または第三者署名証明書を取得してJavaキーストアにインポートする必要があります。

自己署名証明書

開発段階で、Keycloakの配備をテストするための第三者署名証明書を用意していない場合、Java JDKに付属する keytool ユーティリティーを使用して自己署名証明書を生成する必要があります。

$ keytool -genkey -alias localhost -keyalg RSA -keystore keycloak.jks -validity 10950
    Enter keystore password: secret
    Re-enter new password: secret
    What is your first and last name?
    [Unknown]:  localhost
    What is the name of your organizational unit?
    [Unknown]:  Keycloak
    What is the name of your organization?
    [Unknown]:  Red Hat
    What is the name of your City or Locality?
    [Unknown]:  Westford
    What is the name of your State or Province?
    [Unknown]:  MA
    What is the two-letter country code for this unit?
    [Unknown]:  US
    Is CN=localhost, OU=Keycloak, O=Test, L=Westford, ST=MA, C=US correct?
    [no]:  yes

What is your first and last name ? という質問にはサーバーをインストールしたマシンのDNS名を答えてください。 テスト目的なので、 localhost を使用します。このコマンドを実行すると、 keytool コマンドが実行されたのと同じディレクトリー内で keycloak.jks ファイルが生成されます。

第三者署名証明書が必要だが用意していない場合、 cacert.org から無料で取得することができます。しかしこの方法で取得する前に、少し設定が必要になります。

まず、証明書署名要求を生成します。

$ keytool -certreq -alias yourdomain -keystore keycloak.jks > keycloak.careq

yourdomain はこの証明書が生成されるDNS名になります。Keytoolにより以下のように生成されます。

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIC2jCCAcICAQAwZTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1BMREwDwYDVQQHEwhXZXN0Zm9y
ZDEQMA4GA1UEChMHUmVkIEhhdDEQMA4GA1UECxMHUmVkIEhhdDESMBAGA1UEAxMJbG9jYWxob3N0
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr7kck2TaavlEOGbcpi9c0rncY4HhdzmY
Ax2nZfq1eZEaIPqI5aTxwQZzzLDK9qbeAd8Ji79HzSqnRDxNYaZu7mAYhFKHgixsolE3o5Yfzbw1
29RvyeUVe+WZxv5oo9wolVVpdSINIMEL2LaFhtX/c1dqiqYVpfnvFshZQaIg2nL8juzZcBjj4as
H98gIS7khql/dkZKsw9NLvyxgJvp7PaXurX29fNf3ihG+oFrL22oFyV54BWWxXCKU/GPn61EGZGw
Ft2qSIGLdctpMD1aJR2bcnlhEjZKDksjQZoQ5YMXaAGkcYkG6QkgrocDE2YXDbi7GIdf9MegVJ35
2DQMpwIDAQABoDAwLgYJKoZIhvcNAQkOMSEwHzAdBgNVHQ4EFgQUQwlZJBA+fjiDdiVzaO9vrE/i
n2swDQYJKoZIhvcNAQELBQADggEBAC5FRvMkhal3q86tHPBYWBuTtmcSjs4qUm6V6f63frhveWHf
PzRrI1xH272XUIeBk0gtzWo0nNZnf0mMCtUBbHhhDcG82xolikfqibZijoQZCiGiedVjHJFtniDQ
9bMDUOXEMQ7gHZg5q6mJfNG9MbMpQaUVEEFvfGEQQxbiFK7hRWU8S23/d80e8nExgQxdJWJ6vd0X
MzzFK6j4Dj55bJVuM7GFmfdNC52pNOD5vYe47Aqh8oajHX9XTycVtPXl45rrWAH33ftbrS8SrZ2S
vqIFQeuLL3BaHwpl3t7j2lMWcK1p80laAxEASib/fAwrRHpLHBXRcq6uALUOZl4Alt8=
-----END NEW CERTIFICATE REQUEST-----

この証明書署名要求を認証局に送信します。認証局は署名された証明書を発行し、送り返します。ただし、新しい証明書をインポートする前に、まず認証局のルート証明書を取得してインポートする必要があります。認証局からルート証明書(例:root.crt)をダウンロードし、以下のとおりインポートします。

$ keytool -import -keystore keycloak.jks -file root.crt -alias root

最後に、以下のように新しい認証局の生成した証明書をキーストアにインポートします。

$ keytool -import -alias yourdomain -keystore keycloak.jks -file your-certificate.cer
キーストアを使用するためのKeycloak設定

適切な証明書を含むJavaキーストアができたため、それを使用するようにKeycloakのインストールを設定する必要があります。まず、キーストアを使用してHTTPSを有効にするために、 standalone.xmlstandalone-ha.xml 、または host.xml の各ファイルを編集する必要があります。その後、キーストア・ファイルをデプロイメントの configuration/ ディレクトリーに移動するか、または任意の場所に配置して絶対パスを指定します。絶対パスを使用している場合は、設定からオプションの relative-to パラメーターを削除してください(動作モードを参照)。

次のようにCLIを使用して、新しい security-realm 要素を追加します。

$ /core-service=management/security-realm=UndertowRealm:add()

$ /core-service=management/security-realm=UndertowRealm/server-identity=ssl:add(keystore-path=keycloak.jks, keystore-relative-to=jboss.server.config.dir, keystore-password=secret)

ドメインモードを使用している場合は、コマンドはすべてのホストで /host=<host_name>/ プレフィックスを使用して実行します(すべてのホストで security-realm を作成するために)。次のように、各ホスト分繰り返します。

$ /host=<host_name>/core-service=management/security-realm=UndertowRealm/server-identity=ssl:add(keystore-path=keycloak.jks, keystore-relative-to=jboss.server.config.dir, keystore-password=secret)

スタンドアローンまたはホスト設定ファイルでは、 security-realms 要素は次のようになります。

<security-realm name="UndertowRealm">
    <server-identities>
        <ssl>
            <keystore path="keycloak.jks" relative-to="jboss.server.config.dir" keystore-password="secret" />
        </ssl>
    </server-identities>
</security-realm>

次に、スタンドアローンまたは各ドメイン設定ファイルで、 security-realm のインスタンスを検索します。作成したレルムを使用するように、次のように https-listener を修正します。

$ /subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=security-realm, value=UndertowRealm)

ドメインモードを使用している場合は、使用されているプロファイルをコマンドの先頭に /profile=<profile_name>/ のように付けます。

結果として得られる要素 server name="default-server" は、これは subsystem xmlns="urn:jboss:domain:undertow:8.0" の子要素であり、次のような内容を含みます。

<subsystem xmlns="urn:jboss:domain:undertow:8.0">
   <buffer-cache name="default"/>
   <server name="default-server">
      <https-listener name="https" socket-binding="https" security-realm="UndertowRealm"/>
   ...
</subsystem>

7.4. 外部へのHTTPリクエスト

Keycloakサーバーは、ブラウザーを介さないHTTPリクエストを、セキュリティー保護されたアプリケーションやサービスに送信する必要が多々あります。Keycloakサーバーは、HTTPクライアント接続プールを維持することによって、これらの外部接続を管理します。 standalone.xmlstandalone-ha.xml または domain.xml 内で少し設定する必要があります。このファイルの場所は、動作モードによって異なります。

HTTPクライアント設定のサンプル
<spi name="connectionsHttpClient">
    <provider name="default" enabled="true">
        <properties>
            <property name="connection-pool-size" value="256"/>
        </properties>
    </provider>
</spi>

設定可能なオプションは、以下のとおりです。

establish-connection-timeout-millis

ソケット接続を確立する際のタイムアウトです。

socket-timeout-millis

外部へのリクエストがこの時間の間にデータを受信しない場合、接続をタイムアウトします。

connection-pool-size

プールされる接続数です(デフォルトでは128)。

max-pooled-per-route

ホストごとにプールされる接続数です(デフォルトでは64)。

connection-ttl-millis

ミリ秒単位での最大接続時間です。デフォルトでは設定されていません。

max-connection-idle-time-millis

接続プール内でアイドル状態を維持する最大時間です(デフォルトでは900秒)。Apache HTTPクライアントのバックグラウンド・クリーナー・スレッドを開始します。 -1 にセットすると、このチェックとバックグラウンド・スレッドは無効になります。

disable-cookies

デフォルトでは true です。trueを設定した場合、Cookieキャッシュは無効になります。

client-keystore

これはJavaキーストア・ファイルへのファイルパスです。このキーストアには、双方向SSL用のクライアント証明書を含めます。

client-keystore-password

クライアント・キーストア用のパスワードです。 client-keystore が設定されている場合、これは REQUIRED です。

client-key-password

Password for the client’s key. This is REQUIRED if client-keystore is set.

proxy-mappings

送信するHTTPリクエストのプロキシー設定を示します。詳細については、送信HTTPリクエストのプロキシー・マッピングのセクションを参照してください。

7.4.1. 送信HTTPリクエストのプロキシー・マッピング

Keycloakによって送信された送信HTTPリクエストは、プロキシー・マッピングのカンマで区切られたリストに基づいて、オプションでプロキシー・サーバーを使用できます。プロキシー・マッピングは、正規表現ベースのホスト名パターンと hostnamePattern;proxyUri 形式のproxy-uriの組み合わせを示します。例:

.*\.(google|googleapis)\.com;http://www-proxy.acme.com:8080

送信するHTTPリクエストのプロキシーを判別するために、ターゲットホスト名が、設定されたホスト名パターンと照合されます。最初に一致するパターンで、使用するproxy-uriが決定します。指定されたホスト名と一致する設定済みのパターンが無い場合、プロキシーは使用されません。

proxy-uriに対する特別な値 NO_PROXY は、関連するホスト名パターンと一致するホストに対して、プロキシーを使用しないことを示すために使用できます。プロキシー・マッピングの終わりにcatch-allパターンを指定して、送信するすべてのリクエストのデフォルト・プロキシーを定義することが可能です。

次の例は、プロキシー・マッピングの設定を示しています。

# All requests to Google APIs should use http://www-proxy.acme.com:8080 as proxy
.*\.(google|googleapis)\.com;http://www-proxy.acme.com:8080

# All requests to internal systems should use no proxy
.*\.acme\.com;NO_PROXY

# All other requests should use http://fallback:8080 as proxy
.*;http://fallback:8080

これは、以下の jboss-cli コマンドで設定できます。以下に示す正規表現パターンを適切にエスケープする必要があることに注意してください。

echo SETUP: Configure proxy routes for HttpClient SPI

# In case there is no connectionsHttpClient definition yet
/subsystem=keycloak-server/spi=connectionsHttpClient/provider=default:add(enabled=true)

# Configure the proxy-mappings
/subsystem=keycloak-server/spi=connectionsHttpClient/provider=default:write-attribute(name=properties.proxy-mappings,value=[".*\\.(google|googleapis)\\.com;http://www-proxy.acme.com:8080",".*\\.acme\\.com;NO_PROXY",".*;http://fallback:8080"])

jboss-cli コマンドの結果、以下のサブシステム設定になります。 "" 文字をエンコードする必要があることに注意してください。

<spi name="connectionsHttpClient">
    <provider name="default" enabled="true">
        <properties>
            <property
            name="proxy-mappings"
            value="[&quot;.*\\.(google|googleapis)\\.com;http://www-proxy.acme.com:8080&quot;,&quot;.*\\.acme\\.com;NO_PROXY&quot;,&quot;.*;http://fallback:8080&quot;]"/>
        </properties>
    </provider>
</spi>

7.4.2. 外部へのHTTPSリクエストのトラストストア

KeycloakがリモートのHTTPSエンドポイントを呼び出す場合、信頼できるサーバーへの接続かどうかを確認するために、リモートサーバーの証明書を検証する必要があります。これは、中間者攻撃を防ぐために必要です。リモートサーバーや署名した認証局の証明書は、トラストストアに保存されている必要があります。このトラストストアは、Keycloakサーバーによって管理されます。

トラストストアは、アイデンティティー・ブローカーやLDAPアイデンティティー・プロバイダーに安全に接続する場合、電子メールを送信する場合、およびクライアント・アプリケーションとのバックチャネル通信をする場合に使用されます。

デフォルトでは、トラストストア・プロバイダーは設定されておらず、https接続は Javaの JSSE Reference Guideで説明されている標準のJavaトラストストア設定にフォールバックします。信頼が確立されていない場合、これらの送信するHTTPSリクエストはエラーになります。

keytool を使用し、新しいトラストストア・ファイルを作成、または信頼できるホスト証明書を既存のトラストストア・ファイルに追加することができます。

$ keytool -import -alias HOSTDOMAIN -keystore truststore.jks -file host-certificate.cer

トラストストアは、配布物内の standalone.xmlstandalone-ha.xml または domain.xml で設定されます。このファイルの場所は、動作モードによって異なります。以下のテンプレートを使用して、トラストストア設定を追加することができます。

<spi name="truststore">
    <provider name="file" enabled="true">
        <properties>
            <property name="file" value="path to your .jks file containing public certificates"/>
            <property name="password" value="password"/>
            <property name="hostname-verification-policy" value="WILDCARD"/>
            <property name="disabled" value="false"/>
        </properties>
    </provider>
</spi>

設定可能なオプションは以下のとおりです。

file

Javaキーストア・ファイルへのパスです。HTTPSリクエストでは、通信を行うサーバーのホストを検証する必要があります。これはトラストストアの役割です。キーストアには、1つ以上の信頼できるホスト証明書または認証局が含まれています。このトラストストア・ファイルには、セキュリティー保護されたホストのパブリック証明書のみを含めるべきです。 disabled がtrueでない場合、これは REQUIRED です。

password

トラストストア用のパスワードです。 disabled がtrueでない場合、これは REQUIRED です。

hostname-verification-policy

デフォルトでは WILDCARD です。HTTPSリクエストを行うために、サーバー証明書のホスト名を検証します。 ANY はホスト名が検証されないことを意味します。 WILDCARD によって、サブドメイン名にワイルドカードが使用できるようになります。サンプルとしては、 *.foo.comです。 STRICT の場合、CNはホスト名と正確に一致する必要があります。

disabled

true(デフォルト値)の場合、トラストストアの設定は無視され、証明書チェックは前述のJSSE設定にフォールバックします。falseに設定されている場合は、トラストストア用に filepassword を設定する必要があります。

8. クラスタリング

このセクションでは、Keycloakをクラスター内で実行するように設定する方法について説明します。クラスターを設定する場合、多くの設定が必要になります。具体的には以下のとおりです。

動作モードの選択と共有データベースの設定については、このガイドの前半で説明しました。この章では、ロードバランサーの設定とプライベート・ネットワークの提供について説明します。また、クラスター内でホストを起動する場合の注意点についても説明します。

IPマルチキャストなしでもKeycloakをクラスター構成にすることは可能ですが、このトピックについてはこのガイドの範囲を超えています。詳しくは、 WildFly 16 DocumentationJGroupsを参照してください。

8.1. 推奨ネットワーク・アーキテクチャー

Keycloakをデプロイするための推奨ネットワーク・アーキテクチャーは、パブリックIPアドレスを持つHTTP/HTTPSロードバランサーを配置し、プライベート・ネットワーク上のKeycloakサーバーへのリクエストをルーティングさせます。これにより、クラスタリング接続はすべて分離され、サーバーを保護する優れた手段が提供されます。

デフォルトでは、許可されていないノードがクラスターに加わり、マルチキャスト・メッセージをブロードキャストするのを防ぐものは何もありません。このため、クラスター・ノードはプライベート・ネットワーク内に置かれ、ファイアウォールによって外部の攻撃から保護される必要があります。

8.2. クラスタリングのサンプル

Keycloakには、ドメインモードでそのまま利用できるクラスタリングのデモが付属しています。詳しくはクラスター構成ドメインのサンプルの章を参照してください。

8.3. ロードバランサーまたはプロキシーの設定

このセクションでは、クラスター構成のKeycloakの前にリバース・プロキシーまたはロードバランサーを配置するにあたって、設定が必要な項目について説明します。また、組み込みのロードバランサーの設定についても説明します。これはクラスター構成ドメインのサンプルでも確認できます。

8.3.1. クライアントIPアドレスの特定

Keycloakのいくつかの機能は、認証サーバーに接続するHTTPクライアントのリモート・アドレスがクライアント・マシンの実際のIPアドレスであることを前提としています。例は、以下のとおりです。

  • イベントログ - 間違ったソースIPアドレスでログインエラーが記録されます

  • SSLの要求 - SSLの要求がexternal(デフォルト)に設定されている場合、すべての外部リクエストに対してSSLを要求します

  • 認証フロー - IPアドレスを使用して、たとえば外部リクエストに対してのみOTPを表示するようなカスタム認証フロー

  • 動的クライアント登録

Keycloak認証サーバーの前にリバース・プロキシーまたはロードバランサーが置いてある場合、問題になる可能性があります。通常の設定では、パブリック・ネットワーク上にフロントエンドのプロキシーを置いています。このプロキシーはプライベート・ネットワーク内のバックエンドのKeycloakサーバー・インスタンスに負荷を分散してリクエストを転送するものです。実際のクライアントIPアドレスが転送され、Keycloakサーバー・インスタンスによって処理されるように、追加設定する必要があります。具体的には以下のとおりです。

  • X-Forwarded-ForX-Forwarded-Proto HTTPヘッダーを適切にセットするように、リバース・プロキシーまたはロードバランサーを設定します。

  • オリジナルの 'Host' HTTPヘッダーを保持するように、リバース・プロキシーまたはロードバランサーを設定します。

  • X-Forwarded-For ヘッダーからクライアントのIPアドレスを読み取るように、認証サーバーを設定します。

X-Forwarded-ForX-Forwarded-Proto HTTPヘッダーを生成するためにプロキシーを設定し、オリジナルの Host HTTPヘッダーを保持する方法については、このガイドの説明範囲を超えています。 X-Forwarded-For ヘッダーがプロキシーによって設定されているか、特に注意して確認してください。プロキシーが正しく設定されていない場合、不正な クライアントがこのヘッダーを自身で設定して、クライアントが実際とは異なるIPアドレスから接続しているとKeycloakに認識させることができてしまいます。IPアドレスのブラックリストまたはホワイトリストを作成している場合、この設定は非常に重要です。

プロキシーの他にも、いくつかKeycloak側で設定する必要があります。プロキシーがHTTPプロトコル経由でリクエストを転送している場合、クライアントのIPアドレスをネットワーク・パケットからではなく X-Forwarded-For ヘッダーから取得するように、Keycloakを設定する必要があります。これを行うには、プロファイル設定ファイル(動作モードに応じて standalone.xmlstandalone-ha.xml または domain.xml )を開き、 urn:jboss:domain:undertow:8.0 XMLブロックを検索します。

X-Forwarded-For HTTP設定
<subsystem xmlns="urn:jboss:domain:undertow:8.0">
   <buffer-cache name="default"/>
   <server name="default-server">
      <ajp-listener name="ajp" socket-binding="ajp"/>
      <http-listener name="default" socket-binding="http" redirect-socket="https"
          proxy-address-forwarding="true"/>
      ...
   </server>
   ...
</subsystem>

http-listener 要素に proxy-address-forwarding 属性を追加します。値を true に設定します。

プロキシーがリクエストを転送するためにHTTPの代わりにAJPプロトコルを使用している場合(サンプルとしては、Apache HTTPD + mod-clusterなど)、少し異なる設定が必要になります。 http-listener を変更する代わりに、AJPパケットからこの情報を取得するフィルターを追加する必要があります。

X-Forwarded-For AJP設定
<subsystem xmlns="urn:jboss:domain:undertow:8.0">
     <buffer-cache name="default"/>
     <server name="default-server">
         <ajp-listener name="ajp" socket-binding="ajp"/>
         <http-listener name="default" socket-binding="http" redirect-socket="https"/>
         <host name="default-host" alias="localhost">
             ...
             <filter-ref name="proxy-peer"/>
         </host>
     </server>
        ...
     <filters>
         ...
         <filter name="proxy-peer"
                 class-name="io.undertow.server.handlers.ProxyPeerAddressHandler"
                 module="io.undertow.core" />
     </filters>
 </subsystem>

8.3.2. リバース・プロキシーでのHTTPS/SSLの有効化

リバース・プロキシーがSSL用にポート8443を使用しない場合、HTTPSトラフィックのどのポートをリダイレクトするか設定する必要があります。

<subsystem xmlns="urn:jboss:domain:undertow:8.0">
    ...
    <http-listener name="default" socket-binding="http"
        proxy-address-forwarding="true" redirect-socket="proxy-https"/>
    ...
</subsystem>

http-listener 要素に redirect-socket 属性を追加します。値には proxy-https を設定しますが、これは定義が必要なソケット・バインディングを指しています。

次に、新しい socket-binding 要素を socket-binding-group 要素に追加します。以下のようになります。

<socket-binding-group name="standard-sockets" default-interface="public"
    port-offset="${jboss.socket.binding.port-offset:0}">
    ...
    <socket-binding name="proxy-https" port="443"/>
    ...
</socket-binding-group>

8.3.3. 設定の確認

リバース・プロキシー経由で /auth/realms/master/.well-known/openid-configuration のパスを開き、リバース・プロキシーまたはロードバランサーの設定を確認することができます。たとえば、リバース・プロキシーのアドレスが https://acme.com/ の場合、 https://acme.com/auth/realms/master/.well-known/openid-configuration のURLを開きます。そうすると、Keycloakのいくつかのエンドポイントを示すJSONドキュメントが表示されます。エンドポイントが、リバース・プロキシーまたはロードバランサーのアドレス(スキーム、ドメイン、ポート)で始まることを確認します。このようにして、Keycloakが正しいエンドポイントを使用しているか確認します。

また、KeycloakがリクエストのソースIPアドレスを正しく認識しているか確認する必要があります。無効なユーザー名とパスワードの両方またはいずれかで管理コンソールにログインし、確認してください。これを行うと、サーバーログに以下のような警告が表示されます。

08:14:21,287 WARN  XNIO-1 task-45 [org.keycloak.events] type=LOGIN_ERROR, realmId=master, clientId=security-admin-console, userId=8f20d7ba-4974-4811-a695-242c8fbd1bf8, ipAddress=X.X.X.X, error=invalid_user_credentials, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8080/auth/admin/master/console/?redirect_fragment=%2Frealms%2Fmaster%2Fevents-settings, code_id=a3d48b67-a439-4546-b992-e93311d6493e, username=admin

ipAddress の値はログインしようとしているマシンのIPアドレスで、リバース・プロキシーまたはロードバランサーのIPアドレスではないということを確認してください。

8.3.4. 組み込みロードバランサーの使用

このセクションでは、クラスター構成ドメインのサンプルで説明している組み込みのロードバランサーの設定方法について説明します。

クラスター構成ドメインのサンプルは1台のマシンで実行するためにのみ作られています。他のホストでスレーブを起動するには、次の操作が必要です。

  1. 新しいホスト・スレーブを指し示すように、 domain.xml ファイルを編集します。

  2. サーバー配布物をコピーします。 domain.xmlhost.xml または host-master.xml は必要ありません。 standalone/ ディレクトリーも必要ありません。

  3. 使用しているバインドアドレスを変更、またはコマンドラインでそのアドレスを上書きするように、 host-slave.xml ファイルを編集します。

ロードバランサーでの新しいホスト登録

まず、 domain.xml 内のロードバランサーを設定して、新しいホスト・スレーブを登録する方法について学んでいきましょう。このファイルを開き、 load-balancer プロファイル内のundertow設定に移動します。 reverse-proxy のXMLブロック内で、 remote-host3 と呼ばれる host の新しい定義を追加します。

domain.xmlのreverse-proxy設定
<subsystem xmlns="urn:jboss:domain:undertow:8.0">
  ...
  <handlers>
      <reverse-proxy name="lb-handler">
         <host name="host1" outbound-socket-binding="remote-host1" scheme="ajp" path="/" instance-id="myroute1"/>
         <host name="host2" outbound-socket-binding="remote-host2" scheme="ajp" path="/" instance-id="myroute2"/>
         <host name="remote-host3" outbound-socket-binding="remote-host3" scheme="ajp" path="/" instance-id="myroute3"/>
      </reverse-proxy>
  </handlers>
  ...
</subsystem>

output-socket-bindingdomain.xml 内で後ほど設定する socket-binding を指す論理名です。 instance-id 属性の値は、負荷を分散する時にスティッキー・セッションを有効にできるようにCookieで使用されるため、新しいホスト固有のものでなければなりません。

次に、 load-balancer-socketssocket-binding-group へとスクロールダウンし、 remote-host3 用の outbound-socket-binding を追加します。この新しいバインディングは、新しいホストのホスト名とポートを指し示す必要があります。

domain.xmlのoutbound-socket-binding
<socket-binding-group name="load-balancer-sockets" default-interface="public">
    ...
    <outbound-socket-binding name="remote-host1">
        <remote-destination host="localhost" port="8159"/>
    </outbound-socket-binding>
    <outbound-socket-binding name="remote-host2">
        <remote-destination host="localhost" port="8259"/>
    </outbound-socket-binding>
    <outbound-socket-binding name="remote-host3">
        <remote-destination host="192.168.0.5" port="8259"/>
    </outbound-socket-binding>
</socket-binding-group>
マスター・バインドアドレス

次に、マスターホスト用の publicmanagement バインドアドレスを変更する必要があります。バインドアドレス内で説明した domain.xml ファイルを編集するか、以下のとおりコマンドライン上でこれらのバインドアドレスを指定します。

$ domain.sh --host-config=host-master.xml -Djboss.bind.address=192.168.0.2 -Djboss.bind.address.management=192.168.0.2
ホスト・スレーブ・バインドアドレス

次は、 publicmanagement 、ドメイン・コントローラー・バインドアドレス( jboss.domain.master-address )を変更する必要があります 。 host-slave.xml ファイルを編集するか、以下のとおりコマンドラインでこれらを指定します。

$ domain.sh --host-config=host-slave.xml
     -Djboss.bind.address=192.168.0.5
      -Djboss.bind.address.management=192.168.0.5
       -Djboss.domain.master.address=192.168.0.2

jboss.bind.addressjboss.bind.addres.management の値はホストスレーブのIPアドレスに関係します。 jboss.domain.master.address の値は、マスターホストの管理アドレスであるドメイン・コントローラーのIPアドレスとする必要があります。

8.3.5. その他のロードバランサー設定

その他のソフトウェア・ベースのロードバランサーの使用方法については、 WildFly 16 Documentation 内のロードバランシングのセクションを参照してください。

8.4. スティッキー・セッション

典型的なクラスター構成は、ロードバランサー(リバース・プロキシー)とプライベート・ネットワーク上の2つ以上のKeycloakサーバーで構成されています。パフォーマンスのために、ロードバランサーが特定のブラウザー・セッションに関するリクエストをすべて同じKeycloakバックエンド・ノードに転送することは有用かもしれません。

なぜなら、Keycloakが現在の認証セッションとユーザー・セッションに関するデータを保存するために、Infinispanの分散キャッシュを使用しているためです。Infinispanの分散キャッシュは、デフォルトで所有者は1つと設定されています。つまり、特定のセッションは1つのクラスター・ノードに保存され、他のノードがこのセッションにアクセスする場合は、リモートで検索する必要があります。

たとえば、ID 123 を持つ認証セッションが node1 上のInfinispanキャッシュに保存されていて、 node2 はこのセッションを検索する必要がある場合は、特定のセッション・エンティティーを返すためにネットワーク経由で node1 へリクエストを送信する必要があります。

特定のセッション・エンティティーが常にローカルで使用できる場合は、スティッキー・セッションに助けを借りることができます。パブリック・フロントエンド・ロードバランサーと2つのバックエンドKeycloakノードを持つクラスター環境内のワークフローは、以下のようになります。

  • ユーザーはKeycloakのログイン画面を表示するため初回リクエストを送信します。

  • このリクエストは、フロントエンド・ロードバランサーによって処理され、このフロントエンド・ロードバランサーがランダムにノード(たとえばnode1)に転送します。厳密には、ノードはランダムである必要はなく、他のいくつかの基準(クライアントIPアドレスなど)によって選択することができます。それらはすべて、ロードバランサー(リバース・プロキシー)の実装と設定に依存します。

  • Keycloakは認証セッションを任意のID(たとえば123)で作成し、Infinispanキャッシュに保存します。

  • Infinispanの分散キャッシュは、セッションIDのハッシュに基づいてセッションの主な所有者を割り当てます。これに関する詳細は、Infinispan documentationを参照してください。Infinispanがこのセッションの所有者として node2 を割り当てたとしましょう。

  • Keycloakは <session-id>.<owner-node-id> のような形式のCookie AUTH_SESSION_ID を作成します。この例では 123.node2 となります。

  • レスポンスは、Keycloakのログイン画面とブラウザーの AUTH_SESSION_ID Cookieでユーザーに返されます。

以上の点から、ID 123 を持つ認証セッションの所有者はこのノードであり、それゆえにInfinispanはローカルでこのセッションを検索できるため、ロードバランサーが node2 に対して以降のリクエストすべてを転送するなら有益なのです。認証セッションは、認証されるとユーザー・セッションに変換され、同じID 123 を持っているため node2 に保存されます。

スティッキー・セッションはクラスターの設定のために必須ではありませんが、上記の理由からパフォーマンスをあげるために有効です。 AUTH_SESSION_ID Cookieで固定するようロードバランサーを設定する必要があります。どのように行うかはロードバランサーによります。

Keycloak側では、起動時にシステム・プロパティー jboss.node.name を使用し、経路名に対応する値を使用することをお勧めします。たとえば、 -Djboss.node.name=node1 は経路を識別するために node1 を使います。この経路はInfinispanのキャッシュで使用され、ノードが特定のキーの所有者である場合は、AUTH_SESSION_ID Cookieに関連付けられます。このシステム・プロパティーを使用した起動コマンドの例を以下に示します。

cd $RHSSO_NODE1
./standalone.sh -c standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=node1

通常、プロダクション環境では経路名はバックエンド・ホストと同じ名前を使うべきですが、必要ではありません。たとえば、プライベート・ネットワーク内のKeycloakサーバーのホスト名を隠す場合などに、別の経路名を使用できます。

8.4.1. 経路の追加を無効にする

いくつかのロードバランサーは、バックエンドのKeycloakノードに頼るのではなく、経路情報を自身で追加するように設定できます。ただし、前述のとおり、Keycloakによる経路の追加が推奨されます。これは、Keycloakが特定のセッションの所有者であり、必ずしもローカルノードではないノードにルーティングできるエンティティーを認識しているため、このようにパフォーマンスが向上するためです。

必要に応じて、Keycloakサブシステム設定の RHSSO_HOME/standalone/configuration/standalone-ha.xml ファイルに以下を追加することにより、AUTH_SESSION_ID Cookieへの経路情報の追加を無効にすることができます。

<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
  ...
    <spi name="stickySessionEncoder">
        <provider name="infinispan" enabled="true">
            <properties>
                <property name="shouldAttachRoute" value="false"/>
            </properties>
        </provider>
    </spi>

</subsystem>

8.5. マルチキャスト・ネットワークの設定

ほとんど設定せずにクラスタリングを利用するには、IPマルチキャストが必要となります。マルチキャストとはネットワーク・ブロードキャスト・プロトコルのことです。このプロトコルは、起動時にクラスターを検出して参加させるために使用されます。また、Keycloakが使用する分散キャッシュのレプリケーションおよび無効化のためのメッセージをブロードキャストするためにも使用されます。

Keycloakのクラスタリング・サブシステムは、JGroupsスタック上で実行されます。クラスタリングのためのバインドアドレスは、デフォルトのIPアドレスとして127.0.0.1で、プライベート・ネットワーク・インターフェイスにバインドされています。バインドアドレスの章で説明した standalone-ha.xml または domain.xml セクションを編集する必要があります。

プライベート・ネットワーク設定
    <interfaces>
        ...
        <interface name="private">
            <inet-address value="${jboss.bind.address.private:127.0.0.1}"/>
        </interface>
    </interfaces>
    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        ...
        <socket-binding name="jgroups-mping" interface="private" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
        <socket-binding name="jgroups-tcp" interface="private" port="7600"/>
        <socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
        <socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
        <socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
        <socket-binding name="modcluster" port="0" multicast-address="224.0.1.105" multicast-port="23364"/>
        ...
    </socket-binding-group>

設定したい項目は jboss.bind.address.privatejboss.default.multicast.address そしてクラスタリング・スタック上のサービスのポートになるでしょう。

IPマルチキャストなしでKeycloakをクラスタリングすることは可能ですが、このトピックはこのガイドの説明範囲を超えています。詳しくは、 WildFly 16 Documentation 内のJGroupsを参照してください。

8.6. クラスター通信のセキュリティー保護

クラスター・ノードがプライベート・ネットワークで隔離されている場合は、クラスターに参加したり、クラスター内の通信を表示したりするために、プライベート・ネットワークへのアクセスが必要です。また、クラスター通信の認証と暗号化を有効にすることもできます。プライベート・ネットワークが安全である限り、認証と暗号化を有効にする必要はありません。どちらの場合も、Keycloakはクラスター上で機密情報を送信しません。

クラスタリング通信の認証と暗号化を有効にする場合は、 JBoss EAP Configuration GuideSecuring a Clusterを参照してください。

8.7. シリアライズされたクラスターの起動

Keycloakクラスター・ノードは並行して起動することができます。Keycloakサーバー・インスタンスが起動すると、データベースの移行、インポート、初回の初期化を行います。DBロックは、クラスター・ノードが同時に起動した場合、起動アクションが競合するのを防ぐために使用されます。

デフォルトでは、このロックの最大タイムアウトは900秒です。ノードがこのタイムアウト時間を超えてロック状態のままだった場合、起動は失敗します。通常は、デフォルト値を増減する必要はありません。しかし、配布物内の standalone.xmlstandalone-ha.xml または domain.xml ファイルで、念のためこの増減を設定することができます。このファイルの場所は、動作モードに依存します。

<spi name="dblock">
    <provider name="jpa" enabled="true">
        <properties>
            <property name="lockWaitTimeout" value="900"/>
        </properties>
    </provider>
</spi>

8.8. クラスターの起動

クラスター内でのKeycloakの起動は動作モードによって異なります。

スタンドアローン・モード
$ bin/standalone.sh --server-config=standalone-ha.xml
ドメインモード
$ bin/domain.sh --host-config=host-master.xml
$ bin/domain.sh --host-config=host-slave.xml

追加のパラメーターまたはシステム・プロパティーを使用する必要があります。たとえば、バインディング・ホストの場合は -b パラメーターで、ルートの名前を指定する場合は jboss.node.name システム・プロパティーです(スティッキー・セッションのセクションで説明しています)。

8.9. トラブルシューティング

  • クラスターを実行すると、両方のクラスターノードのログに以下のようなメッセージが表示されます。

    INFO  [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-10,shared=udp)
    ISPN000094: Received new cluster view: [node1/keycloak|1] (2) [node1/keycloak, node2/keycloak]

    ノードが1つだけ表示されている場合、クラスターホストが同じクラスターに参加していない可能性があります。

    通常、プライベート・ネットワーク上にクラスターノードを置き、ファイアウォールは置かずに通信するのがベスト・プラクティスです。その代わり、ネットワークへのパブリック・アクセス・ポイントでは、ファイアウォールを有効にすることができます。何らかの理由によりクラスターノードでファイアウォールを有効にする場合、ポートをいくつか開く必要があります。デフォルト値は、マルチキャスト・アドレス230.0.0.4のUDPポート55200とマルチキャスト・ポート45688です。JGroupsスタックの診断機能などの追加機能を有効にするには、ポートをさらに開く必要がありますので、その点は注意してください。Keycloakは、InfinispanまたはJGroupsにクラスタリング作業のほとんどを委譲します。詳しくは、 WildFly 16 DocumentationJGroupsを参照してください。

  • フェイルオーバーのサポート(高可用性)、エビクション、有効期限およびキャッシュ・チューニングに関心がある場合は、サーバー・キャッシュ設定を参照してください。

9. サーバー・キャッシュ設定

Keycloakには、2つのタイプのキャッシュがあります。1つ目のキャッシュは、データベースの前に置かれていて、DBの負荷を減らし、データをメモリーに保持することで全体の応答時間を短縮します。レルム、クライアント、ロール、およびユーザー・メタデータは、このタイプのキャッシュに保持されます。このキャッシュはローカル・キャッシュです。クラスターにより多くのKeycloakサーバーがある場合でも、ローカル・キャッシュはレプリケーションを使用しません。その代わり、ローカル・キャッシュはローカルにのみコピーを保持し、エントリーが更新された場合は無効化メッセージが残りのクラスターに送られてエントリーは追い出されます。これとは別にレプリケーションされるキャッシュ work があります。これは、どのエントリーがローカル・キャッシュから取り除かれるかについて、クラスター全体に無効化メッセージを送信するタスクになります。これによって、ネットワーク・トラフィックが大幅に減少し、効率化され、機密なメタデータのネットワーク経由での送信が回避されます。

2つ目のキャッシュは、ユーザー・セッション、オフライン・トークン、ログイン・エラーの履歴保持の管理を担当し、サーバーがパスワード・フィッシングやその他の攻撃を検出できるようにします。これらのキャッシュ内のデータは一時的で、メモリーにのみ保持されるものですが、クラスター全体にレプリケーションされる可能性があります。

この章では、クラスター構成と非クラスター構成の両方のためのキャッシュ用設定オプションについて説明します。

これらのキャッシュのより高度な設定については、 WildFly 16 DocumentationInfinispanのセクションを参照してください。

9.1. 退避と有効期限

Keycloakには、さまざまなキャッシュの設定があります。その一つにレルムキャッシュというものがあり、セキュリティー保護されたアプリケーション、一般的なセキュリティー・データおよび設定オプションに関する情報を保持しています。また、ユーザー・メタデータを含むユーザー・キャッシュがあります。このキャッシュのデフォルト値は最大10000エントリーで、最近最も使われなかったものを退避する戦略が使用されます。また、これらのキャッシュはそれぞれ、クラスター構成内での退避を制御するオブジェクト・リビジョン・キャッシュにも関連付けられています。このキャッシュは暗黙的に作成され、設定されたサイズの2倍のサイズがあります。認可データを保持する authorization キャッシュについても同様です。 keys キャッシュは外部キーに関するデータを保持し、専用のリビジョン・キャッシュを保持する必要はありません。それはむしろ明示的に expiration が宣言されているので、キーは定期的に期限切れとなり、外部のクライアントやアイデンティティー・プロバイダーから定期的にダウンロードされます。

これらのキャッシュの退避ポリシーと最大エントリーは、動作モードに応じて standalone.xmlstandalone-ha.xml または domain.xml 内で設定することができます。設定ファイルには、infinispanサブシステムの次のような部分があります。

<subsystem xmlns="urn:jboss:domain:infinispan:8.0">
    <cache-container name="keycloak">
        <local-cache name="realms">
            <object-memory size="10000"/>
        </local-cache>
        <local-cache name="users">
            <object-memory size="10000"/>
        </local-cache>
        ...
        <local-cache name="keys">
            <object-memory size="1000"/>
            <expiration max-idle="3600000"/>
        </local-cache>
        ...
    </cache-container>

許可されたエントリーの数を制限したり拡張したりするには、特定のキャッシュ設定の object 要素または expiration 要素を追加または編集するだけでできます。

さらに、 sessionsclientSessionsofflineSessionsofflineClientSessionsloginFailuresactionTokens といったキャッシュもあります。これらのキャッシュはクラスター環境で分散され、デフォルトではサイズが制限されていません。制限がある場合、一部のセッションが失われる可能性があります。制限なしにこれらのキャッシュサイズが増大することを避けるために、期限切れのセッションはKeycloak自身によって内部的にクリアされます。多数のセッションが原因でメモリーの問題が発生した場合は、次の操作を試すことができます。

  • クラスターのサイズを大きくします(クラスター内のノードが多いほど、セッションはノード間でより均等に分散されます)

  • Keycloakサーバープロセスのメモリーを増やします

  • キャッシュが1つの場所に保存されるように、所有者の数を減らします。詳細はレプリケーションとフェイルオーバーを参照してください

  • 分散キャッシュのl1-lifespanを無効にします。詳細については、Infinispanのマニュアルを参照してください。

  • セッション・タイムアウトを減らします。これはKeycloak管理コンソールの各レルムで個別に実行できます。しかし、これはエンドユーザーのユーザビリティーに影響する可能性があります。詳細については、 Timeouts を参照してください。

クラスターノード間でメッセージを送信するために主に使用される追加のレプリケーション・キャッシュ work があります。デフォルトでは無制限です。ただし、このキャッシュ内のエントリーは非常に短期間であるため、このキャッシュがメモリーの問題を引き起こすことはありません。

9.2. レプリケーションとフェイルオーバー

sessionsauthenticationSessionsofflineSessionsloginFailures などのキャッシュがあります(詳細は退避と有効期限を参照)。クラスター化された設定を使用するときは分散キャッシュとして設定されます。エントリーはすべてのノードにひとつひとつレプリケーションされるわけではありませんが、1つ以上のノードがそのデータの所有者として選ばれます。ノードが特定のキャッシュ・エントリーの所有者ではない場合は、そのキャッシュ・エントリーを取得するためにクラスターに問い合わせをします。これがフェイルオーバーに対して何を意味するかというと、データを保持しているノードがすべてダウンした場合は、そのデータは永遠に失われてしまうということです。デフォルトでは、Keycloakが指定するデータの所有者は1つだけです。そのため、その1つのノードがダウンした場合は、そのデータは失われることになります。このことは通常、ユーザーはログアウトされ、再度ログインし直さなければならないということを意味します。

distributed-cache の宣言で owners 属性を変更すると、データをレプリケートするノードの数を変更することができます。

owners
<subsystem xmlns="urn:jboss:domain:infinispan:8.0">
   <cache-container name="keycloak">
       <distributed-cache name="sessions" owners="2"/>
...

ここで上記のとおりに変更すると、少なくとも2つのノードが1つの特定のユーザー・ログイン・セッションをレプリケーションします。

推奨される所有者数は、構成によって異なります。ノードがダウンした時にユーザーがログアウトされてもされなくても良い場合は、所有者は1つで十分であり、レプリケーションを避けることができます。
スティッキー・セッションでロードバランサーを使用するように環境を設定することは、一般的には賢明です。通常は、特定のリクエストが処理されるKeycloakサーバーが分散キャッシュデータの所有者であるため、ローカルでデータをルックアップできますので、パフォーマンスを向上させるために有益です。詳細はスティッキー・セッションを参照してください。

9.3. キャッシングの無効化

レルムまたはユーザー・キャッシュを無効にするには、配布物内の standalone.xmlstandalone-ha.xml または domain.xml を編集する必要があります。このファイルの場所は、動作モードによって異なります。初期状態の設定は以下のようになっています。

    <spi name="userCache">
        <provider name="default" enabled="true"/>
    </spi>

    <spi name="realmCache">
        <provider name="default" enabled="true"/>
    </spi>

キャッシュを無効にするには、無効にするキャッシュの enabled 属性をfalseに設定します。この変更を有効にするには、サーバーを再起動する必要があります。

9.4. 実行時のキャッシュクリア

レルムキャッシュまたはユーザー・キャッシュをクリアするには、Keycloakの管理コンソールのRealm Settings→Cache設定ページへ移動します。このページでは、レルムキャッシュ、ユーザー・キャッシュ、外部の公開鍵キャッシュをクリアすることができます。

すべてのレルムのキャッシュがクリアされます。

1. https://issues.jboss.org/browse/KEYCLOAK-3873として追跡