Chuyển đến nội dung chính

レッスン 18: イベント システムと監査ログ

イベントタイプ (ログインイベント、管理イベント)、イベントロギングの有効化、イベントリスナーの設定 (jboss-logging、電子メール)、イベントストア、イベント詳細、イベントフィルタリング、管理コンソールおよび REST API を介したイベントのクエリ、カスタムイベントリスナー SPI、一元化されたロギングのための ELK スタック / Grafana Loki の統合、SIEM 統合、監査コンプライアンス (SOC2、HIPAA)、アラート自動化。

🔒 DevSecOps — レッスン 18 レッスン 18: イベント システムと監査ログ

基本から上級までの Keycloak

パート 5: テーマ、イベント、セキュリティ、および Vault

xdev.asia

1. イベントシステムの概要

Keycloakがシステムを提供イベントシステムシステム内のすべてのアクティビティを監視するための包括的な機能。ログイン、登録、管理設定の変更に至るすべてのアクションがイベントとして記録されます。

1.1 2 種類のイベント

タイプ説明する例えば
ログインイベント(ユーザーイベント)ユーザー関連のアクションログイン、登録、ログアウト、TOKEN_EXCHANGE
管理者イベント管理コンソール/API を介した構成変更ユーザーの作成、レルムの更新、クライアントの削除

1.2 ログインイベントの種類

イベントタイプ説明するそれはいつ起こりますか?
ログインログインに成功しましたユーザーが正しい認証情報を入力する
ログインエラーログインに失敗しましたユーザー名/パスワードが間違っています
登録する新しいアカウントを登録するユーザーがアカウントを正常に作成しました
登録エラー登録に失敗しましたメールが重複しています。検証に失敗しました
ログアウトサインアウトユーザーのログアウトまたはセッションの期限切れ
CODE_TO_TOKEN認可コード→トークンの交換OIDC 認可コードのフロー
CODE_TO_TOKEN_ERRORトークン交換に失敗しました無効なコード、期限切れのコード
REFRESH_TOKENアクセストークンを更新するクライアントはリフレッシュトークンを使用します
REFRESH_TOKEN_ERRORリフレッシュトークンが失敗しましたトークンが取り消されたか期限切れになった
CLIENT_LOGINクライアント認証サービスアカウントのログイン
INTROSPECT_TOKENトークンの導入リソースサーバー検証トークン
UPDATE_PASSWORDパスワードを変更するユーザーがパスワードを変更する
RESET_PASSWORDパスワードをリセットする電子メールリンク経由でユーザーがリセット
VERIFY_EMAILメール認証ユーザーが確認リンクをクリックする
SEND_RESET_PASSWORDパスワードリセットメールを送信するパスワードを忘れた場合のリクエスト
UPDATE_PROFILEプロフィールを更新するユーザーが個人情報を更新する
REMOVE_TOTPTOTPデバイスの削除ユーザーが OTP 認証システムを削除する
UPDATE_TOTPTOTP 構成ユーザーが OTP を設定する
GRANT_CONSENTユーザーが同意するOAuth2同意画面
TOKEN_EXCHANGEトークン交換クライアント間でトークンを交換する

1.3 管理イベントの操作

手術説明するリソースの種類
作成する新しいリソースを作成するユーザー、クライアント、レルム、グループ、ロール...
アップデートリソースを更新するユーザー、クライアント、REALM_SETTINGS...
消去リソースの削除ユーザー、クライアント、セッション...
アクション行動を起こすRESET_PASSWORD、SEND_VERIFY_EMAIL...

2. イベントログをオンにする

2.1 管理コンソール経由

  1. ログイン管理コンソール
  2. レルムを選択 →レルム設定→タブイベント
  3. 構成ユーザーイベント設定:
    • イベントの保存: の上
    • 有効期限: 30 日間 (コンプライアンス要件に応じて)
    • 保存されたタイプ: 保存するイベントの種類を選択します (デフォルト: ALL)
  4. 構成管理者イベントの設定:
    • イベントの保存: の上
    • 表現を含める: ON (リクエスト/レスポンスボディを保存)
  5. イベントリスナー: 必要なリスナーを追加します

