איסוף יומנים של שרת Proxy של OAuth2

נתמך ב:

במאמר הזה מוסבר איך להטמיע יומנים של OAuth2 Proxy ב-Google Security Operations באמצעות Google Cloud Storage V2.

‫OAuth2 Proxy הוא שרת proxy הפוך של CNCF Sandbox שמספק אימות באמצעות ספקי OAuth2/OIDC (Google,‏ GitHub,‏ Keycloak,‏ Azure AD ואחרים) כדי לאמת חשבונות לפי אימייל, דומיין או קבוצה. הוא יוצר יומני אימות (כניסה מוצלחת או כניסה שנכשלה), יומני בקשות (בקשות HTTP שעברו דרך פרוקסי עם זהות המשתמש) ויומני אפליקציה רגילים. מכיוון ש-OAuth2 Proxy פועל כקונטיינר ב-Kubernetes וכותב את כל היומנים ל-stdout, נעשה שימוש באוסף יומנים מקורי של Kubernetes ‏ (Fluentd) כדי להעביר יומנים לקטגוריה של GCS לצורך הטמעה ב-Google SecOps.

לפני שמתחילים

חשוב לוודא שמתקיימות הדרישות המוקדמות הבאות:

  • מופע של Google SecOps
  • פרויקט ב-GCP עם Cloud Storage API מופעל
  • הרשאות ליצירה ולניהול של מאגרי GCS
  • הרשאות לניהול מדיניות IAM בקטגוריות GCS
  • אשכול Kubernetes פעיל עם שרת proxy של OAuth2 שפרוס (דרך תרשים Helm או פריסה ידנית)
  • kubectl גישה לאשכול Kubernetes עם הרשאות ליצירת DaemonSets,‏ ConfigMaps,‏ Secrets ו-Namespaces
  • מפתח JSON של חשבון שירות ב-GCP עם הרשאה storage.objects.create בקטגוריית היעד ב-GCS

יצירת קטגוריה של Google Cloud Storage

  1. נכנסים אל מסוף Google Cloud.
  2. בוחרים פרויקט או יוצרים פרויקט חדש.
  3. בתפריט הניווט, עוברים אל Cloud Storage > Buckets.
  4. לוחצים על Create bucket.
  5. מספקים את פרטי ההגדרה הבאים:

    הגדרה ערך
    Name your bucket (שם הקטגוריה) מזינים שם ייחודי גלובלית (לדוגמה, oauth2-proxy-logs-bucket)
    סוג המיקום בוחרים לפי הצרכים (אזור, שני אזורים, מספר אזורים)
    Location בוחרים את המיקום (לדוגמה, us-central1).
    סוג אחסון (storage class) רגיל (מומלץ ליומנים שמתבצעת אליהם גישה לעיתים קרובות)
    בקרת גישה אחיד (מומלץ)
    כלים להגנה אופציונלי: הפעלת ניהול גרסאות של אובייקטים או מדיניות שמירת נתונים
  6. לוחצים על יצירה.

הגדרת רישום ביומן של שרת Proxy מסוג OAuth2

‫OAuth2 Proxy כותב שלושה סוגים של יומנים ל-stdout: יומנים רגילים, יומני אימות ויומני בקשות. כל שלושת הסוגים מופעלים כברירת מחדל. כדי לוודא שהיומנים מכילים את כל הפרטים שרלוונטיים לאבטחה, צריך להגדיר את OAuth2 Proxy עם דגלי הרישום ביומן הבאים.

אפשרות 1: הגדרה באמצעות ערכי תרשים Helm

  • אם OAuth2 Proxy נפרס באמצעות תרשים Helm הרשמי, צריך להוסיף את הקוד הבא לקובץ values.yaml:

    config:
      configFile: |-
        standard_logging = true
        auth_logging = true
        request_logging = true
        silence_ping_logging = true
            standard_logging_format = "[{{.Timestamp}}] [{{.File}}] {{.Message}}"
        auth_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}"
        request_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"
    
  • מחילים את ערכי Helm המעודכנים:

    helm upgrade oauth2-proxy oauth2-proxy/oauth2-proxy -f values.yaml -n <your-namespace>
    

