איסוף יומנים של File Scanning Framework
גרסת כלי הניתוח: 3.0
במאמר הזה מוסבר איך להטמיע יומנים של File Scanning Framework ב-Google Security Operations באמצעות Google Cloud Storage V2.
File Scanning Framework (FSF) הוא פתרון סריקת קבצים רקורסיבי מודולרי בקוד פתוח שפותח על ידי Emerson Electric Co.. הפתרון הזה משתמש בארכיטקטורת לקוח-שרת כדי לנתח קבצים וליצור תוצאות סריקה מפורטות בפורמט JSON, כולל מטא-נתונים של קבצים, התאמות של חתימות YARA, אובייקטים משניים שחולצו ומטא-נתונים ספציפיים למודול.
לפני שמתחילים
חשוב לוודא שמתקיימות הדרישות המוקדמות הבאות:
- מופע של Google SecOps
- פרויקט ב-GCP עם Cloud Storage API מופעל
- הרשאות ליצירה ולניהול של מאגרי GCS
- הרשאות לניהול מדיניות IAM בקטגוריות GCS
- מופע של שרת FSF שנפרס עם גישת כתיבה לספריית היומן
- גישת רוט (Root) או גישת sudo למארח השרת של FSF
יצירת קטגוריה של Google Cloud Storage
- נכנסים אל מסוף Google Cloud.
- בוחרים פרויקט או יוצרים פרויקט חדש.
- בתפריט הניווט, עוברים אל Cloud Storage > Buckets.
- לוחצים על Create bucket.
מספקים את פרטי ההגדרה הבאים:
הגדרה ערך Name your bucket (שם הקטגוריה) מזינים שם ייחודי בעולם (לדוגמה, fsf-logs-secops).סוג המיקום בוחרים לפי הצרכים (אזור, שני אזורים, מספר אזורים). Location בוחרים את המיקום (לדוגמה, us-central1).סוג אחסון (storage class) רגיל (מומלץ ליומנים שניגשים אליהם לעיתים קרובות). בקרת גישה אחיד (מומלץ). כלים להגנה אופציונלי: הפעלת ניהול גרסאות של אובייקטים או מדיניות שמירת נתונים. לוחצים על יצירה.
הגדרת ספריית פלט של יומני FSF
FSF כותב את תוצאות הסריקה בפורמט JSON לספריית יומן שניתנת להגדרה. מגדירים ספרייה ייעודית להעברה של נתונים ל-Google SecOps.
- מתחברים למארח השרת של FSF באמצעות SSH.
פותחים את קובץ ההגדרות של שרת ה-FSF:
sudo nano /opt/fsf/fsf-server/conf/config.pyמאתרים את המילון
SCANNER_CONFIG.מעדכנים את הפרמטר
LOG_PATHלספרייה ייעודית:SCANNER_CONFIG = { 'LOG_PATH': '/var/log/fsf', 'YARA_PATH': '/opt/fsf/fsf-server/yara/rules.yara', 'PID_PATH': '/tmp/scanner.pid', 'EXPORT_PATH': '/tmp', 'TIMEOUT': 60, 'MAX_DEPTH': 10 }שומרים את הקובץ וסוגרים אותו.
יוצרים את ספריית היומן עם ההרשאות המתאימות:
sudo mkdir -p /var/log/fsf sudo chown -R fsf:fsf /var/log/fsf sudo chmod 755 /var/log/fsfמפעילים מחדש את שרת FSF כדי להחיל את השינויים:
sudo systemctl restart fsfמוודאים ש-FSF כותב יומנים לספרייה החדשה:
ls -lh /var/log/fsf/
התקנה והגדרה של Fluentd
Fluentd יעקוב אחרי קובצי היומן של FSF ויעביר אותם ל-Google Cloud Storage.
התקנת Fluentd
במארח השרת של FSF, מתקינים את Fluentd (td-agent):
curl -fsSL https://toolbelt.treasuredata.com/sh/install-ubuntu-jammy-td-agent4.sh | shמתקינים את פלאגין הפלט של GCS:
sudo td-agent-gem install fluent-plugin-gcsמאמתים את התקנת הפלאגין:
td-agent-gem list | grep fluent-plugin-gcs
יצירת חשבון שירות ב-GCP עבור Fluentd
- במסוף GCP, עוברים אל IAM & Admin > Service Accounts.
- לוחצים על יצירת חשבון שירות.
- מספקים את פרטי ההגדרה הבאים:
- שם חשבון השירות: מזינים
fsf-fluentd-shipper. - תיאור חשבון השירות: מזינים
Service account for Fluentd to ship FSF logs to GCS.
- שם חשבון השירות: מזינים
- לוחצים על יצירה והמשך.
- בקטע Grant this service account access to project (מתן גישה של חשבון השירות הזה לפרויקט):
- לוחצים על בחירת תפקיד.
- מחפשים את Storage Object Admin ולוחצים עליו.
- לוחצים על Continue.
- לוחצים על סיום.
יצירת מפתח של חשבון שירות
- ברשימה Service Accounts (חשבונות שירות), לוחצים על חשבון השירות (
fsf-fluentd-shipper). - עוברים לכרטיסייה Keys.
- לוחצים על Add Key > Create new key.
- בוחרים באפשרות JSON בתור סוג המפתח.
- לוחצים על יצירה.
- קובץ ה-JSON עם המפתח יירד למחשב.
מעבירים את קובץ המפתח למארח השרת של FSF:
scp /path/to/downloaded-key.json user@fsf-server:/etc/td-agent/gcp-key.jsonמגדירים הרשאות מתאימות בקובץ המפתח:
sudo chown td-agent:td-agent /etc/td-agent/gcp-key.json sudo chmod 600 /etc/td-agent/gcp-key.json
מתן הרשאות IAM בקטגוריית GCS
- עוברים אל Cloud Storage > Buckets (קטגוריות).
- לוחצים על שם הקטגוריה (
fsf-logs-secops). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מספקים את פרטי ההגדרה הבאים:
- Add principals (הוספת ישויות מורשות): מזינים את כתובת האימייל בחשבון השירות (לדוגמה,
fsf-fluentd-shipper@PROJECT_ID.iam.gserviceaccount.com). - הקצאת תפקידים: בוחרים באפשרות אדמין של אובייקטים באחסון.
- Add principals (הוספת ישויות מורשות): מזינים את כתובת האימייל בחשבון השירות (לדוגמה,
- לוחצים על Save.
הגדרת Fluentd
במארח של שרת FSF, יוצרים קובץ הגדרה של Fluentd:
sudo nano /etc/td-agent/td-agent.confמוסיפים את ההגדרה הבאה:
# Tail FSF JSON logs <source> @type tail path /var/log/fsf/*.log pos_file /var/log/td-agent/fsf.log.pos tag fsf.scan read_from_head true <parse> @type json time_key timestamp time_format %Y-%m-%dT%H:%M:%S.%L%z </parse> </source> # Ship to Google Cloud Storage <match fsf.scan> @type gcs project YOUR_GCP_PROJECT_ID keyfile /etc/td-agent/gcp-key.json bucket fsf-logs-secops object_key_format %{path}%{time_slice}_%{index}.%{file_extension} path fsf-logs/ <buffer tag,time> @type file path /var/log/td-agent/buffer/gcs timekey 3600 timekey_wait 10m timekey_use_utc true chunk_limit_size 10MB </buffer> <format> @type json </format> store_as json auto_create_bucket false </match>מחליפים את
YOUR_GCP_PROJECT_IDבמזהה הפרויקט בפועל ב-GCP.שומרים את הקובץ וסוגרים אותו.
יוצרים את ספריית המאגר:
sudo mkdir -p /var/log/td-agent/buffer/gcs sudo chown -R td-agent:td-agent /var/log/td-agent/bufferמפעילים מחדש את Fluentd כדי להחיל את ההגדרה:
sudo systemctl restart td-agentמפעילים את Fluentd כדי להתחיל בהפעלה:
sudo systemctl enable td-agentמוודאים ש-Fluentd פועל:
sudo systemctl status td-agent
אימות של העברת יומנים
בודקים אם יש שגיאות ביומני Fluentd:
sudo tail -f /var/log/td-agent/td-agent.logמפעילים סריקת FSF לצורך בדיקה כדי ליצור יומנים:
echo "test content" > /tmp/test.txt /opt/fsf/fsf-client/fsf_client.py /tmp/test.txt --suppress-reportממתינים דקה או שתיים עד ש-Fluentd יעבד את היומנים וישלח אותם.
במסוף GCP, עוברים אל Cloud Storage > Buckets.
לוחצים על שם הקטגוריה (
fsf-logs-secops).עוברים לקידומת
fsf-logs/.מוודאים שקובצי ה-JSON נוצרים עם חותמות זמן.
מורידים קובץ ובודקים אותו כדי לוודא שהוא מכיל תוצאות סריקה של FSF בפורמט JSON.
אחזור חשבון השירות של Google SecOps
Google SecOps משתמש בחשבון שירות ייחודי כדי לקרוא נתונים מהקטגוריה של GCS. צריך להעניק לחשבון השירות הזה גישה לדלי.
קבלת כתובת האימייל בחשבון השירות
- עוברים אל SIEM Settings > Feeds (הגדרות SIEM > פידים).
- לוחצים על הוספת פיד חדש.
- לוחצים על Configure a single feed (הגדרת פיד יחיד).
- בשדה שם הפיד, מזינים שם לפיד (לדוגמה,
FSF File Scanning Logs). - בוחרים באפשרות Google Cloud Storage V2 בתור סוג המקור.
- בוחרים באפשרות File Scanning Framework (מסגרת לסריקת קבצים) בתור סוג היומן.
לוחצים על קבלת חשבון שירות. תוצג כתובת האימייל בחשבון שירות ייחודי, לדוגמה:
secops-12345678@secops-gcp-prod.iam.gserviceaccount.comמעתיקים את כתובת האימייל לשימוש בשלב הבא.
לוחצים על הבא.
מציינים ערכים לפרמטרים הבאים של הקלט:
כתובת ה-URL של קטגוריית האחסון: מזינים את ה-URI של קטגוריית GCS עם נתיב הקידומת:
gs://fsf-logs-secops/fsf-logs/אפשרות למחיקת המקור: בוחרים את אפשרות המחיקה לפי ההעדפה שלכם:
- אף פעם: לא נמחקים קבצים אחרי ההעברה (מומלץ לבדיקות).
- מחיקת קבצים שהועברו: הקבצים נמחקים אחרי שההעברה מסתיימת.
מחיקת קבצים שהועברו וספריות ריקות: מחיקת קבצים וספריות ריקות אחרי שההעברה מסתיימת בהצלחה.
הגיל המקסימלי של הקובץ: כולל קבצים ששונו במספר הימים האחרון (ברירת המחדל היא 180 ימים).
מרחב השמות של הנכס: מרחב השמות של הנכס.
תוויות להוספה: התווית שתתווסף לאירועים מהפיד הזה.
לוחצים על הבא.
בודקים את ההגדרות של הפיד החדש במסך סיום ולוחצים על שליחה.
הענקת הרשאות IAM לחשבון השירות של Google SecOps
לחשבון השירות של Google SecOps צריך להיות התפקיד צפייה באובייקט אחסון בקטגוריה של GCS.
- עוברים אל Cloud Storage > Buckets (קטגוריות).
- לוחצים על שם הקטגוריה (
fsf-logs-secops). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מספקים את פרטי ההגדרה הבאים:
- Add principals (הוספת ישויות מורשות): מדביקים את כתובת האימייל בחשבון של חשבון השירות של Google SecOps.
- הקצאת תפקידים: בוחרים באפשרות צפייה באובייקט אחסון.
- לוחצים על Save.
אימות ההטמעה
- מחכים 10-15 דקות עד שההעברה הראשונית תסתיים.
- ב-Google SecOps, עוברים אל SIEM Settings > Feeds (הגדרות SIEM > פידים).
- מאתרים את הפיד (
FSF File Scanning Logs). - מוודאים שהסטטוס הוא פעיל.
- לוחצים על שם הפיד כדי לראות את מדדי ההטמעה.
- מוודאים שהערך של Events ingested (אירועים שהמערכת קולטת) עולה.
- עוברים אל חיפוש ב-Google SecOps.
מריצים שאילתת חיפוש כדי לוודא שיומני FSF נקלטים:
metadata.log_type = "FILE_SCANNING_FRAMEWORK"מוודאים שתוצאות הסריקה של FSF מופיעות בתוצאות החיפוש.
פתרון בעיות
לא מופיעים יומנים ב-GCS
בודקים ש-FSF כותב יומנים אל
/var/log/fsf/:ls -lh /var/log/fsf/ tail -f /var/log/fsf/*.logבודקים אם יש שגיאות ביומני Fluentd:
sudo tail -f /var/log/td-agent/td-agent.logמוודאים שמפתח חשבון השירות ב-GCP תקין ושיש לו את ההרשאות הנכונות.
מוודאים ששם הקטגוריה בהגדרות של Fluentd זהה לשם הקטגוריה בפועל.
שגיאות הרשאה ב-Fluentd
- מוודאים שלחשבון השירות (
fsf-fluentd-shipper) יש את התפקיד אדמין של אובייקטים באחסון בקטגוריה. - בודקים שנתיב המפתח בהגדרות של Fluentd נכון.
מוודאים שלקובץ המפתח יש בעלות והרשאות נכונות:
ls -l /etc/td-agent/gcp-key.json
יומנים לא מוטמעים ב-Google SecOps
- מוודאים שלחשבון השירות של Google SecOps יש תפקיד צפייה באובייקט אחסון בקטגוריה.
- בודקים שערך ה-URI של מאגר הנתונים בהגדרות הפיד נכון וכולל את התו האחרון של הנתיב (סלאש).
- מוודאים שהקבצים קיימים בקטגוריה ב-GCS בנתיב הקידומת שצוין.
- בודקים את סטטוס הפיד בהגדרות SIEM > פידים כדי לראות אם יש הודעות שגיאה.
יומני FSF לא בפורמט הצפוי
- מוודאים ש-FSF מוגדר לכתוב פלט JSON (התנהגות ברירת המחדל).
- בודקים שהקטע Fluentd
<parse>מוגדר עם@type json. בודקים קובץ יומן באופן ידני כדי לוודא שהוא מכיל קובץ JSON תקין:
head -n 1 /var/log/fsf/*.log | jq .
טבלת מיפוי UDM
| שדה ביומן | מיפוי UDM | היגיון |
|---|---|---|
| CompressType_label, compressed_parents | about.labels | מוזג מ-CompressType_label (מפתח Compress Type, ערך מ-Object.EXTRACT_ZIP.Object_0.Compress Type אם ההודעה מכילה Compress Type) ומ-compressed_parents (מפתח Compressed Parent Files, שרשור מ-Object.EXTRACT_ZIP.Object_0.META_VT_CACHE.vt_data.additional_info.compressed_parents) |
| Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.MD5, Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.MD5, Object.EXTRACT_SWF.META_BASIC_INFO.MD5, Object.EXTRACT_GZIP.META_BASIC_INFO.MD5, Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.MD5 | intermediary.file.md5 | הערך מ-Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.MD5 אם EXTRACT_EMBEDDED קיים, אחרת הערך מ-Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.MD5 אם EXTRACT_ZIP קיים, אחרת הערך מ-Object.EXTRACT_SWF.META_BASIC_INFO.MD5 אם EXTRACT_SWF קיים, אחרת הערך מ-Object.EXTRACT_GZIP.META_BASIC_INFO.MD5 אם EXTRACT_GZIP קיים, אחרת הערך מ-Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.MD5 |
| Object.EXTRACT_EMBEDDED.Object_0.Description | intermediary.file.mime_type | הערך הועתק ישירות |
| Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.SHA1, Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.SHA1, Object.EXTRACT_SWF.META_BASIC_INFO.SHA1, Object.EXTRACT_GZIP.META_BASIC_INFO.SHA1, Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.SHA1 | intermediary.file.sha1 | הערך מ-Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.SHA1 אם EXTRACT_EMBEDDED קיים, אחרת הערך מ-Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.SHA1 אם EXTRACT_ZIP קיים, אחרת הערך מ-Object.EXTRACT_SWF.META_BASIC_INFO.SHA1 אם EXTRACT_SWF קיים, אחרת הערך מ-Object.EXTRACT_GZIP.META_BASIC_INFO.SHA1 אם EXTRACT_GZIP קיים, אחרת הערך מ-Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.SHA1 |
| Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.SHA256, Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.SHA256, Object.EXTRACT_SWF.META_BASIC_INFO.SHA256, Object.EXTRACT_GZIP.META_BASIC_INFO.SHA256, Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.SHA256 | intermediary.file.sha256 | הערך מ-Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.SHA256 אם EXTRACT_EMBEDDED קיים, אחרת הערך מ-Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.SHA256 אם EXTRACT_ZIP קיים, אחרת הערך מ-Object.EXTRACT_SWF.META_BASIC_INFO.SHA256 אם EXTRACT_SWF קיים, אחרת הערך מ-Object.EXTRACT_GZIP.META_BASIC_INFO.SHA256 אם EXTRACT_GZIP קיים, אחרת הערך מ-Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.SHA256 |
| Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.Size, Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.Size, Object.EXTRACT_SWF.META_BASIC_INFO.Size, Object.EXTRACT_GZIP.META_BASIC_INFO.Size, Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.Size | intermediary.file.size | הערך מ-Object.EXTRACT_EMBEDDED.Object_0.META_BASIC_INFO.Size אם EXTRACT_EMBEDDED קיים, אחרת הערך מ-Object.EXTRACT_ZIP.Object_0.META_BASIC_INFO.Size אם EXTRACT_ZIP קיים, אחרת הערך מ-Object.EXTRACT_SWF.META_BASIC_INFO.Size אם EXTRACT_SWF קיים, אחרת הערך מ-Object.EXTRACT_GZIP.META_BASIC_INFO.Size אם EXTRACT_GZIP קיים, אחרת הערך מ-Object.EXTRACT_CAB.Object_0.META_BASIC_INFO.Size. הערך הזה נלקח אחרי הסרת התו .* בסוף והמרה למספר שלם לא שלילי. |
| Object.EXTRACT_ZIP.Object_0.META_VT_CACHE.vt_data.scan_id | intermediary.resource.id | הערך הועתק ישירות |
| Object.EXTRACT_ZIP.Object_0.META_VT_CACHE.vt_data.permalink | intermediary.url | הערך הועתק ישירות |
| Object.META_EMERSON_INFO.results | intermediary.user.email_addresses | מוזג מ-matched_email במערך התוצאות |
| Summary.Observations | metadata.description | המחרוזת נוצרה מאיחוד של מערך עם מפריד של ', ', והפסיק המוביל הוסר |
| זמן הסריקה | metadata.event_timestamp | ההמרה בוצעה באמצעות מסנן תאריכים בפורמט yyyy-MM-dd HH:mm:ss |
| מקור | metadata.event_type | הערך הוא SCAN_FILE אם המקור לא ריק, אחרת GENERIC_EVENT |
| Object.META_VT_CACHE._id | metadata.product_log_id | הערך הועתק ישירות |
| result.ad_data.message | network.http.response_code | הערך מחולץ כמספר שלם באמצעות תבנית grok INT מהתוצאה result.ad_data.message |
| מקור | principal.hostname | הערך הועתק ישירות |
| Object.META_EMERSON_INFO.result_summary, Object.EXTRACT_ZIP.Object_0.META_VT_CACHE.vt_data.verbose_msg | security_result.summary | הערך מוגדר ל-Object.META_EMERSON_INFO.result_summary אם הוא קיים, אחרת ל-Object.EXTRACT_ZIP.Object_0.META_VT_CACHE.vt_data.verbose_msg |
| שם קובץ | target.file.full_path | הערך הועתק ישירות |
| Object.META_BASIC_INFO.MD5 | target.file.md5 | הערך הועתק ישירות |
| Summary.Yara | target.file.mime_type | הערך חולץ מהאינדקס הראשון של Summary.Yara, הומר לאותיות רישיות והוסר ממנו הערך FT_ אם Yara קיים, אחרת הערך מוגדר כ-ZIP אם EXTRACT_ZIP קיים, כ-SWF אם EXTRACT_SWF קיים, כ-GZIP אם EXTRACT_GZIP קיים וכ-CAB אם EXTRACT_CAB קיים |
| Object.META_BASIC_INFO.SHA1, Object.META_VT_CACHE.SHA1 | target.file.sha1 | ערך מ-Object.META_BASIC_INFO.SHA1 אם הוא לא ריק, אחרת מ-Object.META_VT_CACHE.SHA1 |
| Object.META_BASIC_INFO.SHA256 | target.file.sha256 | הערך הועתק ישירות |
| Object.META_BASIC_INFO.Size | target.file.size | הוסר ממנו הסיומת " .*" והוא הומר למספר שלם לא שלילי |
| metadata.vendor_name | ההגדרה היא EMERSON | |
| metadata.product_name | ההגדרה היא FILE SCANNING FRAMEWORK (מסגרת לסריקת קבצים) |
הבעיה עדיין לא נפתרה? קבלת תשובות מחברי הקהילה וממומחי Google SecOps.