איסוף יומנים של Google App Engine
גרסת המנתח: 3.0
במסמך הזה מוסבר איך להטמיע יומנים של Google App Engine ב-Google Security Operations באמצעות Google Cloud Storage V2.
Google App Engine היא פלטפורמה מנוהלת ללא שרת (serverless) לבנייה ולפריסה של אפליקציות אינטרנט וממשקי API. מערכת App Engine יוצרת באופן אוטומטי יומני בקשות לבקשות HTTP ויומני אפליקציות מהקוד שלכם. היומנים האלה נשלחים ל-Cloud Logging ואפשר לייצא אותם ל-Cloud Storage כדי להטמיע אותם ב-Google Security Operations.
לפני שמתחילים
חשוב לוודא שמתקיימות דרישות הסף הבאות:
- מכונה של Google SecOps
- פרויקט ב-GCP עם Cloud Storage API מופעל
- הרשאות ליצירה ולניהול של קטגוריות ב-GCS
- הרשאות לניהול מדיניות IAM בקטגוריות GCS
- הרשאות ליצירת sinks ב-Cloud Logging (roles/logging.configWriter)
- אפליקציית App Engine פעילה (סביבה רגילה או גמישה)
יצירת קטגוריה של Google Cloud Storage
- נכנסים למסוף Google Cloud.
- בוחרים פרויקט או יוצרים פרויקט חדש.
- בתפריט הניווט, נכנסים ל-Cloud Storage > Buckets.
- לוחצים על Create bucket.
מספקים את פרטי ההגדרה הבאים:
הגדרה ערך Name your bucket מזינים שם ייחודי ברמה גלובלית (לדוגמה, appengine-logs-export)סוג המיקום בוחרים לפי הצרכים (אזור, שני אזורים, מספר אזורים) Location בוחרים את המיקום (לדוגמה, us-central1).סוג האחסון (storage class) רגיל (מומלץ ליומנים שנכנסים אליהם לעיתים קרובות) בקרת גישה אחיד (מומלץ) כלי הגנה אופציונלי: הפעלת ניהול גרסאות של אובייקטים או מדיניות שמירת נתונים לוחצים על יצירה.
הגדרת Cloud Logging לייצוא יומני App Engine ל-GCS
ב-Cloud Logging, רשומות ביומן מנותבות ליעדים נתמכים, כולל קטגוריות של Cloud Storage, באמצעות כיורים ביומן. לזהות הכותב של יעד הנתונים נדרש התפקיד 'יצירת אובייקטים של אחסון' (roles/storage.objectCreator) בקטגוריית היעד.
יצירת sink ב-Cloud Logging
- במסוף Google Cloud, נכנסים לדף Logging > Log Router.
- לוחצים על Create sink (יצירת יעד).
- מזינים את פרטי ההגדרה הבאים:
- שם יעד: מזינים שם תיאורי (לדוגמה,
appengine-to-gcs). - תיאור הכיור: תיאור אופציונלי.
- שם יעד: מזינים שם תיאורי (לדוגמה,
- לוחצים על הבא.
- בקטע Select sink service (בחירת שירות יעד):
- שירות היעד: בוחרים באפשרות קטגוריה של Cloud Storage.
- בחירת קטגוריית אחסון של Cloud Storage: בוחרים באפשרות
appengine-logs-exportמהתפריט הנפתח.
- לוחצים על הבא.
בקטע Choose logs to include in sink, מזינים שאילתת סינון כדי לבחור את היומנים של App Engine. סוג המשאב חייב להיות בדיוק "gae_app".
לכל היומנים של App Engine (יומני בקשות ויומני אפליקציות):
resource.type="gae_app"רק עבור יומני בקשות של App Engine:
resource.type="gae_app" logName="projects/PROJECT_ID/logs/appengine.googleapis.com/request_log"ביומני האפליקציות של App Engine (stdout/stderr):
resource.type="gae_app" (logName="projects/PROJECT_ID/logs/stdout" OR logName="projects/PROJECT_ID/logs/stderr")מחליפים את
PROJECT_IDבמזהה הפרויקט ב-GCP.לוחצים על הבא.
בודקים את ההגדרות ולוחצים על Create sink (יצירת מאגר).
מתן הרשאות לזהות של כותב היעד
אחרי שיוצרים את הכיור, צריך להקצות לזהות הכותב של הכיור את התפקיד "יצירת אובייקטים של אחסון" בקטגוריית היעד. הזהות של חשבון השירות עם הרשאת הכתיבה נראית בערך כך: serviceAccount:service-123456789012@gcp-sa-logging.iam.gserviceaccount.com
- בדף Log Router, מאתרים את ה-sink החדש שנוצר.
- לוחצים על סמל התפריט (שלוש נקודות אנכיות) לצד שם הכיור.
- בוחרים באפשרות View sink details.
- מעתיקים את הזהות של הכותב (כתובת האימייל של חשבון השירות).
- עוברים אל Cloud Storage > Buckets.
- לוחצים על שם הקטגוריה (
appengine-logs-export). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מזינים את פרטי ההגדרה הבאים:
- הוספת חשבונות משתמשים: מדביקים את זהות הכותב של הכיור (כתובת האימייל של חשבון השירות).
- הקצאת תפקידים: בוחרים באפשרות יצירת אובייקטים של אחסון.
- לוחצים על Save.
אחזור חשבון השירות של Google SecOps
מערכת Google SecOps משתמשת בחשבון שירות ייחודי כדי לקרוא נתונים מהקטגוריה שלכם ב-GCS. אתם צריכים לתת לחשבון השירות הזה גישה לקטגוריה שלכם.
הגדרה של פיד ב-Google SecOps להטמעה של יומנים מ-App Engine
- עוברים אל SIEM Settings > Feeds.
- לוחצים על הוספת פיד חדש.
- לוחצים על הגדרת פיד יחיד.
- בשדה שם הפיד, מזינים שם לפיד (לדוגמה,
App Engine Logs). - בוחרים באפשרות Google Cloud Storage V2 בתור סוג המקור.
בוחרים באפשרות GCP_APP_ENGINE בתור Log type.
לוחצים על Get Service Account (קבלת חשבון שירות).
תוצג כתובת אימייל ייחודית של חשבון שירות, לדוגמה:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comמעתיקים את כתובת האימייל הזו. תשתמשו בו בשלב הבא.
לוחצים על הבא.
מציינים ערכים לפרמטרים הבאים של הקלט:
כתובת ה-URL של קטגוריית האחסון: מזינים את ה-URI של קטגוריית GCS עם נתיב הקידומת:
gs://appengine-logs-export/ב-Cloud Logging, קובצי היומן שמיוצאים מאורגנים בהיררכיות של ספריות לפי סוג היומן והתאריך. סוג היומן יכול להיות שם מורכב כמו appengine.googleapis.com/request_log. הקבצים מחולקים למקטעים ונקראים לפי תקופות (לדוגמה, 08:00:00_08:59:59_S0.json).
אפשרות למחיקת המקור: בוחרים את אפשרות המחיקה לפי ההעדפה שלכם:
- אף פעם: לא נמחקים קבצים אחרי ההעברה (מומלץ לבדיקות).
- מחיקת קבצים שהועברו: מחיקת קבצים אחרי שהם הועברו בהצלחה.
- מחיקת קבצים שהועברו וספריות ריקות: מחיקת קבצים וספריות ריקות אחרי שההעברה מסתיימת.
גיל מקסימלי של קובץ: כולל קבצים ששונו במספר הימים האחרונים. ברירת המחדל היא 180 ימים.
מרחב השמות של הנכס: מרחב השמות של הנכס.
תוויות הטמעה: התווית שתתווסף לאירועים מהפיד הזה.
לוחצים על הבא.
בודקים את ההגדרות של הפיד החדש במסך סיום ולוחצים על שליחה.
הקצאת הרשאות IAM לחשבון השירות של Google SecOps
חשבון השירות של Google SecOps צריך את התפקיד צפייה באובייקט אחסון בקטגוריית GCS.
- נכנסים לדף Cloud Storage > Buckets.
- לוחצים על שם הקטגוריה (
appengine-logs-export). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מזינים את פרטי ההגדרה הבאים:
- Add principals (הוספת חשבונות משתמשים): מדביקים את כתובת האימייל של חשבון השירות של Google SecOps.
- הקצאת תפקידים: בוחרים באפשרות צפייה באובייקט אחסון.
לוחצים על Save.
הסבר על מבנה היומן של App Engine
App Engine שולח באופן אוטומטי גם יומני בקשות וגם יומני אפליקציות ל-Cloud Logging. App Engine מפיק באופן אוטומטי יומנים של בקשות שנשלחות לאפליקציה, כך שאין צורך לכתוב יומנים של בקשות. בקטע הזה מוסבר איך לכתוב יומני אפליקציות.
ביומני הבקשות של App Engine יש רשומות ביומן שמכילות שדות protoPayload שמחזיקים אובייקטים מסוג RequestLog עם @type 'type.googleapis.com/google.appengine.logging.v1.RequestLog'. סוג המשאב הוא gae_app.
כברירת מחדל, מטען היומן הוא מחרוזת טקסט שמאוחסנת בשדה textPayload של רשומת היומן. המחרוזות מופיעות כהודעות ב-Logs Explorer ומשויכות לשירות ולגרסה של App Engine שהפיקו אותן.
כדי לכתוב יומנים מובְנים, כותבים יומנים בצורה של שורה אחת של JSON שעבר סריאליזציה. כשמספקים יומן מובנה כמילון JSON, חלק מהשדות המיוחדים מוסרים מ-jsonPayload ונכתבים בשדה התואם ב-LogEntry שנוצר. לדוגמה, אם קובץ ה-JSON כולל מאפיין חומרה, הוא יוסר מ-jsonPayload ויופיע במקום זאת כחומרה של רשומת היומן.
מגבלות ידועות
כשמנתבים יומנים מ-sink ביומן ל-Cloud Storage, היעד ב-Cloud Storage מכיל רק יומני בקשות. App Engine כותב את יומני האפליקציות בתיקיות שונות.
רשומות ביומן שמנותבות נשמרות בקטגוריות של Cloud Storage בקבוצות לפי שעה. יכול להיות שיעברו שעתיים או שלוש עד שהרשומות הראשונות יתחילו להופיע.
בסביבה הגמישה ב-App Engine, הרישום מתבצע באופן אוטומטי. עם זאת, היומנים נאספים בפורמט שונה. היומנים לא יקובצו לפי בקשות, והיומנים מ-stdout ומ-stderr ייאספו בנפרד.
טבלת מיפוי UDM
| שדה ביומן | מיפוי UDM | היגיון |
|---|---|---|
| jsonPayload.logger, taskTypeName, jsonPayload.@type, jsonPayload.backendTargetProjectNumber, jsonPayload.cacheDecision, resource.labels.version_id, resource.labels.module_id, logName, spanId, trace, protoPayload.@type, labels.clone_id, operation.producer | additional.fields | מיזוג עם תוויות של מפתח/ערך שנוצרו מכל שדה |
| מטא-נתונים | מטא-נתונים | שם חדש במקום מטא-נתונים |
| receiveTimestamp | metadata.collected_timestamp | נותח באמצעות מסנן תאריכים עם RFC 3339 |
| metadata.event_type | הערך הוא USER_LOGIN אם יש has_principal, has_target, has_principal_user; NETWORK_CONNECTION אם יש has_principal ו-has_target; USER_UNCATEGORIZED אם אין has_principal ויש has_target; STATUS_UPDATE אם יש has_principal; USER_UNCATEGORIZED אם יש has_principal_user; אחרת, הערך הוא GENERIC_EVENT. | |
| metadata.extensions.auth.type | צריך להגדיר את הערך "AUTHTYPE_UNSPECIFIED" אם יש has_principal, has_target, has_principal_user | |
| insertId | metadata.product_log_id | הערך הועתק ישירות |
| httpRequest.requestMethod,protoPayload.method | network.http.method | הערך מ-httpRequest.requestMethod אם הוא לא ריק, אחרת הערך מ-protoPayload.method |
| httpRequest.userAgent | network.http.parsed_user_agent | ההמרה ל-parseduseragent בוצעה |
| httpRequest.status | network.http.response_code | הומר למחרוזת ואז למספר שלם |
| httpRequest.userAgent | network.http.user_agent | הערך הועתק ישירות |
| httpRequest.responseSize | network.received_bytes | הומר ל-uinteger |
| httpRequest.requestSize | network.sent_bytes | הומר ל-uinteger |
| משתמש (principal) | משתמש (principal) | השם שונה מהשם הראשי אם הוא לא ריק |
| protoPayload.host | principal.asset.hostname | הערך הועתק ישירות |
| httpRequest.serverIp, protoPayload.ip | principal.asset.ip | מוזג עם server_ip מ-httpRequest.serverIp או מ-protoPayload.ip |
| protoPayload.host | principal.hostname | הערך הועתק ישירות |
| httpRequest.serverIp, protoPayload.ip | principal.ip | מוזג עם server_ip מ-httpRequest.serverIp או מ-protoPayload.ip |
| protoPayload.appId | principal.resource.attribute.labels | מוזג עם התווית appId_label שמכילה את המפתח appId ואת הערך מהשדה |
| requestUser | principal.user.email_addresses | מוזג עם requestUser אם יש התאמה לתבנית האימייל |
| security_result | security_result | Merged from security_result |
| resource.labels.forwarding_rule_name | security_result.rule_labels | מוזג עם rule_label שמכיל את המפתח "forwarding_rule_name" ואת הערך מהשדה |
| severity | security_result.severity | מוגדר לערך חומרה אם יש התאמה ל- (?i)ERROR|CRITICAL, INFORMATIONAL אם יש התאמה ל- (?i)INFO, MEDIUM אם יש התאמה ל- (?i)WARN, LOW אם יש התאמה ל- (?i)DEBUG, אחרת UNKNOWN_SEVERITY |
| jsonPayload.statusDetails | security_result.summary | הערך הועתק ישירות |
| יעד | target | השם החדש הוא target אם הוא לא ריק |
| resource.labels.backend_service_name | target.application | הערך הועתק ישירות |
| httpRequest.remoteIp, jsonPayload.remoteIp | target.asset.ip | מוזג עם remote_ip שחולץ מ-httpRequest.remoteIp או מ-jsonPayload.remoteIp |
| resource.labels.project_id | target.cloud.project.name | הערך הועתק ישירות |
| httpRequest.remoteIp, jsonPayload.remoteIp | target.ip | מוזג עם remote_ip שחולץ מ-httpRequest.remoteIp או מ-jsonPayload.remoteIp |
| resource.labels.zone | target.resource.attribute.cloud.availability_zone | הערך הועתק ישירות |
| resource.labels.target_proxy_name, resource.labels.url_map_name | target.resource.attribute.labels | ממוזג עם תוויות מכל מקור |
| resource.type | target.resource.type | הערך הועתק ישירות |
| httpRequest.requestUrl | target.url | הערך הועתק ישירות |
| metadata.product_name | מגדירים את הערך "GCP_APP_ENGINE" | |
| metadata.vendor_name | מגדירים את הערך "GCP" | |
protoPayload.requestMetadata.callerIp |
event.idm.read_only_udm.principal.ip |
מופה מיומן השינויים |
protoPayload.requestMetadata.callerIp |
event.idm.read_only_udm.principal.asset.ip |
מופה מיומן השינויים |
protoPayload.authenticationInfo.principalEmail |
event.idm.read_only_udm.principal.user.email_addresses |
מופה מיומן השינויים |
protoPayload.methodName |
event.idm.read_only_udm.metadata.product_event_type |
מופה מיומן השינויים |
protoPayload.resourceName |
event.idm.read_only_udm.target.resource.name |
מופה מיומן השינויים |
protoPayload.serviceName |
event.idm.read_only_udm.target.application |
מופה מיומן השינויים |
protoPayload.resourceLocation.currentLocations |
event.idm.read_only_udm.target.resource.attribute.labels |
מופה מיומן השינויים |
protoPayload.authenticationInfo.oauthInfo.oauthClientId |
event.idm.read_only_udm.principal.resource.attribute.labels |
מופה מיומן השינויים |
protoPayload.requestMetadata.requestAttributes.time |
event.idm.read_only_udm.additional.fields |
מופה מיומן השינויים |
operation.first |
event.idm.read_only_udm.additional.fields |
מופה מיומן השינויים |
protoPayload.serviceData.updateService.request.updateMask |
event.idm.read_only_udm.additional.fields |
מופה מיומן השינויים |
operation.id |
event.idm.read_only_udm.additional.fields |
מופה מיומן השינויים |
protoPayload.authenticationInfo.serviceAccountKeyName |
event.idm.read_only_udm.additional.fields |
מופה מיומן השינויים |
protoPayload.authorizationInfo.permission |
event.idm.read_only_udm.security_result.rule_name |
מופה מיומן השינויים |
protoPayload.authorizationInfo.permissionType |
event.idm.read_only_udm.security_result.description |
מופה מיומן השינויים |
protoPayload.authorizationInfo.granted |
event.idm.read_only_udm.security_result.action |
מופה מיומן השינויים |
protoPayload.@type" , "labels.clone_id" and "operation.producer |
additional.labels |
מופה מיומן השינויים |
יומן שינויים
צפייה ביומן השינויים של המנתח הזה
הבעיה עדיין לא נפתרה? קבלת תשובות מחברי הקהילה וממומחי Google SecOps.