ZeroFox Platform のログを収集する
このドキュメントでは、Amazon S3 を使用して ZeroFox Platform ログを Google Security Operations に取り込む方法について説明します。
始める前に
次の前提条件を満たしていることを確認してください。
- Google SecOps インスタンス。
- ZeroFox Platform テナントへの特権アクセス。
- AWS(S3、Identity and Access Management(IAM)、Lambda、EventBridge)への特権アクセス。
ZeroFox の前提条件を取得する
- https://cloud.zerofox.comで ZeroFox Platform にログインします。
- [Data Connectors> API Data Feeds] に移動します。- 直接 URL(ログイン後): https://cloud.zerofox.com/data_connectors/api
- このメニュー項目が表示されない場合は、ZeroFox 管理者にアクセス権をリクエストしてください。
 
- 直接 URL(ログイン後): 
- [Generate Token] または [Create Personal Access Token] をクリックします。
- 次の構成の詳細を入力します。
- 名前: わかりやすい名前を入力します(例: Google SecOps S3 Ingestion)。
- 有効期限: 組織のセキュリティ ポリシーに従ってローテーション期間を選択します。
- 権限/フィード: Alerts、CTI feeds、およびエクスポートするその他のデータ型に対する読み取り権限を選択します。
 
- 名前: わかりやすい名前を入力します(例: 
- [生成] をクリックします。
- 生成された個人用アクセス トークンをコピーして安全な場所に保存します(再度表示することはできません)。
- ZEROFOX_BASE_URL: https://api.zerofox.com(ほとんどのテナントのデフォルト)を保存します。
Google SecOps 用に AWS S3 バケットと IAM を構成する
- バケットの作成のユーザーガイドに沿って、Amazon S3 バケットを作成します。
- 後で参照できるように、バケットの名前とリージョンを保存します(例: zerofox-platform-logs)。
- IAM ユーザーの作成のユーザーガイドに沿って、ユーザーを作成します。
- 作成したユーザーを選択します。
- [セキュリティ認証情報] タブを選択します。
- [アクセスキー] セクションで [アクセスキーを作成] をクリックします。
- [ユースケース] として [サードパーティ サービス] を選択します。
- [次へ] をクリックします。
- 省略可: 説明タグを追加します。
- [アクセスキーを作成] をクリックします。
- [.csv ファイルをダウンロード] をクリックし、[アクセスキー] と [シークレット アクセスキー] を保存して、今後の参照に備えます。
- [完了] をクリックします。
- [権限] タブを選択します。
- [権限ポリシー] セクションの [権限を追加] をクリックします。
- [権限を追加] を選択します。
- [ポリシーを直接アタッチする] を選択します。
- AmazonS3FullAccess ポリシーを検索します。
- ポリシーを選択します。
- [次へ] をクリックします。
- [権限を追加] をクリックします。
S3 アップロードの IAM ポリシーとロールを構成する
- AWS コンソールで、[IAM] > [ポリシー] に移動します。
- [ポリシーを作成> [JSON] タブ] をクリックします。
- 次のポリシーをコピーして貼り付けます。
- ポリシー JSON(別のバケット名を入力した場合は - zerofox-platform-logsを置き換えます):- { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::zerofox-platform-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::zerofox-platform-logs/zerofox/platform/state.json" } ] }
- [次へ] > [ポリシーを作成] をクリックします。 
- [IAM] > [ロール] > [ロールの作成] > [AWS サービス] > [Lambda] に移動します。 
- 新しく作成したポリシーを関連付けます。 
- ロールに「 - ZeroFoxPlatformToS3Role」という名前を付けて、[ロールを作成] をクリックします。
Lambda 関数を作成する
- AWS コンソールで、[Lambda] > [Functions] > [Create function] に移動します。
- [Author from scratch] をクリックします。
- 次の構成情報を提供してください。 - 設定 - 値 - 名前 - zerofox_platform_to_s3- ランタイム - Python 3.13 - アーキテクチャ - x86_64 - 実行ロール - ZeroFoxPlatformToS3Role
- 関数を作成したら、[コード] タブを開き、スタブを削除して次のコード( - zerofox_platform_to_s3.py)を貼り付けます。- #!/usr/bin/env python3 # Lambda: Pull ZeroFox Platform data (alerts/incidents/logs) to S3 (no transform) import os, json, time, urllib.parse from urllib.request import Request, urlopen from urllib.error import HTTPError, URLError import boto3 S3_BUCKET = os.environ["S3_BUCKET"] S3_PREFIX = os.environ.get("S3_PREFIX", "zerofox/platform/") STATE_KEY = os.environ.get("STATE_KEY", "zerofox/platform/state.json") LOOKBACK_SEC = int(os.environ.get("LOOKBACK_SECONDS", "3600")) PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "200")) MAX_PAGES = int(os.environ.get("MAX_PAGES", "20")) HTTP_TIMEOUT = int(os.environ.get("HTTP_TIMEOUT", "60")) HTTP_RETRIES = int(os.environ.get("HTTP_RETRIES", "3")) URL_TEMPLATE = os.environ.get("URL_TEMPLATE", "") AUTH_HEADER = os.environ.get("AUTH_HEADER", "") # e.g. "Authorization: Bearer <token>" ZEROFOX_BASE_URL = os.environ.get("ZEROFOX_BASE_URL", "https://api.zerofox.com") ZEROFOX_API_TOKEN = os.environ.get("ZEROFOX_API_TOKEN", "") s3 = boto3.client("s3") def _iso(ts: float) -> str: return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts)) def _load_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) b = obj["Body"].read() return json.loads(b) if b else {} except Exception: return {"last_since": _iso(time.time() - LOOKBACK_SEC)} def _save_state(st: dict) -> None: s3.put_object( Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(st, separators=(",", ":")).encode("utf-8"), ContentType="application/json", ) def _headers() -> dict: hdrs = {"Accept": "application/json", "Content-Type": "application/json"} if AUTH_HEADER: try: k, v = AUTH_HEADER.split(":", 1) hdrs[k.strip()] = v.strip() except ValueError: hdrs["Authorization"] = AUTH_HEADER.strip() elif ZEROFOX_API_TOKEN: hdrs["Authorization"] = f"Bearer {ZEROFOX_API_TOKEN}" return hdrs def _http_get(url: str) -> dict: attempt = 0 while True: try: req = Request(url, method="GET") for k, v in _headers().items(): req.add_header(k, v) with urlopen(req, timeout=HTTP_TIMEOUT) as r: body = r.read() try: return json.loads(body.decode("utf-8")) except json.JSONDecodeError: return {"raw": body.decode("utf-8", errors="replace")} except HTTPError as e: if e.code in (429, 500, 502, 503, 504) and attempt < HTTP_RETRIES: retry_after = int(e.headers.get("Retry-After", 1 + attempt)) time.sleep(max(1, retry_after)) attempt += 1 continue raise except URLError: if attempt < HTTP_RETRIES: time.sleep(1 + attempt) attempt += 1 continue raise def _put_json(obj: dict, label: str) -> str: ts = time.gmtime() key = f"{S3_PREFIX}/{time.strftime('%Y/%m/%d/%H%M%S', ts)}-zerofox-{label}.json" s3.put_object( Bucket=S3_BUCKET, Key=key, Body=json.dumps(obj, separators=(",", ":")).encode("utf-8"), ContentType="application/json", ) return key def _extract_next_token(payload: dict): next_token = (payload.get("next") or payload.get("next_token") or payload.get("nextPageToken") or payload.get("next_page_token")) if isinstance(next_token, dict): return next_token.get("token") or next_token.get("cursor") or next_token.get("value") return next_token def _extract_items(payload: dict) -> list: for key in ("results", "data", "alerts", "items", "logs", "events"): if isinstance(payload.get(key), list): return payload[key] return [] def _extract_newest_timestamp(items: list, current: str) -> str: newest = current for item in items: timestamp = (item.get("timestamp") or item.get("created_at") or item.get("last_modified") or item.get("event_time") or item.get("log_time") or item.get("updated_at")) if isinstance(timestamp, str) and timestamp > newest: newest = timestamp return newest def lambda_handler(event=None, context=None): st = _load_state() since = st.get("last_since") or _iso(time.time() - LOOKBACK_SEC) # Use URL_TEMPLATE if provided, otherwise construct default alerts endpoint if URL_TEMPLATE: base_url = URL_TEMPLATE.replace("{SINCE}", urllib.parse.quote(since)) else: base_url = f"{ZEROFOX_BASE_URL}/v1/alerts?since={urllib.parse.quote(since)}" page_token = "" pages = 0 total_items = 0 newest_since = since while pages < MAX_PAGES: # Construct URL with pagination if URL_TEMPLATE: url = (base_url .replace("{PAGE_TOKEN}", urllib.parse.quote(page_token)) .replace("{PAGE_SIZE}", str(PAGE_SIZE))) else: url = f"{base_url}&limit={PAGE_SIZE}" if page_token: url += f"&page_token={urllib.parse.quote(page_token)}" payload = _http_get(url) _put_json(payload, f"page-{pages:05d}") items = _extract_items(payload) total_items += len(items) newest_since = _extract_newest_timestamp(items, newest_since) pages += 1 next_token = _extract_next_token(payload) if not next_token: break page_token = str(next_token) if newest_since and newest_since != st.get("last_since"): st["last_since"] = newest_since _save_state(st) return {"ok": True, "pages": pages, "items": total_items, "since": since, "new_since": newest_since} if __name__ == "__main__": print(lambda_handler())
- [構成] > [環境変数] に移動します。 
- [編集>新しい環境変数を追加] をクリックします。 
- 次の表に示す環境変数を入力し、サンプル値を実際の値に置き換えます。 - 環境変数 - キー - 値の例 - S3_BUCKET- zerofox-platform-logs- S3_PREFIX- zerofox/platform/- STATE_KEY- zerofox/platform/state.json- ZEROFOX_BASE_URL- https://api.zerofox.com- ZEROFOX_API_TOKEN- your-zerofox-personal-access-token- LOOKBACK_SECONDS- 3600- PAGE_SIZE- 200- MAX_PAGES- 20- HTTP_TIMEOUT- 60- HTTP_RETRIES- 3- URL_TEMPLATE- (省略可) - {SINCE}、- {PAGE_TOKEN}、- {PAGE_SIZE}を含むカスタム URL テンプレート- AUTH_HEADER- カスタム認証用の (省略可) - Authorization: Bearer <token>
- 関数が作成されたら、そのページにとどまるか、[Lambda] > [関数] > [your-function] を開きます。 
- [CONFIGURATION] タブを選択します。 
- [全般設定] パネルで、[編集] をクリックします。 
- [Timeout] を [5 minutes (300 seconds)] に変更し、[Save] をクリックします。 
EventBridge スケジュールを作成する
- [Amazon EventBridge] > [Scheduler] > [スケジュールの作成] に移動します。
- 次の構成の詳細を入力します。
- 定期的なスケジュール: レート(1 hour)。
- ターゲット: Lambda 関数 zerofox_platform_to_s3。
- 名前: zerofox-platform-1h
 
- 定期的なスケジュール: レート(
- [スケジュールを作成] をクリックします。
(省略可)Google SecOps 用の読み取り専用の IAM ユーザーと鍵を作成する
- AWS コンソール > IAM > ユーザーに移動します。
- [ユーザーを追加] をクリックします。
- 次の構成の詳細を入力します。
- ユーザー: 「secops-reader」と入力します。
- アクセスの種類: [アクセスキー - プログラムによるアクセス] を選択します。
 
- ユーザー: 「
- [ユーザーを作成] をクリックします。
- 最小限の読み取りポリシー(カスタム)を関連付ける: [ユーザー] > [secops-reader] > [権限] > [権限を追加] > [ポリシーを直接関連付ける] > [ポリシーを作成]。
- JSON: - { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::zerofox-platform-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::zerofox-platform-logs" } ] }
- 名前 = - secops-reader-policy。
- [ポリシーを作成> 検索/選択> 次へ> 権限を追加] をクリックします。 
- secops-readerのアクセスキーを作成します。[セキュリティ認証情報] > [アクセスキー] に移動します。
- [アクセスキーを作成] をクリックします。 
- .CSVをダウンロードします。(これらの値はフィードに貼り付けます)。
ZeroFox Platform のログを取り込むように Google SecOps でフィードを構成する
- [SIEM 設定] > [フィード] に移動します。
- [+ 新しいフィードを追加] をクリックします。
- [フィード名] フィールドに、フィードの名前を入力します(例: ZeroFox Platform Logs)。
- [ソースタイプ] として [Amazon S3 V2] を選択します。
- [ログタイプ] として [ZeroFox Platform] を選択します。
- [次へ] をクリックします。
- 次の入力パラメータの値を指定します。
- S3 URI: s3://zerofox-platform-logs/zerofox/platform/
- Source deletion options: 必要に応じて削除オプションを選択します。
- ファイルの最大経過日数: 指定した日数以内に変更されたファイルを含めます。デフォルトは 180 日です。
- アクセスキー ID: S3 バケットにアクセスできるユーザー アクセスキー。
- シークレット アクセスキー: S3 バケットにアクセスできるユーザーのシークレット キー。
- アセットの名前空間: アセットの名前空間。
- Ingestion labels: このフィードのイベントに適用されるラベル。
 
- S3 URI: 
- [次へ] をクリックします。
- [Finalize] 画面で新しいフィードの設定を確認し、[送信] をクリックします。
さらにサポートが必要な場合 コミュニティ メンバーや Google SecOps のプロフェッショナルから回答を得ることができます。