אפשרות 2: הגדרה באמצעות תגי שורת פקודה

  • אם OAuth2 Proxy נפרס באמצעות מניפסט של פריסת Kubernetes, צריך להוסיף את הארגומנטים הבאים למפרט של הקונטיינר:

    args:
      - --standard-logging=true
      - --auth-logging=true
      - --request-logging=true
      - --silence-ping-logging=true
    

אפשרות 3: הגדרה באמצעות משתני סביבה

  • מגדירים את משתני הסביבה הבאים בקונטיינר של OAuth2 Proxy:

    env:
      - name: OAUTH2_PROXY_STANDARD_LOGGING
        value: "true"
      - name: OAUTH2_PROXY_AUTH_LOGGING
        value: "true"
      - name: OAUTH2_PROXY_REQUEST_LOGGING
        value: "true"
      - name: OAUTH2_PROXY_SILENCE_PING_LOGGING
        value: "true"
    

אימות הרישום ביומן של OAuth2 Proxy

  • אחרי שמחילים את ההגדרה, מוודאים ש-OAuth2 Proxy יוצר יומנים:

    kubectl logs -l app=oauth2-proxy -n <your-namespace> --tail=20
    
  • הפלט כולל שורות שדומות לאלה:

    10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:00] [AuthSuccess] Authenticated via OAuth2
    10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:01] example.com GET 10.0.0.5:8080 "/dashboard" HTTP/1.1 "Mozilla/5.0" 200 1234 0.005
    

יצירת חשבון שירות ב-GCP עבור Fluentd

‫Fluentd דורש חשבון שירות ב-GCP עם גישת כתיבה לקטגוריה של GCS.

  1. במסוף GCP, עוברים אל IAM & Admin > Service Accounts.
  2. לוחצים על יצירת חשבון שירות.
  3. מספקים את פרטי ההגדרה הבאים:
    • שם חשבון השירות: מזינים fluentd-gcs-writer
    • תיאור חשבון השירות: מזינים Service account for Fluentd to write OAuth2 Proxy logs to GCS
  4. לוחצים על יצירה והמשך.
  5. בקטע Grant this service account access to project (מתן גישה של חשבון השירות הזה לפרויקט):
    1. לוחצים על בחירת תפקיד.
    2. מחפשים את Storage Object Admin ולוחצים עליו.
  6. לוחצים על Continue.
  7. לוחצים על סיום.

יצירה והורדה של מפתח לחשבון שירות

  1. ברשימה Service Accounts, לוחצים על חשבון השירות fluentd-gcs-writer.
  2. עוברים לכרטיסייה Keys.
  3. לוחצים על Add Key > Create new key.
  4. בוחרים באפשרות JSON בתור סוג המפתח.
  5. לוחצים על יצירה.
  6. שומרים את קובץ מפתח ה-JSON שהורדתם במקום בטוח. הקובץ הזה ישמש בשלב הבא.

יצירת סוד של Kubernetes בשביל המפתח של חשבון השירות

  • יוצרים סוד של Kubernetes שמכיל את המפתח של חשבון השירות ב-GCP במרחב השמות שבו יופעל Fluentd:

    kubectl create namespace logging
    kubectl create secret generic fluentd-gcs-key \
      --from-file=service-account-key.json=<path-to-downloaded-key>.json \
      -n logging
    

פריסת Fluentd DaemonSet לאיסוף יומנים של OAuth2 Proxy

פורסים את Fluentd כ-DaemonSet באשכול Kubernetes כדי לאסוף יומני קונטיינרים מתרמילי OAuth2 Proxy ולהעביר אותם לדלי GCS.

