Mengumpulkan log Keycloak
Dokumen ini menjelaskan cara mengonfigurasi Keycloak untuk mengirimkan log ke Google Security Operations menggunakan webhook.
Keycloak adalah solusi pengelolaan akses dan identitas (IAM) open source yang menyediakan kemampuan single sign-on (SSO), federasi pengguna, perantara identitas, dan login sosial. Layanan ini mendukung protokol OpenID Connect, OAuth 2.0, dan SAML 2.0 serta melacak peristiwa pengguna (login, logout, pendaftaran, perubahan sandi) dan peristiwa admin (operasi pengelolaan pengguna, klien, realm, dan peran) untuk audit keamanan.
Sebelum memulai
Pastikan Anda memiliki prasyarat berikut:
- Instance Google SecOps
- Instance Keycloak yang sedang berjalan (direkomendasikan versi 20 atau yang lebih baru)
- Akses administrator ke Konsol Admin Keycloak
- Akses ke sistem file atau container server Keycloak untuk men-deploy ekstensi
- Akses ke Konsol Google Cloud (untuk pembuatan kunci API)
Membuat feed webhook di Google SecOps
Buat feed
- Buka Setelan SIEM > Feed.
- Klik Tambahkan Feed Baru.
- Di halaman berikutnya, klik Konfigurasi satu feed.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
Keycloak Events). - Pilih Webhook sebagai Jenis sumber.
- Pilih Keycloak sebagai Jenis log.
- Klik Berikutnya.
- Tentukan nilai untuk parameter input berikut:
- Pemisah pemisahan (opsional): Masukkan
\nuntuk memisahkan peristiwa multi-baris (setiap POST webhook berisi satu peristiwa, sehingga kolom ini dapat dibiarkan kosong). - Namespace aset: Namespace aset
- Label penyerapan: Label yang akan diterapkan ke peristiwa dari feed ini
- Pemisah pemisahan (opsional): Masukkan
- Klik Berikutnya.
- Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.
Buat dan simpan kunci rahasia
Setelah membuat feed, Anda harus membuat kunci rahasia untuk autentikasi:
- Di halaman detail feed, klik Buat Kunci Rahasia.
- Dialog akan menampilkan kunci rahasia.
- Salin dan simpan kunci rahasia dengan aman.
Penting: Kunci rahasia hanya ditampilkan satu kali dan tidak dapat diambil lagi. Jika Anda kehilangan kunci tersebut, Anda harus membuat kunci rahasia baru.
Mendapatkan URL endpoint feed
- Buka tab Detail untuk feed tersebut.
- Di bagian Endpoint Information, salin Feed endpoint URL.
Format URL-nya adalah:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateatau
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateSimpan URL ini untuk langkah berikutnya.
Klik Done.
Membuat kunci Google Cloud API
Chronicle memerlukan kunci API untuk autentikasi. Buat kunci API yang dibatasi di Konsol Google Cloud.
Buat kunci API
- Buka halaman Kredensial Konsol Google Cloud.
- Pilih project Anda (project yang terkait dengan instance Chronicle Anda).
- Klik Create credentials > API key.
- Kunci API dibuat dan ditampilkan dalam dialog.
- Klik Edit API key untuk membatasi kunci.
Membatasi kunci API
- Di halaman setelan kunci API:
- Nama: Masukkan nama deskriptif (misalnya,
Chronicle Webhook API Key)
- Nama: Masukkan nama deskriptif (misalnya,
- Di bagian Pembatasan API:
- Pilih Restrict key.
- Di drop-down Select APIs, telusuri dan pilih Google SecOps API (atau Chronicle API).
- Klik Simpan.
- Salin nilai kunci API dari kolom API key di bagian atas halaman.
- Simpan kunci API dengan aman.
Mengaktifkan penyimpanan peristiwa di Keycloak
Sebelum mengonfigurasi ekstensi webhook, aktifkan penyimpanan peristiwa di Keycloak agar peristiwa dibuat dan tersedia untuk penerusan.
Mengaktifkan peristiwa pengguna
- Login ke Konsol Admin Keycloak.
- Pilih realm yang ingin Anda pantau dari dropdown realm di sudut kiri atas.
- Buka Setelan Realm > Peristiwa.
- Pilih sub-tab Setelan peristiwa pengguna.
- Aktifkan tombol Simpan acara.
- Tetapkan periode Masa berlaku (minimum yang direkomendasikan: 7 hari).
- Klik Simpan.
Mengaktifkan peristiwa admin
- Di tab Peristiwa yang sama, pilih sub-tab Setelan peristiwa admin.
- Aktifkan tombol Simpan acara.
- Aktifkan tombol Sertakan representasi untuk merekam detail lengkap objek yang diubah.
- Tetapkan periode Masa berlaku (minimum yang direkomendasikan: 7 hari).
- Klik Simpan.
Menginstal ekstensi pemroses peristiwa webhook
Keycloak tidak menyertakan pemroses peristiwa webhook native. Instal ekstensi keycloak-events dari Phase Two (p2-inc) untuk mengaktifkan pengiriman webhook.
Mendownload dan men-deploy ekstensi
Download JAR rilis terbaru dari halaman rilis keycloak-events di Maven Central atau bangun dari sumber:
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean installSalin file JAR gabungan yang dihasilkan ke direktori
providersKeycloak:cp target/keycloak-events-*.jar /opt/keycloak/providers/Bangun ulang dan mulai ulang Keycloak:
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
Aktifkan pemroses peristiwa webhook
- Login ke Konsol Admin Keycloak.
- Pilih realm target dari dropdown realm.
- Buka Setelan Realm > Peristiwa.
- Di dropdown Event listeners, pilih ext-event-webhook.
- Klik Simpan.
Mengonfigurasi webhook Keycloak
Buat URL webhook
Gabungkan URL endpoint Chronicle dan kunci API:
<ENDPOINT_URL>?key=<API_KEY>Contoh:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Membuat langganan webhook melalui Keycloak REST API
Ekstensi keycloak-events menyediakan endpoint REST untuk mengelola langganan webhook. Gunakan Keycloak Admin REST API untuk membuat webhook.
Langkah 1: Dapatkan token akses
Minta token akses dari Keycloak menggunakan akun admin:
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')
Ganti kode berikut:
<KEYCLOAK_HOST>: Nama host dan port server Keycloak Anda (misalnya,keycloak.example.com:8443)<ADMIN_USERNAME>: Nama pengguna admin Keycloak Anda<ADMIN_PASSWORD>: Sandi admin Keycloak Anda
Langkah 2: Buat webhook
Kirim permintaan POST untuk membuat langganan webhook untuk realm target:
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": ["*"] }'
Ganti kode berikut:
<KEYCLOAK_HOST>: Nama host server Keycloak Anda<REALM_NAME>: Nama realm yang akan dipantau (misalnya,masterataumy-realm)<ENDPOINT_URL>: URL endpoint feed Chronicle yang disalin sebelumnya<API_KEY>: Kunci Google Cloud API yang dibuat sebelumnya<SECRET_KEY>: Kunci rahasia webhook Chronicle yang dibuat sebelumnya<WEBHOOK_HMAC_SECRET>: String rahasia arbitrer untuk penandatanganan HMAC payload webhook (misalnya,mySecretKey123)
Langkah 3: Verifikasi webhook
Pastikan webhook telah dibuat dengan mencantumkan semua webhook untuk realm:
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
Respons menampilkan daftar objek webhook. Verifikasi bahwa webhook Anda muncul dengan "enabled": "true" dan URL yang benar.
Jenis peristiwa webhook
Kolom eventTypes menerima array ekspresi untuk memfilter peristiwa mana yang dikirim:
*— Mengirim semua peristiwa (direkomendasikan untuk integrasi SIEM)access.*— Mengirim semua peristiwa aksesadmin.*— Mengirim semua peristiwa adminadmin.USER-*— Mengirim semua peristiwa admin yang terkait dengan penggunaadmin-USER-CREATE— Hanya mengirim peristiwa admin pembuatan pengguna
Format payload webhook
Webhook mengirim peristiwa sebagai permintaan HTTP POST dengan payload JSON. Contoh payload peristiwa pengguna:
{ "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" } }
Perilaku percobaan ulang webhook
Ekstensi menggunakan backoff eksponensial otomatis untuk percobaan ulang saat respons non-2xx diterima:
| Parameter | Nilai Default | Deskripsi |
|---|---|---|
| backoffInitialInterval | 500 md | Interval percobaan ulang awal |
| backoffMaxElapsedTime | 900000 md (15 menit) | Total waktu percobaan ulang maksimum |
| backoffMaxInterval | 180000 md (3 menit) | Interval maksimum antara percobaan ulang |
| backoffMultiplier | 5 | Pengganda untuk setiap interval percobaan ulang |
| backoffRandomizationFactor | 0,5 | Faktor pengacakan untuk jitter |
Referensi metode autentikasi
Feed webhook Chronicle mendukung beberapa metode autentikasi. Pilih metode yang didukung vendor Anda.
Metode 1: Header kustom (Direkomendasikan)
Jika vendor Anda mendukung header HTTP kustom, gunakan metode ini untuk keamanan yang lebih baik.
Format permintaan:
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" }
Kelebihan:
- Kunci API dan rahasia tidak terlihat di URL
- Lebih aman (header tidak dicatat dalam log akses server web)
- Metode pilihan jika vendor mendukungnya
Metode 2: Parameter kueri
Jika vendor Anda tidak mendukung header kustom, tambahkan kredensial ke URL.
Format URL:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>Contoh:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...Format permintaan:
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Kekurangan:
- Kredensial terlihat di URL
- Dapat dicatat dalam log akses server web
- Kurang aman dibandingkan header
Metode 3: Hybrid (URL + Header)
Beberapa konfigurasi menggunakan kunci API di URL dan kunci rahasia di header.
Format permintaan:
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" }
Nama header autentikasi
Chronicle menerima nama header berikut untuk autentikasi:
Untuk kunci API:
x-goog-chronicle-auth(direkomendasikan)X-Goog-Chronicle-Auth(tidak peka huruf besar/kecil)
Untuk kunci rahasia:
x-chronicle-auth(direkomendasikan)X-Chronicle-Auth(tidak peka huruf besar/kecil)
Batasan dan praktik terbaik webhook
Batas permintaan
| Batas | Nilai |
|---|---|
| Ukuran permintaan maksimum | 4 MB |
| QPS Maks (kueri per detik) | 15.000 |
| Waktu tunggu permintaan | 30 seconds |
| Perilaku percobaan ulang | Otomatis dengan backoff eksponensial |
Tabel pemetaan UDM
| Kolom Log | Pemetaan UDM | Logika |
|---|---|---|
| payload.client_id | additional.fields | Digabungkan dengan kolom yang dibuat dari payload.client_id, payload.realm_id |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | Diuraikan menggunakan filter tanggal dengan pola ISO8601 dan yyyy-MM-dd'T'HH:mm:ss.SSSZ |
| payload.ip_address | metadata.event_type | Ditetapkan ke "STATUS_UPDATE" jika payload.ip_address tidak kosong, atau "USER_UNCATEGORIZED" jika uuid tidak kosong, atau "GENERIC_EVENT" |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | Nilai disalin secara langsung |
| payload.session_id | network.session_id | Nilai disalin secara langsung |
| payload.ip_address | principal.ip | Nilai disalin secara langsung |
| source_metadata.schema | principal.resource.attribute.labels | Digabungkan dengan label yang dibuat dari source_metadata.schema, source_metadata.table, source_metadata.is_deleted (dikonversi menjadi string), 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 | Nilai disalin secara langsung |
| objek | security_result.detection_fields | Digabungkan dengan label yang dibuat dari object, read_method, payload.id |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | Nilai disalin secara langsung |
| nama pengguna | target.user.userid | Nilai disalin secara langsung |
| metadata.product_name | metadata.product_name | Ditetapkan ke "KEYCLOAK" |
| metadata.vendor_name | metadata.vendor_name | Ditetapkan ke "KEYCLOAK" |
username" from "details_json |
target.user.userid |
Dipetakan dari log perubahan |
redirect_uri" from "details_json |
target.url |
Dipetakan dari log perubahan |
realm_id" and "client_id |
additional.fields |
Dipetakan dari log perubahan |
Log Perubahan
Melihat Log Perubahan untuk parser ini
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.