2.2 REST API経由

# Bật event logging cho realm
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "eventsEnabled": true,
    "eventsExpiration": 2592000,
    "eventsListeners": ["jboss-logging"],
    "enabledEventTypes": [
        "LOGIN", "LOGIN_ERROR",
        "REGISTER", "REGISTER_ERROR",
        "LOGOUT",
        "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR",
        "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR",
        "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR",
        "UPDATE_PASSWORD",
        "RESET_PASSWORD",
        "SEND_RESET_PASSWORD"
    ],
    "adminEventsEnabled": true,
    "adminEventsDetailsEnabled": true
  }'

3. イベントリスナー

イベント リスナーは、イベントが発生したときに処理します。 Keycloak には次のリスナーが利用可能です。

3.1 jboss-logging リスナー

イベントをKeycloakサーバーログに記録します(デフォルトで有効):

# Log output mẫu
2026-03-15 10:30:45,123 INFO  [org.keycloak.events] (executor-thread-1)
  type=LOGIN, realmId=my-realm, clientId=my-app, userId=abc-123,
  ipAddress=192.168.1.100, auth_method=openid-connect,
  auth_type=code, redirect_uri=https://myapp.com/callback,
  [email protected]

3.2 電子メールリスナー

重要なイベントがあるときにユーザーに電子メールを送信します (例: 新しいデバイスからのログイン):

# Bật email listener
Realm Settings → Events → Event listeners → Thêm "email"

# Events được gửi email por defecto:
- LOGIN_ERROR (quá nhiều lần → cảnh báo compromised account)
- UPDATE_PASSWORD
- REMOVE_TOTP
- UPDATE_TOTP

4. イベント詳細とイベントストア

4.1 ログインイベントの構造

{
    "time": 1710489045000,
    "type": "LOGIN",
    "realmId": "my-realm",
    "clientId": "my-web-app",
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "sessionId": "abc-session-id",
    "ipAddress": "192.168.1.100",
    "details": {
        "auth_method": "openid-connect",
        "auth_type": "code",
        "redirect_uri": "https://myapp.com/callback",
        "consent": "no_consent_required",
        "code_id": "xyz-code-id",
        "username": "[email protected]",
        "identity_provider": null
    }
}

4.2 管理イベントの構造

{
    "time": 1710489100000,
    "realmId": "my-realm",
    "authDetails": {
        "realmId": "master",
        "clientId": "security-admin-console",
        "userId": "admin-user-id",
        "ipAddress": "10.0.0.1"
    },
    "operationType": "CREATE",
    "resourceType": "USER",
    "resourcePath": "users/new-user-id",
    "representation": "{\"username\":\"newuser\",\"email\":\"[email protected]\",\"enabled\":true}"
}

4.3 イベントストア — データベース

イベントはKeycloakのデータベースに保存されます。

テーブルコンテンツ
EVENT_ENTITYログインイベント
ADMIN_EVENT_ENTITY管理者イベント

注記:デフォルトのイベントストアはKeycloak DBに保存されます。イベントの数が多い場合は、カスタム イベント リスナーを使用してイベントを外部システムに送信し、組み込みストアの有効期限を短く設定する必要があります。

5. イベントのフィルタリングとクエリ

5.1 管理コンソール経由

  1. 入力イベント→タブユーザーイベントまたは管理者イベント
  2. イベントを次の条件でフィルタリングします。
    • イベントの種類: ログイン、ログインエラー、登録...
    • クライアント: 特定のクライアントを選択します
    • ユーザー:ユーザーIDで検索
    • 日付範囲: 開始日/終了日
    • IPアドレス: IPでフィルタリング

5.2 REST API経由 — ログインイベント

