איסוף יומנים של Keycloak
במאמר הזה מוסבר איך להגדיר את Keycloak כדי לשלוח יומנים ל-Google Security Operations באמצעות ווּבּהוּקים.
Keycloak הוא פתרון קוד פתוח לניהול זהויות והרשאות גישה (IAM) שמספק יכולות של כניסה יחידה (SSO), איחוד משתמשים, תיווך זהויות וכניסה באמצעות חשבונות ברשתות חברתיות. הוא תומך בפרוטוקולים OpenID Connect, OAuth 2.0 ו-SAML 2.0, ועוקב אחרי אירועי משתמשים (התחברות, התנתקות, הרשמה, שינוי סיסמה) ואירועי אדמין (פעולות ניהול של משתמשים, לקוחות, תחומים ותפקידים) לצורך ביקורת אבטחה.
לפני שמתחילים
חשוב לוודא שמתקיימות הדרישות המוקדמות הבאות:
- מופע של Google SecOps
- מופע פעיל של Keycloak (מומלצת גרסה 20 ואילך)
- גישת אדמין למסוף Admin של Keycloak
- גישה למערכת הקבצים או למאגר של שרת Keycloak כדי לפרוס תוספים
- גישה למסוף Google Cloud (ליצירת מפתח API)
יצירת פיד של webhook ב-Google SecOps
יצירת הפיד
- עוברים אל SIEM Settings > Feeds (הגדרות SIEM > פידים).
- לוחצים על הוספת פיד חדש.
- בדף הבא, לוחצים על הגדרת פיד יחיד.
- בשדה שם הפיד, מזינים שם לפיד (לדוגמה,
Keycloak Events). - בוחרים באפשרות Webhook בתור סוג המקור.
- בוחרים באפשרות Keycloak בתור סוג היומן.
- לוחצים על הבא.
- מציינים ערכים לפרמטרים הבאים של הקלט:
- תו מפריד לפיצול (אופציונלי): מזינים
\nכדי לפצל אירועים מרובי שורות (כל בקשת POST של webhook מכילה אירוע יחיד, כך שאפשר להשאיר את השדה הזה ריק). - מרחב שמות של נכס: מרחב השמות של הנכס
- תוויות להוספה: התווית שתתווסף לאירועים מהפיד הזה
- תו מפריד לפיצול (אופציונלי): מזינים
- לוחצים על הבא.
- בודקים את ההגדרות של הפיד החדש במסך סיום ולוחצים על שליחה.
יצירה ושמירה של מפתח סודי
אחרי שיוצרים את הפיד, צריך ליצור מפתח סודי לאימות:
- בדף הפרטים של הפיד, לוחצים על יצירת מפתח סודי.
- בתיבת דו-שיח מוצג המפתח הסודי.
- מעתיקים ושומרים את המפתח הסודי באופן מאובטח.
חשוב: המפתח הסודי מוצג רק פעם אחת, ואי אפשר לאחזר אותו מאוחר יותר. אם מאבדים אותו, צריך ליצור מפתח סודי חדש.
קבלת כתובת ה-URL של נקודת הקצה של הפיד
- עוברים לכרטיסייה פרטים של הפיד.
- בקטע Endpoint Information, מעתיקים את Feed endpoint 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. יוצרים מפתח API מוגבל במסוף Google Cloud.
יצירת מפתח API
- נכנסים אל הדף Credentials במסוף Google Cloud.
- בוחרים את הפרויקט (הפרויקט שמשויך למופע Chronicle).
- לוחצים על Create credentials > API key.
- מפתח API נוצר ומוצג בתיבת דו-שיח.
- לוחצים על Edit API key כדי להגביל את המפתח.
הגבלת מפתח ה-API
- בדף ההגדרות API key:
- שם: מזינים שם תיאורי (לדוגמה,
Chronicle Webhook API Key)
- שם: מזינים שם תיאורי (לדוגמה,
- בקטע API restrictions (הגבלות על API):
- בוחרים באפשרות הגבלת המקש.
- בתפריט הנפתח Select APIs (בחירת ממשקי API), מחפשים ובוחרים באפשרות Google SecOps API (או Chronicle API).
- לוחצים על Save.
- מעתיקים את הערך של מפתח ה-API מהשדה מפתח ה-API בחלק העליון של הדף.
- שומרים את מפתח ה-API בצורה מאובטחת.
הפעלת אחסון אירועים ב-Keycloak
לפני שמגדירים את תוסף ה-webhook, צריך להפעיל את אחסון האירועים ב-Keycloak כדי שהאירועים ייווצרו ויהיו זמינים להעברה.
הפעלת אירועים של משתמשים
- נכנסים למסוף Admin של Keycloak.
- בוחרים את מקבץ שרתי המשחק (Realm) שרוצים לעקוב אחריו מהתפריט הנפתח של מקבץ שרתי המשחק (Realm) בפינה הימנית העליונה.
- עוברים אל Realm Settings > Events.
- לוחצים על כרטיסיית המשנה הגדרות אירועים של משתמשים.
- מפעילים את המתג שמירת אירועים.
- מגדירים את תקופת התפוגה (מומלץ להגדיר 7 ימים לפחות).
- לוחצים על Save.
הפעלת אירועי אדמין
- בכרטיסייה אירועים, בוחרים בכרטיסיית המשנה הגדרות אירועים של אדמין.
- מפעילים את המתג שמירת אירועים.
- מפעילים את המתג Include representation כדי לתעד פרטים מלאים של אובייקטים שהשתנו.
- מגדירים את תקופת התפוגה (מומלץ להגדיר 7 ימים לפחות).
- לוחצים על Save.
התקנה של התוסף webhook רכיב event listener
Keycloak לא כולל רכיב event listener מקורי של webhook. מתקינים את התוסף keycloak-events מ-Phase Two (p2-inc) כדי להפעיל את השליחה של תגובות לפעולות מאתר אחר (webhook).
הורדה ופריסה של התוסף
מורידים את קובץ ה-JAR של הגרסה האחרונה מדף הגרסאות של keycloak-events ב-Maven Central או יוצרים אותו מהמקור:
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean installמעתיקים את קובץ ה-JAR שמתקבל לספרייה
providersשל Keycloak:cp target/keycloak-events-*.jar /opt/keycloak/providers/בונים מחדש את Keycloak ומפעילים אותו מחדש:
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
הפעלת רכיב ה-event listener של ה-webhook
- נכנסים למסוף Admin של Keycloak.
- בוחרים את התחום הרצוי מהתפריט הנפתח של התחומים.
- עוברים אל Realm Settings > Events.
- בתפריט הנפתח Event listeners (מאזינים לאירועים), בוחרים באפשרות ext-event-webhook.
- לוחצים על Save.
הגדרת webhook ב-Keycloak
הרכבת ה-webhook URL
משלבים את כתובת ה-URL של נקודת הקצה של Chronicle ואת מפתח ה-API:
<ENDPOINT_URL>?key=<API_KEY>לדוגמה:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
יצירת מינוי ל-webhook באמצעות API בארכיטקטורת REST של Keycloak
התוסף keycloak-events מספק נקודות קצה של REST לניהול מינויים של webhook. משתמשים ב-Keycloak Admin API בארכיטקטורת REST כדי ליצור 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>: כתובת ה-URL של נקודת הקצה של הפיד של Chronicle שהועתקה קודם -
<API_KEY>: מפתח Google Cloud API שנוצר קודם -
<SECRET_KEY>: מפתח הסוד של ה-webhook ב-Chronicle שנוצר קודם -
<WEBHOOK_HMAC_SECRET>: מחרוזת סודית שרירותית לחתימת מטען ייעודי (payload) של 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— שליחה רק של אירועי אדמין שקשורים ליצירת משתמשים
פורמט המטען הייעודי (payload) של webhook
ה-webhook שולח אירועים כבקשות HTTP POST עם מטען ייעודי (payload) מסוג JSON. מטען ייעודי (payload) של אירוע משתמש לדוגמה:
{ "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
התוסף משתמש בהשהיה מעריכית לפני ניסיון חוזר (exponential backoff) אוטומטית לניסיונות חוזרים כשמתקבלת תגובה שאינה 2xx:
| פרמטר | ערך ברירת המחדל | תיאור |
|---|---|---|
| backoffInitialInterval | 500 אלפיות השנייה | מרווח הזמן הראשוני לניסיון חוזר |
| backoffMaxElapsedTime | 900,000 אלפיות השנייה (15 דקות) | משך הזמן המקסימלי הכולל לביצוע ניסיון חוזר |
| backoffMaxInterval | 180,000 אלפיות השנייה (3 דקות) | המרווח המקסימלי בין ניסיונות חוזרים |
| backoffMultiplier | 5 | מכפיל לכל מרווח זמן בין ניסיונות חוזרים |
| backoffRandomizationFactor | 0.5 | גורם אקראיות לשינוי קצב העברת הנתונים |
הפניה לשיטות אימות
פידים של webhook ב-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 + כותרת)
חלק מההגדרות משתמשות במפתח API בכתובת ה-URL ובמפתח סודי בכותרת.
פורמט הבקשה:
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(case-insensitive)
למפתח סודי:
x-chronicle-auth(מומלץ)-
X-Chronicle-Auth(case-insensitive)
מגבלות ושיטות מומלצות לשימוש ב-Webhook
מגבלות על בקשות
| הגבלה | ערך |
|---|---|
| גודל בקשה מקסימלי | 4MB |
| מספר QPS מקסימלי (שאילתות לשנייה) | 15,000 |
| זמן קצוב לתפוגה של בקשה | 30 שניות |
| התנהגות של ניסיון חוזר | אוטומטי עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff) |
טבלת מיפוי 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 | הערך הוא STATUS_UPDATE אם payload.ip_address לא ריק, אחרת USER_UNCATEGORIZED אם uuid לא ריק, אחרת 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 | מוזגו עם תוויות שנוצרו מאובייקט, 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.