יצירת ConfigMap של Fluentd

  • יוצרים קובץ בשם fluentd-configmap.yaml עם התוכן הבא:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: fluentd-gcs-config
      namespace: logging
    data:
      fluent.conf: |
        <source>
          @type tail
          read_from_head true
          tag kubernetes.*
          path /var/log/containers/*oauth2-proxy*.log
          pos_file /var/log/fluentd-oauth2-proxy.log.pos
          <parse>
            @type regexp
            expression /^(?<time>[^ ]+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
            time_format %Y-%m-%dT%H:%M:%S.%N%z
          </parse>
        </source>
    
        <filter kubernetes.**>
          @type kubernetes_metadata
          @id filter_kube_metadata
        </filter>
    
        <match kubernetes.**>
          @type gcs
          project YOUR_GCP_PROJECT_ID
          keyfile /etc/secrets/service-account-key.json
          bucket oauth2-proxy-logs-bucket
          path oauth2-proxy-logs/%Y/%m/%d/
          object_key_format %{path}%{time_slice}_%{hostname}_%{index}.%{file_extension}
          <buffer tag,time>
            @type file
            path /var/log/fluentd/gcs
            timekey 300
            timekey_wait 60
            timekey_use_utc true
            chunk_limit_size 10MB
          </buffer>
          <format>
            @type json
          </format>
        </match>
    

מחליפים את הערכים הבאים:

  • ‫YOUR_GCP_PROJECT_ID: מזהה הפרויקט ב-GCP (לדוגמה, my-project-123456)
  • ‫oauth2-proxy-logs-bucket: השם של קטגוריית GCS שנוצרה קודם

  • מחילים את ה-ConfigMap:

    kubectl apply -f fluentd-configmap.yaml
    

יצירת Fluentd DaemonSet

  • יוצרים קובץ בשם fluentd-daemonset.yaml עם התוכן הבא:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: fluentd-gcs
      namespace: logging
      labels:
        k8s-app: fluentd-gcs
    spec:
      selector:
        matchLabels:
          k8s-app: fluentd-gcs
      template:
        metadata:
          labels:
            k8s-app: fluentd-gcs
        spec:
          tolerations:
            - key: node-role.kubernetes.io/control-plane
              effect: NoSchedule
            - key: node-role.kubernetes.io/master
              effect: NoSchedule
          containers:
            - name: fluentd-gcs
              image: fluent/fluentd-kubernetes-daemonset:v1-debian-gcs
              resources:
                limits:
                  memory: 512Mi
                requests:
                  cpu: 100m
                  memory: 200Mi
              volumeMounts:
                - name: fluentd-gcs-config-volume
                  mountPath: /fluentd/etc/fluent.conf
                  subPath: fluent.conf
                  readOnly: true
                - name: fluentd-gcs-secrets-volume
                  mountPath: /etc/secrets/service-account-key.json
                  subPath: service-account-key.json
                  readOnly: true
                - name: varlog
                  mountPath: /var/log
                - name: dockercontainerlogdirectory
                  mountPath: /var/log/pods
                  readOnly: true
                - name: fluentd-buffer
                  mountPath: /var/log/fluentd
          terminationGracePeriodSeconds: 30
          volumes:
            - name: fluentd-gcs-config-volume
              configMap:
                name: fluentd-gcs-config
            - name: fluentd-gcs-secrets-volume
              secret:
                secretName: fluentd-gcs-key
            - name: varlog
              hostPath:
                path: /var/log
            - name: dockercontainerlogdirectory
              hostPath:
                path: /var/log/pods
            - name: fluentd-buffer
              emptyDir: {}
    
  • מחילים את DaemonSet:

    kubectl apply -f fluentd-daemonset.yaml
    

אימות הפריסה של Fluentd

  1. מוודאים שרכיבי ה-pod של Fluentd פועלים בכל צומת:

    kubectl get pods -n logging -l k8s-app=fluentd-gcs
    
  2. בודקים ביומנים של Fluentd אם הכתיבה ל-GCS הצליחה:

    kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20
    
  3. מוודאים שקבצי היומן מופיעים בקטגוריה GCS:

    1. נכנסים אל Cloud Storage > Buckets במסוף GCP.
    2. לוחצים על ה-bucket (לדוגמה, oauth2-proxy-logs-bucket).
    3. עוברים לתיקייה oauth2-proxy-logs/.
    4. בודקים שקיימים קובצי .json עם חותמות זמן עדכניות.

אחזור חשבון השירות של Google SecOps

‫Google SecOps משתמש בחשבון שירות ייחודי כדי לקרוא נתונים מהקטגוריה של GCS. צריך להעניק לחשבון השירות הזה גישה לדלי.

קבלת כתובת האימייל בחשבון השירות

  1. עוברים אל SIEM Settings > Feeds (הגדרות SIEM > פידים).
  2. לוחצים על הוספת פיד חדש.
  3. לוחצים על Configure a single feed (הגדרת פיד יחיד).
  4. בשדה שם הפיד, מזינים שם לפיד (לדוגמה, OAuth2 Proxy Logs).
  5. בוחרים באפשרות Google Cloud Storage V2 בתור סוג המקור.
  6. בוחרים באפשרות Kubernetes Auth Proxy בתור סוג היומן.
  7. לוחצים על קבלת חשבון שירות. תוצג כתובת האימייל בחשבון שירות ייחודי, לדוגמה:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. מעתיקים את כתובת האימייל הזו לשימוש בשלב הבא.

  9. לוחצים על הבא.

  10. מציינים ערכים לפרמטרים הבאים של הקלט:

    • כתובת ה-URL של קטגוריית האחסון: מזינים את ה-URI של קטגוריית GCS עם נתיב הקידומת:

      gs://oauth2-proxy-logs-bucket/oauth2-proxy-logs/
      
    • מחליפים את oauth2-proxy-logs-bucket בשם של קטגוריית GCS.

    • אפשרות למחיקת המקור: בוחרים את אפשרות המחיקה לפי ההעדפה שלכם:

      • אף פעם: לא נמחקים קבצים אחרי ההעברה (מומלץ לבדיקות).
      • מחיקת קבצים שהועברו: הקבצים נמחקים אחרי שההעברה מסתיימת.
      • מחיקת קבצים שהועברו וספריות ריקות: מחיקת קבצים וספריות ריקות אחרי שההעברה מסתיימת בהצלחה.

    • הגיל המקסימלי של הקובץ: כולל קבצים ששונו במספר הימים האחרון (ברירת המחדל היא 180 ימים)

    • מרחב השמות של הנכס: מרחב השמות של הנכס

    • תוויות להוספה: התווית שתתווסף לאירועים מהפיד הזה

  11. לוחצים על הבא.

  12. בודקים את ההגדרות של הפיד החדש במסך סיום ולוחצים על שליחה.

הענקת הרשאות IAM לחשבון השירות של Google SecOps

לחשבון השירות של Google SecOps צריך להיות התפקיד צפייה באובייקט אחסון בקטגוריה של GCS.

  1. עוברים אל Cloud Storage > Buckets (קטגוריות).
  2. לוחצים על שם הקטגוריה (לדוגמה, oauth2-proxy-logs-bucket).
  3. עוברים לכרטיסייה Permissions.
  4. לוחצים על הענקת גישה.
  5. מספקים את פרטי ההגדרה הבאים:
    • הוספת ישויות מורשות: מדביקים את כתובת האימייל בחשבון השירות של Google SecOps (לדוגמה, chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com)
    • הקצאת תפקידים: בוחרים באפשרות צפייה באובייקטים באחסון.
  6. לוחצים על Save.

הפניה ליומן של OAuth2 Proxy

בטבלה הבאה מתוארים שדות היומן שנוצרים על ידי OAuth2 Proxy והרלוונטיות שלהם לאבטחה:

שדות ביומן האימות

שדה דוגמה תיאור
לקוח 10.0.0.1 כתובת ה-IP של הלקוח או של המחשב המרוחק (משתמש בכותרת X-Real-IP אם --reverse-proxy=true)
RequestID 00010203-0405-4607-8809-0a0b0c0d0e0f מזהה הבקשה מהכותרת X-Request-Id (מזהה ייחודי אוניברסלי (UUID) אקראי אם הכותרת ריקה)
שם משתמש user@example.com כתובת האימייל או שם המשתמש של בקשת האימות
חותמת הזמן 2024/01/15 10:30:00 התאריך והשעה של אירוע האימות
סטטוס AuthSuccess תוצאת האימות: AuthSuccess,‏ AuthFailure או AuthError
הודעה Authenticated via OAuth2 פרטים על ניסיון האימות

שדות ביומן הבקשות

שדה דוגמה תיאור
לקוח 10.0.0.1 כתובת ה-IP של הלקוח או של המחשב המרוחק
RequestID 00010203-0405-4607-8809-0a0b0c0d0e0f מזהה בקשה
שם משתמש user@example.com כתובת האימייל של המשתמש המאומת
חותמת הזמן 2024/01/15 10:30:01 התאריך והשעה של שליחת הבקשה
מארח app.example.com הערך של כותרת המארח
RequestMethod GET שיטת בקשת HTTP
Upstream 10.0.0.5:8080 השרת במעלה הזרם שטיפל בבקשה
RequestURI /dashboard נתיב ה-URI של הבקשה
פרוטוקול HTTP/1.1 פרוטוקול הבקשה
UserAgent Mozilla/5.0 תיאור מלא של סוכן המשתמש
StatusCode 200 קוד הסטטוס של תגובת HTTP
ResponseSize 1234 גודל התשובה בבייטים
RequestDuration 0.005 זמן עיבוד הבקשה בשניות

טבלת מיפוי UDM

שדה ביומן מיפוי UDM היגיון
מידע כללי מידע כללי מידע על האירוע
http_req_id_field additional.fields שדות נוספים שלא נכללים בסכימת UDM רגילה
http_req_path_field additional.fields
k8s_pod_app_field additional.fields
k8s_pod_template_hash_field additional.fields
k8s_pod_tls_mode_field additional.fields
k8s_pod_canonical_revision_field additional.fields
k8s_pod_canonical_name_field additional.fields
pod_name additional.fields
cntnr_name additional.fields
destination_canonical_revision additional.fields
requested_server additional.fields
nodename_label additional.fields
componentName_label additional.fields
componentVersion_label additional.fields
azureResourceID_label additional.fields
producer_label additional.fields
first_label additional.fields
last_label additional.fields
meta_name additional.fields
resource_version_label additional.fields
request_apiVersion additional.fields
request_kind_label additional.fields
request_type_label additional.fields
response_apiVersion additional.fields
response_kind_label additional.fields
response_type_label additional.fields
jsonPayload.message metadata.description תיאור האירוע
event_type metadata.event_type סוג האירוע
labels.request_id metadata.product_log_id מזהה יומן ספציפי למוצר
insertId metadata.product_log_id
jsonPayload.chartVersion metadata.product_version גרסת המוצר
httpRequest.protocol network.application_protocol פרוטוקול האפליקציה שנעשה בו שימוש בחיבור לרשת
network.direction network.direction כיוון התנועה ברשת
httpRequest.requestMethod network.http.method שיטת HTTP
http_method network.http.method
httpRequest.status network.http.response_code קוד תגובת HTTP
httpRequest.userAgent network.http.user_agent סוכן משתמש ב-HTTP
requestMetadata.callerSuppliedUserAgent network.http.user_agent
labels.protocol network.ip_protocol פרוטוקול IP
httpRequest.responseSize network.received_bytes מספר הבייטים שהתקבלו
labels.total_received_bytes network.received_bytes
httpRequest.requestSize network.sent_bytes מספר הבייטים שנשלחו
labels.total_sent_bytes network.sent_bytes
jsonPayload.session network.session_id מזהה הסשן
labels.service_authentication_policy network.tls.cipher סט אלגוריתמים להצפנה (cipher suite) ב-TLS
משתמש (principal) משתמש (principal) הישות העיקרית שמעורבת באירוע
principal_hostname principal.hostname שם המארח של הישות המורשית
prin_userid principal.user.userid מזהה המשתמש של הישות המורשית
security_result security_result תוצאה של הערכת אבטחה
target target ישות היעד שמעורבת באירוע
target_hostname target.hostname שם המארח של היעד
resource_sub_type target.resource.resource_subtype סוג המשנה של משאב היעד
target_userid target.user.userid מזהה המשתמש של היעד
metadata.product_name metadata.product_name שם המוצר
metadata.vendor_name metadata.vendor_name שם הספק

יומן שינויים

צפייה ביומן השינויים של מנתח התוכן הזה

הבעיה עדיין לא נפתרה? קבלת תשובות מחברי הקהילה וממומחי Google SecOps.