# Lấy tất cả login events
curl -s "http://localhost:8080/admin/realms/my-realm/events" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter theo event type
curl -s "http://localhost:8080/admin/realms/my-realm/events?type=LOGIN_ERROR" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter theo user
curl -s "http://localhost:8080/admin/realms/my-realm/events?user=user-uuid" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter theo client và date range
curl -s "http://localhost:8080/admin/realms/my-realm/events?\
client=my-app&\
dateFrom=2026-03-01&\
dateTo=2026-03-31&\
first=0&\
max=100" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter nhiều event types
curl -s "http://localhost:8080/admin/realms/my-realm/events?\
type=LOGIN&type=LOGIN_ERROR&type=REGISTER" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

5.3 REST API 経由 — 管理イベント

# Lấy admin events
curl -s "http://localhost:8080/admin/realms/my-realm/admin-events" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter theo operation type
curl -s "http://localhost:8080/admin/realms/my-realm/admin-events?\
operationTypes=CREATE&\
resourceTypes=USER" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Filter theo resource path
curl -s "http://localhost:8080/admin/realms/my-realm/admin-events?\
resourcePath=users" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

6. カスタム イベント リスナー SPI

Keycloak を使用すると、カスタム イベント リスナーを作成できます。サービスプロバイダーインターフェイス (SPI).

6.1 Maven プロジェクトの作成

<!-- pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>custom-event-listener</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <keycloak.version>26.1.0</keycloak.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-services</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

6.2 EventListenerProviderの実装

// src/main/java/com/example/CustomEventListenerProvider.java
package com.example;

import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.admin.OperationType;
import org.keycloak.models.KeycloakSession;
import org.jboss.logging.Logger;

import java.util.Map;

public class CustomEventListenerProvider implements EventListenerProvider {

    private static final Logger log = Logger.getLogger(CustomEventListenerProvider.class);
    private final KeycloakSession session;

    public CustomEventListenerProvider(KeycloakSession session) {
        this.session = session;
    }

    @Override
    public void onEvent(Event event) {
        // Xử lý Login Events
        log.infof("Event: type=%s, realmId=%s, clientId=%s, userId=%s, ip=%s",
                event.getType(),
                event.getRealmId(),
                event.getClientId(),
                event.getUserId(),
                event.getIpAddress());

        // Xử lý theo event type
        switch (event.getType()) {
            case LOGIN:
                handleLogin(event);
                break;
            case LOGIN_ERROR:
                handleLoginError(event);
                break;
            case REGISTER:
                handleRegister(event);
                break;
            default:
                break;
        }
    }

    @Override
    public void onEvent(AdminEvent event, boolean includeRepresentation) {
        // Xử lý Admin Events
        log.infof("AdminEvent: operation=%s, resourceType=%s, resourcePath=%s, realmId=%s",
                event.getOperationType(),
                event.getResourceType(),
                event.getResourcePath(),
                event.getRealmId());

        if (event.getOperationType() == OperationType.DELETE) {
            handleAdminDelete(event);
        }
    }

    private void handleLogin(Event event) {
        // Ví dụ: Gửi event đến Kafka
        String payload = buildEventPayload(event);
        // kafkaProducer.send("keycloak-login-events", payload);
        log.debugf("Login event sent to message broker: %s", payload);
    }

    private void handleLoginError(Event event) {
        Map<String, String> details = event.getDetails();
        String username = details != null ? details.get("username") : "unknown";
        String error = event.getError();

        log.warnf("Login failure: user=%s, error=%s, ip=%s",
                username, error, event.getIpAddress());

        // Ví dụ: Increment metric counter cho monitoring
        // metricsService.incrementCounter("login_failures",
        //     "realm", event.getRealmId(),
        //     "error", error);
    }

    private void handleRegister(Event event) {
        log.infof("New user registered: userId=%s, realm=%s",
                event.getUserId(), event.getRealmId());
    }

    private void handleAdminDelete(AdminEvent event) {
        log.warnf("Admin DELETE operation: resource=%s/%s by admin=%s",
                event.getResourceType(),
                event.getResourcePath(),
                event.getAuthDetails().getUserId());
    }

