איסוף יומנים של 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, מעתיקים את כתובת ה-URL של נקודת הקצה של הפיד.
הפורמט של כתובת ה-URL הוא:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateאו
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateשומרים את כתובת ה-URL הזו כדי להשתמש בה בשלבים הבאים.
לוחצים על סיום.
יצירת מפתח API של Google Cloud
כדי לבצע אימות ב-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, בוחרים את ה-realm הרצוי.
- עוברים אל Realm Settings > Events.
- בתפריט הנפתח Event listeners (מאזינים לאירועים), בוחרים באפשרות ext-event-webhook.
- לוחצים על Save.
הגדרת webhook ב-Keycloak
הרכבת כתובת ה-URL של ה-webhook
משלבים את כתובת ה-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, מציגים רשימה של כל ה-webhooks של התחום:
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
התגובה מחזירה רשימה של אובייקטים של webhook. מוודאים שכתובת ה-URL הנכונה של ה-webhook מופיעה עם "enabled": "true".
סוגי אירועים של 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 | גורם רנדומיזציה לשינוי קצב העברת הנתונים |
הפניה לשיטות אימות
פידים של Chronicle webhook תומכים בכמה שיטות אימות. בוחרים את השיטה שהספק תומך בה.
שיטה 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(לא תלוי-רישיות)
למפתח סודי:
x-chronicle-auth(מומלץ)-
X-Chronicle-Auth(לא תלוי-רישיות)
מגבלות ושיטות מומלצות לשימוש ב-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.