איסוף יומנים של Proofpoint Secure Email Relay
במאמר הזה מוסבר איך להטמיע יומנים של Proofpoint Secure Email Relay ב-Google Security Operations באמצעות Cloud Storage V2.
Proofpoint Secure Email Relay (SER) הוא שרת ממסר מנוהל לאימיילים שנוצרים על ידי אפליקציות, כמו הודעות על טרנזקציות והתראות. ה-SER מדווח על התנועה הזו דרך Reporting API, אחד מממשקי ה-API להגדרת SER. ה-API מחזיר נתוני שימוש מצטברים: מיקום רוחב הפס המורשה, נפח יומי ומספר הודעות, ומונים לכל משתמש בשרת הממסר. מנתח הנתונים ממפה את הנתונים האלה למודל הנתונים המאוחד (UDM), ומתעד אותם כתוויות לצד זהות המשתמש של הממסר.
לפני שמתחילים
חשוב לוודא שיש לכם את הדברים הבאים:
- מכונה של Google SecOps
- פרויקט ב- Google Cloud עם Cloud Storage API מופעל
- הרשאות ליצירה ולניהול של קטגוריות של Cloud Storage
- הרשאות לניהול כללי מדיניות של ניהול זהויות והרשאות גישה (IAM) בקטגוריות של Cloud Storage
- הרשאות ליצירת שירותי Cloud Run, נושאי Pub/Sub ומשימות ב-Cloud Scheduler
- גישת אדמין לפורטל Proofpoint Secure Email Relay, כולל הרשאה ליצור מפתחות API
יצירת קטגוריה של Cloud Storage
- נכנסים לGoogle Cloud מסוף.
- בוחרים את הפרויקט או יוצרים פרויקט חדש.
- בתפריט הניווט, נכנסים ל-Cloud Storage > Buckets.
- לוחצים על Create bucket.
ממלאים את פרטי ההגדרה הבאים:
הגדרה ערך Name your bucket מזינים שם ייחודי ברמה גלובלית (לדוגמה, proofpoint-ser-logs)סוג המיקום בוחרים לפי הצרכים (אזור, שני אזורים, מספר אזורים) Location בוחרים את המיקום (לדוגמה, us-central1)סוג אחסון (storage class) רגיל (מומלץ ליומנים שהגישה אליהם תכופה) בקרת גישה אחידה (מומלץ) כלי הגנה אופציונלי: הפעלת ניהול גרסאות של אובייקטים או מדיניות שמירת נתונים לוחצים על יצירה.
איסוף פרטי הכניסה ל-Proofpoint SER API
הגישה ל-Reporting API ניתנת באמצעות מפתח API שהונפק על ידי שירות ניהול מפתחות ה-API של Proofpoint. המפתח לא נוצר בתוך מסוף SER עצמו, ולכן הוא לא מופיע באף אחד מהפריטים בתפריט הניווט של SER.
יצירה של מפתח API
- נכנסים לפורטל Proofpoint Secure Email Relay באמצעות פרטי כניסה של אדמין.
- פותחים את הממשק למעבר בין חשבונות בפינה הימנית העליונה.
- עוברים אל Services > API Key Management.
- לוחצים על Create Key.
- בוחרים באפשרות Secure Email Relay בתור המוצר.
מעתיקים את הערכים הבאים ושומרים אותם במקום מאובטח:
- מפתח: משמש בתור
client_id - סוד: משמש בתור
client_secret
- מפתח: משמש בתור
אימות ההרשאות
השירות API Key Management משותף לכמה מוצרים של Proofpoint, ולכן אתם ניגשים אליו דרך הממשק למעבר בין חשבונות ולא דרך תפריט הניווט של SER. כדי לקבל גישה לממשקי ה-API להגדרת SER, כולל Reporting API, צריך לבחור באפשרות ממסר אימייל מאובטח בתור המוצר כשיוצרים את המפתח.
אם האפשרות ניהול מפתחות API לא מופיעה בממשק למעבר בין חשבונות, לחשבון שלכם אין הרשאת ניהול ממשקי API. פנו לאדמין או לצוות ניהול החשבון ב-Proofpoint כדי לקבל את ההרשאה.
בדיקת הגישה ל-API
Reporting API משתמש בהענקת פרטי כניסה של לקוח OAuth 2.0. מחליפים את המפתח והסוד בטוקן למוכ"ז, ואז קוראים ל-API באמצעות הטוקן הזה.
# Replace with the values you copied CLIENT_ID="<your-key>" CLIENT_SECRET="<your-secret>" # 1. Exchange the key and secret for an access token ACCESS_TOKEN=$(curl -s -X POST "https://auth.proofpoint.com/v1/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=${CLIENT_ID}" \ -d "client_secret=${CLIENT_SECRET}" \ | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])") # 2. Call the Reporting API with the token curl -v -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://reporting.ser.proofpoint.com/v1/usage/overview"
קריאה מוצלחת מחזירה אובייקט JSON שהאיבר data שלו מכיל את throughputLimit, licenseStartDate, licenseEndDate ואת ממוצעי התפוקה.
יצירת חשבון שירות לפונקציית Cloud Run
לפונקציית Cloud Run נדרש חשבון שירות עם הרשאות כתיבה בקטגוריה של Cloud Storage, וצריך להפעיל אותה באמצעות Pub/Sub.
יצירת חשבון השירות
- במסוף GCP, נכנסים לדף IAM & Admin > Service Accounts.
- לוחצים על Create Service Account.
- מזינים את פרטי ההגדרה הבאים:
- Service account name: מזינים
proofpoint-ser-collector-sa - Service account description: מזינים
Service account for Cloud Run function to collect Proofpoint Secure Email Relay logs
- Service account name: מזינים
- לוחצים על יצירה והמשך.
- בקטע Grant this service account access to project, מוסיפים את התפקידים הבאים:
- לוחצים על בחירת תפקיד.
- מחפשים את התפקיד Storage Object Admin ובוחרים אותו.
- לוחצים על + הוספת תפקיד.
- מחפשים את התפקיד Cloud Run Invoker ובוחרים בו.
- לוחצים על + הוספת תפקיד.
- מחפשים את התפקיד Cloud Functions Invoker ובוחרים אותו.
- לוחצים על Continue.
- לוחצים על סיום.
התפקידים האלה נדרשים כדי:
- אדמין של אובייקטים באחסון: כתיבת יומנים לקטגוריה של Cloud Storage וניהול קובצי תמונת מצב
- Cloud Run Invoker: מאפשר ל-Pub/Sub להפעיל את הפונקציה
- מפעיל פונקציות של Cloud Functions: מאפשר להפעיל פונקציות
הקצאת הרשאות IAM בקטגוריה של Cloud Storage
נותנים לחשבון השירות הרשאות כתיבה בקטגוריה של Cloud Storage:
- נכנסים לדף Cloud Storage > Buckets.
- לוחצים על שם הקטגוריה (לדוגמה,
proofpoint-ser-logs). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מזינים את פרטי ההגדרה הבאים:
- הוספת חשבונות משתמשים: מזינים את כתובת האימייל של חשבון השירות (לדוגמה,
proofpoint-ser-collector-sa@PROJECT_ID.iam.gserviceaccount.com) - הקצאת תפקידים: בוחרים באפשרות אדמין של אובייקטים באחסון.
- הוספת חשבונות משתמשים: מזינים את כתובת האימייל של חשבון השירות (לדוגמה,
- לוחצים על Save.
יוצרים נושא Pub/Sub
יוצרים נושא Pub/Sub ש-Cloud Scheduler יפרסם בו ופונקציית Cloud Run תירשם אליו.
- במסוף GCP, נכנסים לדף Pub/Sub > Topics.
- לוחצים על Create topic.
- מזינים את פרטי ההגדרה הבאים:
- מזהה הנושא: מזינים
proofpoint-ser-trigger - משאירים את שאר ההגדרות כברירת מחדל.
- מזהה הנושא: מזינים
- לוחצים על יצירה.
יצירת פונקציית Cloud Run לאיסוף יומנים
הודעות Pub/Sub מ-Cloud Scheduler יפעילו את פונקציית Cloud Run כדי לאחזר דוחות שימוש מ-SER Reporting API ולכתוב אותם ב-Cloud Storage.
- במסוף GCP, נכנסים ל-Cloud Run.
- לוחצים על Create service.
- בוחרים באפשרות Function (שימוש בעורך ישיר כדי ליצור פונקציה).
בקטע Configure, מזינים את פרטי ההגדרה הבאים:
הגדרה ערך שם השירות proofpoint-ser-collectorאזור בוחרים את האזור שתואם לקטגוריה של Cloud Storage (לדוגמה, us-central1)זמן ריצה בוחרים באפשרות Python 3.12 או גרסה עדכנית יותר. בקטע טריגר (אופציונלי):
- לוחצים על + הוספת טריגר.
- בוחרים באפשרות Cloud Pub/Sub.
- בקטע Select a Cloud Pub/Sub topic, בוחרים את הנושא ב-Pub/Sub (
proofpoint-ser-trigger). - לוחצים על Save.
בקטע אימות:
- לוחצים על דרישת אימות.
- בודקים את ניהול הזהויות והרשאות הגישה (IAM).
עוברים לקטע Containers, Networking, Security ומרחיבים אותו.
עוברים לכרטיסייה אבטחה:
- Service account: בוחרים את חשבון השירות (
proofpoint-ser-collector-sa)
- Service account: בוחרים את חשבון השירות (
עוברים לכרטיסייה מאגרי תגים:
- לוחצים על Variables & Secrets.
- לוחצים על + Add variable לכל משתנה סביבה:
שם משתנה ערך לדוגמה תיאור GCS_BUCKETproofpoint-ser-logsשם הקטגוריה של Cloud Storage GCS_PREFIXser-logsקידומת לקובצי יומן STATE_KEYser-logs-state.jsonנתיב המצב, מחוץ לקידומת היומן TOKEN_URLhttps://auth.proofpoint.com/v1/tokenנקודת קצה של טוקן OAuth 2.0 API_BASEhttps://reporting.ser.proofpoint.comכתובת ה-URL הבסיסית של SER Reporting API CLIENT_IDyour-keyהמפתח מניהול מפתחות ה-API CLIENT_SECRETyour-secretהסוד מניהול מפתחות ה-API PAGE_SIZE50מספר המשתמשים ב-Relay שביקשת לכל דף MAX_PAGES200מספר הדפים המקסימלי לעימוד של משתמשי ממסר REPORT_DAY_OFFSET1דיווח על היום הזה לפני מספר הימים הזה MAX_BACKFILL_DAYS7ברוב הימים, הרצה אחת תמלא את הנתונים החסרים SEEN_RETENTION_DAYS7מספר הימים שבהם מפתחות לביטול כפילויות נשמרים במצב בקטע Variables & Secrets, עוברים אל Requests:
- Request timeout: מזינים
600שניות (10 דקות)
- Request timeout: מזינים
עוברים לכרטיסייה הגדרות:
- בקטע Resources:
- זיכרון: בוחרים באפשרות 512 MiB או יותר
- מעבד (CPU): בוחרים באפשרות 1
- בקטע Resources:
בקטע Revision scaling:
- Minimum number of instances: מזינים
0 - Maximum number of instances: מזינים
100(או משנים בהתאם לעומס הצפוי)
- Minimum number of instances: מזינים
לוחצים על יצירה.
ממתינים ליצירת השירות (דקה או שתיים).
אחרי שיוצרים את השירות, עורך הקוד המוטבע נפתח באופן אוטומטי.
הוספת קוד הפונקציה
- בשדה Entry point, מזינים main.
- בעורך הקוד, יוצרים שני קבצים:
הקובץ הראשון – main.py:
import functions_framework from google.cloud import storage from google.cloud.exceptions import NotFound import hashlib import json import os import time import urllib.parse import urllib3 from datetime import date, datetime, timezone, timedelta # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=30.0), retries=False, ) # Initialize Storage client storage_client = storage.Client() # Environment variables GCS_BUCKET = os.environ.get('GCS_BUCKET') GCS_PREFIX = os.environ.get('GCS_PREFIX', 'ser-logs') # STATE_KEY must stay OUTSIDE GCS_PREFIX. The feed ingests every object under # its bucket URI and, with a deletion option selected, deletes what it # transferred. A state file inside the prefix would be ingested as log data and # then deleted, resetting collection and re-ingesting duplicates. STATE_KEY = os.environ.get('STATE_KEY', 'ser-logs-state.json') TOKEN_URL = os.environ.get('TOKEN_URL', 'https://auth.proofpoint.com/v1/token') API_BASE = os.environ.get('API_BASE', 'https://reporting.ser.proofpoint.com') CLIENT_ID = os.environ.get('CLIENT_ID') CLIENT_SECRET = os.environ.get('CLIENT_SECRET') PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '50')) MAX_PAGES = int(os.environ.get('MAX_PAGES', '200')) # The Reporting API filters on whole calendar days, so the collector reports on # a day that has already finished rather than on a partial one. REPORT_DAY_OFFSET = int(os.environ.get('REPORT_DAY_OFFSET', '1')) MAX_BACKFILL_DAYS = int(os.environ.get('MAX_BACKFILL_DAYS', '7')) SEEN_RETENTION_DAYS = int(os.environ.get('SEEN_RETENTION_DAYS', '7')) MAX_RATE_LIMIT_RETRIES = 5 class FetchError(Exception): """Raised when a Proofpoint SER API call fails. Collection must fail loudly. Returning an empty result on an API error is indistinguishable from a day with no traffic, and would let the run record a day it never actually read. """ def get_access_token(): """Exchange the API key and secret for an OAuth 2.0 bearer token. The SER configuration APIs use the client credentials grant with the credentials in the form body. The returned token is sent to each API host as an Authorization: Bearer header. """ body = urllib.parse.urlencode({ 'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, }) headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', } try: response = http.request('POST', TOKEN_URL, body=body, headers=headers) except Exception as e: raise FetchError(f'Token request to {TOKEN_URL} failed: {e}') from e if response.status != 200: raise FetchError(f'HTTP {response.status} from {TOKEN_URL}: {response.data.decode("utf-8")}') try: token = json.loads(response.data.decode('utf-8')).get('access_token') except json.JSONDecodeError as e: raise FetchError(f'Malformed token response from {TOKEN_URL}: {e}') from e if not token: raise FetchError('Token response did not contain an access_token') return token def call_api(token, method, path, body=None): """Call one Reporting API endpoint and return the decoded JSON body. Every failure raises. A caller that mistook an error for an empty report would record the day as collected and never come back to it. """ url = f'{API_BASE}{path}' headers = { 'Authorization': f'Bearer {token}', 'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'GoogleSecOps-ProofpointSERCollector/1.0', } payload = json.dumps(body) if body is not None else None backoff = 1.0 retries = 0 while True: try: response = http.request(method, url, body=payload, headers=headers) except Exception as e: raise FetchError(f'Request to {url} failed: {e}') from e if response.status == 429: retries += 1 if retries > MAX_RATE_LIMIT_RETRIES: raise FetchError(f'Rate limited repeatedly by {url}; giving up without recording the day') raw_retry_after = response.headers.get('Retry-After') try: # Retry-After may also be an HTTP date, which int() cannot parse. delay = int(raw_retry_after) if raw_retry_after else int(backoff) except (TypeError, ValueError): delay = int(backoff) print(f'Rate limited (429) on {path}. Retrying after {delay}s...') time.sleep(delay) backoff = min(backoff * 2, 30.0) continue if response.status != 200: raise FetchError(f'HTTP {response.status} from {url}: {response.data.decode("utf-8")}') try: return json.loads(response.data.decode('utf-8')) except json.JSONDecodeError as e: raise FetchError(f'Malformed JSON response from {url}: {e}') from e def as_list(value): """Return a response data member as a list. The Reporting API returns data as an object on some endpoints and as an array on others, and the published schema declares neither. """ if value is None: return [] if isinstance(value, list): return value return [value] def collect_day(token, day, include_overview): """Collect one reporting day and return the records to write. The nesting of each record is chosen to match what the parser expects. Rows carrying acceptedMessages are emitted bare, because the parser nests those under data itself. The other two responses are emitted whole, so that their data and metadata envelope survives to the parser. """ records = [] # Licensed throughput position. This endpoint takes no date and reports # the tenant's standing right now, so it is collected once per run rather # than once per day: a backfill would otherwise write the same snapshot # against every day it catches up on. if include_overview: records.append(call_api(token, 'GET', '/v1/usage/overview')) # Volume in bytes for the day. One envelope per row, so that the # totalThroughput in metadata stays attached to the row it describes. data_trend = call_api(token, 'POST', '/v1/usage/data-trend', { 'dates': day, 'interval': 'day', }) for element in as_list(data_trend.get('data')): records.append({'data': [element], 'metadata': data_trend.get('metadata', {})}) # Message counters for the day. message_trend = call_api(token, 'POST', '/v1/usage/message-trend', { 'dates': day, 'interval': 'day', }) records.extend(as_list(message_trend.get('data'))) # Per relay user counters, paginated. page = 1 while True: if page > MAX_PAGES: raise FetchError(f'Relay user pagination for {day} exceeded MAX_PAGES ({MAX_PAGES})') payload = call_api(token, 'POST', '/v1/usage/relay-users', { 'dates': day, 'pageNum': page, 'pageSize': PAGE_SIZE, }) elements = as_list(payload.get('data')) if not elements: break records.extend(elements) pagination = (payload.get('metadata') or {}).get('pagination') or {} total_pages = pagination.get('totalPages') if not total_pages or page >= total_pages: break page += 1 return records def record_key(record): """Return the deduplication identity of one record. Reporting rows are aggregates and carry no identifier of their own, so identity is the content hash. Keys are held per reporting day, which makes a repeated run of the same day a no-op without ever suppressing a new day whose figures happen to be identical. """ return 'sha256:' + hashlib.sha256( json.dumps(record, sort_keys=True, ensure_ascii=False).encode('utf-8') ).hexdigest() def pending_days(last_report_date, target): """Return the reporting days still to collect, oldest first.""" if not last_report_date: start = target else: start = date.fromisoformat(last_report_date) + timedelta(days=1) if start > target: return [] if (target - start).days >= MAX_BACKFILL_DAYS: start = target - timedelta(days=MAX_BACKFILL_DAYS - 1) print(f'Backfill capped at {MAX_BACKFILL_DAYS} days. Days before {start.isoformat()} are not collected.') return [start + timedelta(days=offset) for offset in range((target - start).days + 1)] def prune_seen(seen, target): """Drop deduplication keys for days outside the retention window.""" cutoff = target - timedelta(days=SEEN_RETENTION_DAYS) return {day: keys for day, keys in seen.items() if date.fromisoformat(day) >= cutoff} @functions_framework.cloud_event def main(cloud_event): """Fetch Proofpoint SER usage reports and write them to Cloud Storage. Args: cloud_event: CloudEvent object containing the Pub/Sub message. """ if not all([GCS_BUCKET, CLIENT_ID, CLIENT_SECRET]): # Raise rather than return: a bare return acks the Pub/Sub message and # reports the run as successful, silently discarding the schedule tick. raise RuntimeError('Missing required environment variables') bucket = storage_client.bucket(GCS_BUCKET) state = load_state(bucket, STATE_KEY) seen = state.get('seen') or {} now = datetime.now(timezone.utc) target = (now - timedelta(days=REPORT_DAY_OFFSET)).date() days = pending_days(state.get('last_report_date'), target) if not days: print(f'Nothing to collect. {target.isoformat()} is already recorded.') return token = get_access_token() for day in days: key = day.isoformat() print(f'Collecting reporting day {key}') # A FetchError here propagates: the run fails, the state is left at the # last day that was fully written, and the next run retries this day. records = collect_day(token, key, include_overview=(day == days[-1])) day_seen = set(seen.get(key, [])) fresh = [] for record in records: digest = record_key(record) if digest in day_seen: continue day_seen.add(digest) fresh.append(record) print(f'{key}: fetched {len(records)} records, {len(fresh)} new after deduplication') if fresh: timestamp = now.strftime('%Y%m%dT%H%M%SZ') object_key = f'{GCS_PREFIX}/usage_{key}_{timestamp}.ndjson' blob = bucket.blob(object_key) ndjson = '\n'.join(json.dumps(record, ensure_ascii=False) for record in fresh) + '\n' blob.upload_from_string(ndjson, content_type='application/x-ndjson') print(f'Wrote {len(fresh)} records to gs://{GCS_BUCKET}/{object_key}') # Record the day only after its data is durably written. seen[key] = sorted(day_seen) state['last_report_date'] = key state['seen'] = prune_seen(seen, target) save_state(bucket, STATE_KEY, state) print(f'Successfully processed {len(days)} reporting day(s)') def load_state(bucket, key): """Read the collector state from Cloud Storage. Only a missing object is treated as a cold start. Any other error is raised: swallowing it would silently restart collection and re-ingest the whole backfill window. """ blob = bucket.blob(key) try: return json.loads(blob.download_as_text()) except NotFound: print('No state file found. Starting from the most recent completed day.') return {} def save_state(bucket, key, state): """Write the collector state to Cloud Storage. Failures are raised, not logged. If the state write fails after the data was uploaded, the next run repeats the same day and duplicates it. """ blob = bucket.blob(key) blob.upload_from_string( json.dumps(state, indent=2), content_type='application/json', ) print(f'Saved state: last_report_date={state.get("last_report_date")}')הקובץ השני – requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0
- לוחצים על Deploy כדי לשמור ולפרוס את הפונקציה.
מחכים שהפריסה תסתיים (2-3 דקות).
יצירת משימה ב-Cloud Scheduler
Cloud Scheduler יפרסם הודעות בנושא Pub/Sub במרווחי זמן קבועים, ויפעיל את פונקציית Cloud Run.
- במסוף GCP, נכנסים ל-Cloud Scheduler.
- לוחצים על Create Job.
ממלאים את פרטי ההגדרה הבאים:
הגדרה ערך שם proofpoint-ser-collector-dailyאזור בוחרים את אותו אזור כמו של פונקציית Cloud Run. תדירות 0 2 * * *(מדי יום, בשעה 02:00)אזור זמן בחירת אזור זמן (מומלץ לבחור ב-UTC) סוג היעד Pub/Sub נושא בוחרים את נושא Pub/Sub ( proofpoint-ser-trigger)גוף ההודעה {}(אובייקט JSON ריק)לוחצים על יצירה.
אפשרויות לתדירות התזמון
ה-Reporting API צובר נתונים לפי יום קלנדרי, כך שיום שהסתיים נאסף פעם אחת. מריצים את המשימה מדי יום, אחרי שיום הדיווח הסתיים באזור הזמן של הדייר:
| תדירות | ביטוי Cron | תרחיש לדוגמה |
|---|---|---|
| כל יום בשעה 02:00 | 0 2 * * * |
רגיל (מומלץ) |
| כל יום בשעה 06:00 | 0 6 * * * |
דיירים שהדיווח שלהם מתעדכן מאוחר יותר |
| פעמיים ביום | 0 2,14 * * * |
הוספת ניסיון חוזר באותו יום אם ההפעלה הראשונה נכשלה |
אם מפעילים את הסקריפט ביום שכבר תועד, הוא נסגר בלי לכתוב שום דבר. לכן, הפעלה נוספת עולה קריאה אחת ל-API ולעולם לא משכפלת נתונים.
בדיקת השילוב
- במסוף Cloud Scheduler, מוצאים את המשימה.
- לוחצים על Force run כדי להפעיל את המשימה באופן ידני.
- מחכים כמה שניות.
- נכנסים לדף Cloud Run > Services.
- לוחצים על שם הפונקציה (
proofpoint-ser-collector). - לוחצים על הכרטיסייה Logs.
מוודאים שהפונקציה בוצעה בהצלחה. חפשו את:
Collecting reporting day YYYY-MM-DD YYYY-MM-DD: fetched X records, X new after deduplication Wrote X records to gs://proofpoint-ser-logs/ser-logs/usage_YYYY-MM-DD_YYYYMMDDTHHMMSSZ.ndjson Saved state: last_report_date=YYYY-MM-DD Successfully processed 1 reporting day(s)נכנסים לדף Cloud Storage > Buckets.
לוחצים על שם הקטגוריה (
proofpoint-ser-logs).עוברים לתיקיית הקידומת (
ser-logs/).מוודאים שנוצר קובץ
.ndjsonחדש עם חותמת הזמן הנוכחית.
אם מופיעות שגיאות ביומנים:
- HTTP 400 מנקודת הקצה של האסימון: המפתח או הסוד שגויים, או שתוקף המפתח פג.
invalid_clientפירושו שהצמד נדחה, וצריך ליצור מפתח חלופי. - HTTP 401 מ-Reporting API: הטוקן למוכ"ז לא נשלח או שהוא כבר לא תקף.
- HTTP 429: הגבלת קצב של יצירת בקשות. הפונקציה מנסה שוב עם השהיה, ואם המגבלה נמשכת, ההרצה נכשלת והיום נשאר להרצה הבאה.
- משתני סביבה חסרים: ודאו שכל המשתנים הנדרשים מוגדרים.
אחזור של חשבון השירות של Google SecOps
מערכת Google SecOps משתמשת בחשבון שירות ייחודי כדי לקרוא נתונים מהקטגוריה של Cloud Storage. אתם צריכים לתת לחשבון השירות הזה גישה לקטגוריה שלכם.
איך מקבלים את כתובת האימייל של חשבון השירות
- עוברים אל הגדרות > ניהול נתונים > פידים. אם אתם משתמשים בפלטפורמה של SIEM בלבד, עוברים אל הגדרות > פידים.
- לוחצים על הוספת פיד חדש.
- לוחצים על הגדרת פיד יחיד.
- בשדה שם הפיד, מזינים שם לפיד (לדוגמה,
Proofpoint SER Logs). - בוחרים באפשרות Google Cloud Storage V2 בתור סוג המקור.
- בוחרים באפשרות ProofPoint Secure Email Relay בתור סוג היומן.
- לוחצים על Get Service Account (קבלת חשבון שירות).
תוצג כתובת אימייל ייחודית של חשבון שירות, לדוגמה:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comמעתיקים את כתובת האימייל הזו כדי להשתמש בה בשלב הבא.
לוחצים על הבא.
מציינים ערכים לפרמטרים הבאים של הקלט:
כתובת ה-URL של קטגוריית האחסון: מזינים את ה-URI של קטגוריית Cloud Storage עם נתיב הקידומת:
gs://proofpoint-ser-logs/ser-logs/- מחליפים את:
-
proofpoint-ser-logs: השם של הקטגוריה ב-Cloud Storage. -
ser-logs: קידומת או נתיב תיקייה אופציונליים שבהם נשמרים היומנים (משאירים ריק עבור תיקיית הבסיס).
-
- מחליפים את:
אפשרות מחיקת המקור: בוחרים את אפשרות המחיקה המועדפת:
- אף פעם לא למחוק קבצים: אף פעם לא למחוק קבצים מהמקור (מומלץ לבדיקה).
- Delete transferred files and empty directories: מחיקת קבצים וספריות ריקות מהמקור אחרי שהאחזור הושלם בהצלחה.
גיל הקובץ המקסימלי: כולל קבצים ששונו במספר הימים האחרונים (ברירת המחדל היא 180 ימים)
מרחב השמות של הנכס: מרחב השמות של הנכס
תוויות הטמעה: התווית שתתווסף לאירועים מהפיד הזה
לוחצים על הבא.
בודקים את הגדרות הפיד החדש במסך סיום ולוחצים על שליחה.
מתן הרשאות IAM לחשבון השירות של Google SecOps
לחשבון השירות של Google SecOps נדרשים שני תפקידים בקטגוריה של Cloud Storage: צפייה באובייקט אחסון כדי לקרוא את אובייקטי היומן, ותפקיד ברמת הקטגוריה כדי לקרוא את המטא-נתונים של הקטגוריה.
- נכנסים לדף Cloud Storage > Buckets.
- לוחצים על שם הקטגוריה (לדוגמה,
proofpoint-ser-logs). - עוברים לכרטיסייה Permissions.
- לוחצים על הענקת גישה.
- מזינים את פרטי ההגדרה הבאים:
- Add principals: מדביקים את כתובת האימייל של חשבון השירות של Google SecOps.
- הקצאת תפקידים: בוחרים את שני התפקידים הבאים:
- צפייה באובייקטים של אחסון: קריאת אובייקטים של יומנים.
- קריאה בקטגוריה באחסון מדור קודם: קריאת המטא-נתונים של הקטגוריה. אם בחרתם באפשרות המחיקה Delete transferred files and empty directories, בחרו במקום זאת באפשרות Storage Legacy Bucket Writer, שגם מעניקה את הרשאת המחיקה.
לוחצים על Save.
טבלת מיפוי UDM
| שדה ביומן | מיפוי UDM | Logic (פונקציה לוגית) |
|---|---|---|
acceptedMessages_label |
additional.fields |
ממוזג |
acceptedThroughput_label |
additional.fields |
ממוזג |
average30DayThroughput_label |
additional.fields |
ממוזג |
average7DayThroughput_label |
additional.fields |
ממוזג |
averageDailyThroughput_label |
additional.fields |
ממוזג |
avgAcceptedMessageSize_label |
additional.fields |
ממוזג |
blockedMessages_label |
additional.fields |
ממוזג |
data_totalThroughput_label |
additional.fields |
ממוזג |
deliveredMessages_label |
additional.fields |
ממוזג |
licenseEndDate_label |
additional.fields |
ממוזג |
licenseStartDate_label |
additional.fields |
ממוזג |
map_label |
additional.fields |
ממוזג |
metadata_totalThroughput_label |
additional.fields |
ממוזג |
quarantinedMessages_label |
additional.fields |
ממוזג |
rejectedMessages_label |
additional.fields |
ממוזג |
remainingThroughput_label |
additional.fields |
ממוזג |
requestedMessages_label |
additional.fields |
ממוזג |
requestedThroughput_label |
additional.fields |
ממוזג |
sentMessages_label |
additional.fields |
ממוזג |
throughputForecast_label |
additional.fields |
ממוזג |
throughputLimit_label |
additional.fields |
ממוזג |
throughput_label |
additional.fields |
ממוזג |
totalMessages_label |
additional.fields |
ממוזג |
undeliveredMessages_label |
additional.fields |
ממוזג |
data.name |
metadata.description |
מיפוי ישיר |
desc |
metadata.description |
מיפוי ישיר |
data.date |
metadata.event_timestamp |
נותח בתור yyyy-MM-dd |
event_type |
metadata.event_type |
מיפוי ישיר |
fromEnvelope |
network.email.bounce_address |
מיפוי ישיר |
fromHeader |
network.email.from |
מיפוי ישיר |
applicationName |
principal.administrative_domain |
מיפוי ישיר |
principal_host |
principal.asset.hostname |
מיפוי ישיר |
principal_host |
principal.hostname |
מיפוי ישיר |
principal_port |
principal.port |
מיפוי ישיר |
data.relayUserId |
principal.user.product_object_id |
מיפוי ישיר |
userId |
principal.user.product_object_id |
מיפוי ישיר |
applicationUserName |
principal.user.user_display_name |
מיפוי ישיר |
senderName |
target.administrative_domain |
מיפוי ישיר |
senderId |
target.user.product_object_id |
מיפוי ישיר |
| לא רלוונטי | metadata.product_name |
קבוע: PROOFPOINT SER |
| לא רלוונטי | metadata.vendor_name |
קבוע: PROOFPOINT |
יומן שינויים
הצגת יומן השינויים של המנתח הזה
הבעיה עדיין לא נפתרה? קבלת תשובות מחברי הקהילה וממומחי Google SecOps.