    private String buildEventPayload(Event event) {
        // Tạo JSON payload cho message broker
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"type\":\"").append(event.getType()).append("\",");
        sb.append("\"realmId\":\"").append(event.getRealmId()).append("\",");
        sb.append("\"userId\":\"").append(event.getUserId()).append("\",");
        sb.append("\"clientId\":\"").append(event.getClientId()).append("\",");
        sb.append("\"ipAddress\":\"").append(event.getIpAddress()).append("\",");
        sb.append("\"time\":").append(event.getTime());
        sb.append("}");
        return sb.toString();
    }

    @Override
    public void close() {
        // Cleanup resources
    }
}

6.3 EventListenerProviderFactoryの実装

// src/main/java/com/example/CustomEventListenerProviderFactory.java
package com.example;

import org.keycloak.Config;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;

public class CustomEventListenerProviderFactory implements EventListenerProviderFactory {

    public static final String PROVIDER_ID = "custom-event-listener";

    @Override
    public EventListenerProvider create(KeycloakSession session) {
        return new CustomEventListenerProvider(session);
    }

    @Override
    public void init(Config.Scope config) {
        // Đọc cấu hình từ keycloak config
        // Ví dụ: String kafkaBrokers = config.get("kafka-brokers", "localhost:9092");
    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {
        // Post-initialization
    }

    @Override
    public void close() {
        // Cleanup
    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }
}

6.4 SPIの登録

# src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
com.example.CustomEventListenerProviderFactory

6.5 導入とアクティベーション

# Build
mvn clean package

# Deploy
cp target/custom-event-listener-1.0.0.jar $KEYCLOAK_HOME/providers/
$KEYCLOAK_HOME/bin/kc.sh build

# Kích hoạt: Admin Console → Realm Settings → Events → Event listeners
# Thêm "custom-event-listener"

7. KeycloakのJSONロギング

集中ログと統合するには、JSON ログを出力するように Keycloak を構成します。

# Bật JSON logging
bin/kc.sh start \
  --log=console \
  --log-console-output=json

# Hoặc qua environment variables
KC_LOG=console
KC_LOG_CONSOLE_OUTPUT=json

7.1 JSON ログ出力のサンプル

{
    "timestamp": "2026-03-15T10:30:45.123Z",
    "level": "INFO",
    "loggerClassName": "org.keycloak.events",
    "loggerName": "org.keycloak.events",
    "message": "type=LOGIN, realmId=my-realm, clientId=my-app, userId=abc-123, ipAddress=192.168.1.100",
    "threadName": "executor-thread-1",
    "threadId": 42,
    "hostName": "keycloak-0",
    "processName": "keycloak",
    "processId": 1
}

7.2 ログレベルの構成

# Cấu hình log levels cho events
bin/kc.sh start \
  --log=console \
  --log-console-output=json \
  --log-level=INFO \
  --log-level=org.keycloak.events:DEBUG

# Environment variables
KC_LOG_LEVEL=INFO
KC_LOG_LEVEL=org.keycloak.events:DEBUG

8.ELKスタックの統合

一元的に分析するために、Keycloak ログを ELK スタック (Elasticsearch、Logstash、Kibana) に送信します。

8.1 一般的なアーキテクチャ

Keycloak (JSON logs)
    ↓
Filebeat (log shipper)
    ↓
Logstash (processing & enrichment)
    ↓
Elasticsearch (storage & indexing)
    ↓
Kibana (visualization & dashboards)

8.2 Filebeat の設定

# filebeat.yml
filebeat.inputs:
  - type: container
    paths:
      - /var/log/containers/keycloak-*.log
    processors:
      - decode_json_fields:
          fields: ["message"]
          target: "keycloak"
          overwrite_keys: true
      - add_fields:
          target: ""
          fields:
            service.name: keycloak
            environment: production

output.logstash:
  hosts: ["logstash:5044"]

8.3 Logstash パイプライン

# logstash/pipeline/keycloak.conf
input {
  beats {
    port => 5044
  }
}

filter {
  if [service][name] == "keycloak" {
    # Parse Keycloak event message
    if [keycloak][message] =~ "^type=" {
      kv {
        source => "[keycloak][message]"
        field_split => ", "
        value_split => "="
        target => "kc_event"
      }

      mutate {
        add_field => {
          "event_type" => "%{[kc_event][type]}"
          "realm" => "%{[kc_event][realmId]}"
          "client_id" => "%{[kc_event][clientId]}"
        }
      }
    }

    # GeoIP enrichment
    if [kc_event][ipAddress] {
      geoip {
        source => "[kc_event][ipAddress]"
        target => "geo"
      }
    }

    # Detect suspicious patterns
    if [kc_event][type] == "LOGIN_ERROR" {
      mutate {
        add_tag => ["login_failure"]
      }
    }
  }
}

output {
  if [service][name] == "keycloak" {
    elasticsearch {
      hosts => ["elasticsearch:9200"]
      index => "keycloak-events-%{+YYYY.MM.dd}"
    }
  }
}

8.4 キバナダッシュボード

監視する Kibana ダッシュボードを作成します。

