Keycloak のログを収集する
このドキュメントでは、Webhook を使用してログを Google Security Operations に push するように Keycloak を構成する方法について説明します。
Keycloak は、シングル サインオン(SSO)、ユーザー フェデレーション、ID ブローカー、ソーシャル ログイン機能を提供するオープンソースの ID とアクセス管理(IAM)ソリューションです。OpenID Connect、OAuth 2.0、SAML 2.0 プロトコルをサポートし、セキュリティ監査のためにユーザー イベント(ログイン、ログアウト、登録、パスワードの変更)と管理者イベント(ユーザー、クライアント、レルム、ロールの管理オペレーション)を追跡します。
始める前に
次の前提条件を満たしていることを確認します。
- Google SecOps インスタンス
- 実行中の Keycloak インスタンス(バージョン 20 以降を推奨)
- Keycloak 管理コンソールへの管理者アクセス権
- 拡張機能をデプロイするための Keycloak サーバーのファイル システムまたはコンテナへのアクセス権
- Google Cloud コンソールへのアクセス(API キーの作成用)
Google SecOps で Webhook フィードを作成する
フィードを作成する
- [SIEM 設定] > [フィード] に移動します。
- [Add New Feed] をクリックします。
- 次のページで、[単一のフィードを設定] をクリックします。
- [フィード名] フィールドに、フィードの名前を入力します(例:
Keycloak Events)。 - [Source type] として [Webhook] を選択します。
- [ログタイプ] として [Keycloak] を選択します。
- [次へ] をクリックします。
- 次の入力パラメータの値を指定します。
- Split delimiter(省略可): 複数行のイベントを分割するには、
\nを入力します(各 Webhook POST には単一のイベントが含まれているため、このフィールドは空のままにできます)。 - アセットの名前空間: アセットの名前空間
- Ingestion labels: このフィードのイベントに適用されるラベル
- Split delimiter(省略可): 複数行のイベントを分割するには、
- [次へ] をクリックします。
- [Finalize] 画面で新しいフィードの設定を確認し、[送信] をクリックします。
秘密鍵を生成して保存する
フィードを作成したら、認証用のシークレット キーを生成する必要があります。
- フィードの詳細ページで、[シークレット キーを生成] をクリックします。
- ダイアログに秘密鍵が表示されます。
- 秘密鍵をコピーして安全に保存します。
重要: 秘密鍵は一度しか表示されず、後で取得することはできません。秘密鍵を紛失した場合は、新しい秘密鍵を生成する必要があります。
フィード エンドポイントの URL を取得する
- フィードの [詳細] タブに移動します。
- [エンドポイント情報] セクションで、[フィード エンドポイント URL] をコピーします。
URL の形式は次のとおりです。
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateまたは
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateこの URL は次の手順で使用します。
[完了] をクリックします。
Google Cloud API キーを作成する
Chronicle では、認証に API キーが必要です。Google Cloud コンソールで制限付き API キーを作成します。
API キーを作成する
- Google Cloud コンソールの [認証情報] ページに移動します。
- プロジェクト(Chronicle インスタンスに関連付けられているプロジェクト)を選択します。
- [認証情報を作成] > [API キー] をクリックします。
- API キーが作成され、ダイアログに表示されます。
- [API キーを編集] をクリックして、キーを制限します。
API キーを制限する
- [API キー] 設定ページで、次の操作を行います。
- 名前: わかりやすい名前を入力します(例:
Chronicle Webhook API Key)。
- 名前: わかりやすい名前を入力します(例:
- [API の制限] で次の操作を行います。
- [キーを制限] を選択します。
- [API を選択] プルダウンで、[Google SecOps API](または [Chronicle API])を検索して選択します。
- [保存] をクリックします。
- ページ上部の [API キー] フィールドから API キーの値をコピーします。
- API キーを安全に保存します。
Keycloak でイベント ストレージを有効にする
Webhook 拡張機能を構成する前に、Keycloak でイベント ストレージを有効にして、イベントが生成され、転送に使用できるようにします。
ユーザー イベントを有効にする
- Keycloak 管理コンソールにログインします。
- 左上のレルム プルダウンから、モニタリングするレルムを選択します。
- [Realm Settings] > [Events] に移動します。
- [ユーザー イベントの設定] サブタブを選択します。
- [予定を保存する] 切り替えを有効にします。
- [有効期限] を設定します(推奨される最小値: 7 日)。
- [保存] をクリックします。
管理者イベントを有効にする
- 同じ [イベント] タブで、[管理イベントの設定] サブタブを選択します。
- [予定を保存する] 切り替えを有効にします。
- [表現を含める] 切り替えを有効にして、変更されたオブジェクトの詳細をすべてキャプチャします。
- [有効期限] を設定します(推奨される最小値: 7 日)。
- [保存] をクリックします。
Webhook イベント リスナー拡張機能をインストールする
Keycloak にはネイティブの Webhook イベント リスナーは含まれていません。フェーズ 2(p2-inc)の keycloak-events 拡張機能をインストールして、Webhook 配信を有効にします。
拡張機能をダウンロードしてデプロイする
Maven Central の keycloak-events リリース ページから最新リリース JAR をダウンロードするか、ソースからビルドします。
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean install生成された fat JAR ファイルを Keycloak の
providersディレクトリにコピーします。cp target/keycloak-events-*.jar /opt/keycloak/providers/Keycloak を再ビルドして再起動します。
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
Webhook イベント リスナーを有効にする
- Keycloak 管理コンソールにログインします。
- レルムのプルダウンからターゲット レルムを選択します。
- [Realm Settings] > [Events] に移動します。
- [イベント リスナー] プルダウンで、[ext-event-webhook] を選択します。
- [保存] をクリックします。
Keycloak Webhook を構成する
Webhook URL を作成する
Chronicle エンドポイント URL と API キーを組み合わせます。
<ENDPOINT_URL>?key=<API_KEY>例:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Keycloak REST API を介して Webhook サブスクリプションを作成する
keycloak-events 拡張機能は、Webhook サブスクリプションを管理するための REST エンドポイントを提供します。Keycloak Admin REST API を使用して Webhook を作成します。
ステップ 1: アクセス トークンを取得する
管理者アカウントを使用して Keycloak からアクセス トークンをリクエストします。
TOKEN=$(curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/master/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=password" \ --data-urlencode "client_id=admin-cli" \ --data-urlencode "username=<ADMIN_USERNAME>" \ --data-urlencode "password=<ADMIN_PASSWORD>" \ | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
次のように置き換えます。
<KEYCLOAK_HOST>: Keycloak サーバーのホスト名とポート(例:keycloak.example.com:8443)<ADMIN_USERNAME>: Keycloak 管理者のユーザー名<ADMIN_PASSWORD>: Keycloak 管理者のパスワード
ステップ 2: Webhook を作成する
POST リクエストを送信して、ターゲット レルムの Webhook サブスクリプションを作成します。
curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "enabled": "true", "url": "<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>", "secret": "<WEBHOOK_HMAC_SECRET>", "eventTypes": ["*"] }'
次のように置き換えます。
<KEYCLOAK_HOST>: Keycloak サーバーのホスト名<REALM_NAME>: モニタリングするレルムの名前(master、my-realmなど)<ENDPOINT_URL>: 前にコピーした Chronicle フィード エンドポイント URL<API_KEY>: 先ほど作成した Google Cloud API キー<SECRET_KEY>: 先ほど生成した Chronicle Webhook のシークレット キー<WEBHOOK_HMAC_SECRET>: Webhook ペイロードの HMAC 署名用の任意のシークレット文字列(例:mySecretKey123)
ステップ 3: Webhook を検証する
レルムのすべての Webhook を一覧表示して、Webhook が作成されたことを確認します。
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
レスポンスは、Webhook オブジェクトのリストを返します。Webhook が "enabled": "true" と正しい URL で表示されることを確認します。
Webhook イベントタイプ
eventTypes フィールドには、送信するイベントをフィルタする式の配列を指定します。
*- すべてのイベントを送信します(SIEM 統合に推奨)。access.*- すべてのアクセス イベントを送信するadmin.*- すべての管理者イベントを送信するadmin.USER-*- ユーザーに関連するすべての管理イベントを送信するadmin-USER-CREATE- ユーザー作成の管理者イベントのみを送信する
Webhook ペイロード形式
Webhook は、JSON ペイロードを含む HTTP POST リクエストとしてイベントを送信します。ユーザー イベントのペイロードの例:
{ "id": "987865-1a2b-3c4d-9876-654321abc", "time": 1767799710612, "type": "LOGIN", "realmId": "12345abcde-1a2b-4d3c-9876-abcd456", "clientId": "account-console", "userId": "abcd456-1234-5678-abc9-987gfed654", "sessionId": "efghij-9876-abcd-456-11223344", "ipAddress": "203.0.113.45", "details": { "auth_method": "openid-connect", "auth_type": "code", "redirect_uri": "https://app.example.com/callback", "consent": "no_consent_required", "username": "jdoe" } }
Webhook の再試行動作
拡張機能は、2xx 以外のレスポンスを受信した場合、再試行に自動指数バックオフを使用します。
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
| backoffInitialInterval | 500 ミリ秒 | 最初の再試行間隔 |
| backoffMaxElapsedTime | 900000 ミリ秒(15 分) | 再試行の合計時間の最大値 |
| backoffMaxInterval | 180000 ミリ秒(3 分) | 再試行間の最大間隔 |
| backoffMultiplier | 5 | 再試行間隔の乗数 |
| backoffRandomizationFactor | 0.5 | ジッターのランダム化係数 |
認証方法のリファレンス
Chronicle ウェブフック フィードは、複数の認証方法をサポートしています。ベンダーがサポートしている方法を選択します。
方法 1: カスタム ヘッダー(推奨)
ベンダーがカスタム HTTP ヘッダーをサポートしている場合は、この方法を使用するとセキュリティが強化されます。
リクエストの形式:
POST <ENDPOINT_URL> HTTP/1.1 Content-Type: application/json x-goog-chronicle-auth: <API_KEY> x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
メリット:
- API キーとシークレットが URL に表示されない
- より安全(ヘッダーがウェブサーバーのアクセスログに記録されない)
- ベンダーがサポートしている場合の推奨方法
方法 2: クエリ パラメータ
ベンダーがカスタム ヘッダーをサポートしていない場合は、認証情報を URL に追加します。
URL 形式:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>例:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...リクエストの形式:
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
デメリット:
- URL に認証情報が表示される
- ウェブサーバーのアクセスログに記録される場合があります
- ヘッダーよりも安全性が低い
方法 3: ハイブリッド(URL + ヘッダー)
一部の構成では、URL に API キー、ヘッダーにシークレット キーを使用します。
リクエストの形式:
POST <ENDPOINT_URL>?key=<API_KEY> HTTP/1.1 Content-Type: application/json x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
認証ヘッダー名
Chronicle は、認証に次のヘッダー名を使用します。
API キーの場合:
x-goog-chronicle-auth(推奨)X-Goog-Chronicle-Auth(大文字と小文字を区別しない)
秘密鍵の場合:
x-chronicle-auth(推奨)X-Chronicle-Auth(大文字と小文字を区別しない)
Webhook の上限とベスト プラクティス
リクエストに関する上限
| 上限 | 値 |
|---|---|
| 最大リクエスト サイズ | 4 MB |
| 最大 QPS(秒間クエリ数) | 15,000 |
| リクエストのタイムアウト | 30 秒 |
| 再試行の動作 | 指数バックオフによる自動再試行 |
UDM マッピング テーブル
| ログフィールド | UDM マッピング | ロジック |
|---|---|---|
| payload.client_id | additional.fields | payload.client_id、payload.realm_id から作成されたフィールドと統合 |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | ISO8601 と yyyy-MM-dd'T'HH:mm:ss.SSSZ のパターンで日付フィルタを使用して解析されます |
| payload.ip_address | metadata.event_type | payload.ip_address が空でない場合は「STATUS_UPDATE」に設定します。uuid が空でない場合は「USER_UNCATEGORIZED」に設定します。それ以外の場合は「GENERIC_EVENT」に設定します。 |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | 値を直接コピーする |
| payload.session_id | network.session_id | 値を直接コピーする |
| payload.ip_address | principal.ip | 値を直接コピーする |
| source_metadata.schema | principal.resource.attribute.labels | source_metadata.schema、source_metadata.table、source_metadata.is_deleted(文字列に変換)、source_metadata.change_type、source_metadata.tx_id、source_metadata.lsn から作成されたラベルと統合されます。 |
| source_metadata.table | principal.resource.attribute.labels | |
| source_metadata.is_deleted | principal.resource.attribute.labels | |
| source_metadata.change_type | principal.resource.attribute.labels | |
| source_metadata.tx_id | principal.resource.attribute.labels | |
| source_metadata.lsn | principal.resource.attribute.labels | |
| uuid | principal.user.userid | 値を直接コピーする |
| オブジェクト | security_result.detection_fields | object、read_method、payload.id から作成されたラベルと統合されました |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | 値を直接コピーする |
| ユーザー名 | target.user.userid | 値を直接コピーする |
| metadata.product_name | metadata.product_name | 「KEYCLOAK」に設定 |
| metadata.vendor_name | metadata.vendor_name | 「KEYCLOAK」に設定 |
username" from "details_json |
target.user.userid |
変更履歴からマッピング |
redirect_uri" from "details_json |
target.url |
変更履歴からマッピング |
realm_id" and "client_id |
additional.fields |
変更履歴からマッピング |
変更ログ
さらにサポートが必要な場合 コミュニティ メンバーや Google SecOps のプロフェッショナルから回答を得ることができます。