  • ログイン成功率/失敗率— 時間の経過に伴う棒グラフ
  • よくあるログインエラー— エラーの種類別の円グラフ
  • 地理的位置によるログイン— 地図の視覚化
  • IP によるログイン失敗— ブルートフォース検出テーブル
  • ユーザー登録動向— 日別の折れ線グラフ
  • 管理操作の監査— 完全な詳細を含むデータテーブル

9. Grafana Loki の統合

Grafana Loki は、ELK よりも軽量なログ集約ソリューションであり、Kubernetes 環境に適しています。

9.1 Promtail の構成

# promtail-config.yml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: keycloak
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        regex: keycloak
        action: keep
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod
    pipeline_stages:
      - json:
          expressions:
            level: level
            logger: loggerName
            message: message
            timestamp: timestamp
      - labels:
          level:
          logger:
      - match:
          selector: '{app="keycloak"} |~ "type=LOGIN|type=REGISTER|type=LOGOUT"'
          stages:
            - regex:
                expression: 'type=(?P<event_type>\w+), realmId=(?P<realm>[\w-]+), clientId=(?P<client_id>[\w-]+), userId=(?P<user_id>[\w-]+)'
            - labels:
                event_type:
                realm:

9.2 Grafana ダッシュボードのクエリ

# Login failures trong 1 giờ qua
{app="keycloak"} |~ "type=LOGIN_ERROR" | json | count_over_time({app="keycloak"} |~ "LOGIN_ERROR" [1h])

# Login events theo realm
sum by (realm) (count_over_time({app="keycloak"} |~ "type=LOGIN" [5m]))

# Top IPs với login failures
{app="keycloak"} |~ "type=LOGIN_ERROR" | regexp `ipAddress=(?P<ip>[\d.]+)` | count by (ip) | sort desc | limit 10

10. SIEM統合

Keycloakイベントをセキュリティ情報およびイベント管理(SIEM)システムと統合します。

10.1 Splunk の統合

# Cấu hình Filebeat ship đến Splunk HEC
output.logstash:
  enabled: false

output.http:
  enabled: true
  hosts: ["https://splunk-hec:8088"]
  path: "/services/collector/event"
  headers:
    Authorization: "Splunk <HEC_TOKEN>"
  format: json

10.2 SIEM の使用例

使用事例イベントパターンアクション
ブルートフォース検出同じ IP からの複数の LOGIN_ERRORアラート + IP ブロック
アカウント乗っ取りGeoIP からのログインは異常ですアラート + MFA が必要
権限昇格管理者が管理者ロールを割り当てますアラート + レビュー
データの引き出し異常なトークンリクエストが多いアラート + セッションの取り消し
不審な登録同じIPからの複数のREGISTERアラート + キャプチャ

11. 監査のコンプライアンス

11.1 SOC2 要件

SOC2制御キークロークの実装
CC6.1— 論理アクセスのセキュリティLOGIN、LOGIN_ERROR、PASSWORD 変更のイベントログ
CC6.2— ユーザー認証MFA イベント、登録イベント
CC6.3— アクセス許可ロール/権限変更の管理者イベント
CC7.2— セキュリティ監視ログイン失敗時のリアルタイムアラート
CC8.1— 変更管理表現を含む管理イベント

11.2 HIPAA 要件

HIPAA コントロールキークロークの実装
§164.312(b)— 監査制御すべてのイベント タイプ、表現を含む管理イベントを有効にする
§164.312(d)— 本人認証認証試行のイベントログ
§164.308(a)(5)— セキュリティ意識不審なアクティビティの電子メール通知

11.3 保持ポリシー

# Cấu hình event retention
# SOC2: minimum 1 năm
# HIPAA: minimum 6 năm

# Trong Keycloak (built-in store)
# Realm Settings → Events → Expiration: 365 days

# Trong Elasticsearch (centralized logging)
# ILM Policy:
# - Hot: 30 days (SSD)
# - Warm: 335 days (HDD)
# - Cold/Frozen: 5+ years (S3/GCS)
# - Delete: 7 years

12. アラートの自動化

12.1 プロメテウスのアラート

Keycloakはメトリクスを次のように公開します。/メトリクスエンドポイント (メトリクスを有効にする必要があります):

# Bật metrics
bin/kc.sh start --metrics-enabled=true
# prometheus-alerts.yml
groups:
  - name: keycloak-security
    rules:
      - alert: HighLoginFailureRate
        expr: |
          sum(rate(keycloak_login_error_total[5m])) by (realm)
          /
          sum(rate(keycloak_login_total[5m])) by (realm)
          > 0.3
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High login failure rate in realm {{ $labels.realm }}"
          description: "Login failure rate is {{ $value | humanizePercentage }} (threshold: 30%)"

      - alert: BruteForceDetected
        expr: |
          sum(increase(keycloak_login_error_total[5m])) by (realm) > 50
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Possible brute-force attack on realm {{ $labels.realm }}"
          description: "{{ $value }} login failures in 5 minutes"

      - alert: UnusualRegistrationSpike
        expr: |
          sum(increase(keycloak_registrations_total[10m])) by (realm) > 100
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Unusual registration spike in realm {{ $labels.realm }}"

12.2 アラートマネージャーのルーティング

# alertmanager.yml
route:
  receiver: default
  routes:
    - match:
        severity: critical
      receiver: pagerduty-security
      continue: true
    - match:
        severity: critical
      receiver: slack-security
    - match:
        severity: warning
      receiver: slack-ops

receivers:
  - name: default
    email_configs:
      - to: [email protected]

  - name: slack-security
    slack_configs:
      - api_url: https://hooks.slack.com/services/xxx
        channel: '#security-alerts'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ .CommonAnnotations.description }}'

  - name: pagerduty-security
    pagerduty_configs:
      - service_key: <pagerduty-integration-key>
        severity: critical

13. ベストプラクティス

  • ログイン イベントと管理者イベントの両方を有効にする— システム内のアクティビティを見逃さないでください。
  • イベントを外部システムに送信する— 組み込みのイベント ストアだけに依存しないでください。長期保存には ELK/Loki/SIEM を使用してください。
  • 管理イベント表現をオンにする— 完全な監査のために、管理操作のリクエスト/レスポンス本文を保存します。
  • 適切な保持期間を設定する— コンプライアンス要件に準拠します (SOC2: 1 年、HIPAA: 6 年)。
  • ログイン失敗率を監視する— ブルートフォース検出とアカウント乗っ取りに対するアラートを設定します。
  • イベントを関連付ける— Keycloakイベントとアプリケーションログを組み合わせて、全体像を把握します。
  • イベントログを保護する— ログ データには PII が含まれているため、保存中および転送中に暗号化してアクセスを制限する必要があります。