PingOne Advanced Identity Cloud-Logs erfassen
In diesem Dokument wird beschrieben, wie Sie PingOne Advanced Identity Cloud-Logs über Amazon S3 in Google Security Operations aufnehmen.
Hinweise
- Google SecOps-Instanz
- Privilegierter Zugriff auf den PingOne Advanced Identity Cloud-Mandanten
- Privilegierter Zugriff auf AWS (S3, IAM, Lambda, EventBridge)
PingOne-API-Schlüssel und FQDN des Mandanten abrufen
- Melden Sie sich in der Admin-Konsole von Advanced Identity Cloud an.
- Klicken Sie auf das Nutzersymbol und dann auf Tenant Settings (Mandanteneinstellungen).
- Klicken Sie auf dem Tab Globale Einstellungen auf API-Schlüssel protokollieren.
- Klicken Sie auf New Log API Key (Neuen Log-API-Schlüssel) und geben Sie einen Namen für den Schlüssel an.
- Klicken Sie auf Schlüssel erstellen.
- Kopieren Sie die Werte api_key_id und api_key_secret und speichern Sie sie an einem sicheren Ort. Der Wert api_key_secret wird nicht noch einmal angezeigt.
- Klicken Sie auf Fertig.
- Rufen Sie die Mandanteneinstellungen > „Details“ auf und suchen Sie nach dem FQDN des Mandanten (z. B.
example.tomcat.pingone.com
).
AWS S3-Bucket und IAM für Google SecOps konfigurieren
- Erstellen Sie einen Amazon S3-Bucket. Folgen Sie dazu dieser Anleitung: Bucket erstellen.
- Speichern Sie den Namen und die Region des Buckets zur späteren Verwendung (z. B.
pingone-aic-logs
). - Erstellen Sie einen Nutzer gemäß dieser Anleitung: IAM-Nutzer erstellen.
- Wählen Sie den erstellten Nutzer aus.
- Wählen Sie den Tab Sicherheitsanmeldedaten aus.
- Klicken Sie im Abschnitt Zugriffsschlüssel auf Zugriffsschlüssel erstellen.
- Wählen Sie als Anwendungsfall Drittanbieterdienst aus.
- Klicken Sie auf Weiter.
- Optional: Fügen Sie ein Beschreibungstag hinzu.
- Klicken Sie auf Zugriffsschlüssel erstellen.
- Klicken Sie auf CSV-Datei herunterladen, um den Zugriffsschlüssel und den geheimen Zugriffsschlüssel zur späteren Verwendung zu speichern.
- Klicken Sie auf Fertig.
- Wählen Sie den Tab Berechtigungen aus.
- Klicken Sie im Bereich Berechtigungsrichtlinien auf Berechtigungen hinzufügen.
- Wählen Sie Berechtigungen hinzufügen aus.
- Wählen Sie Richtlinien direkt anhängen aus.
- Suchen Sie nach der Richtlinie AmazonS3FullAccess und wählen Sie sie aus.
- Klicken Sie auf Weiter.
- Klicken Sie auf Berechtigungen hinzufügen.
IAM-Richtlinie und -Rolle für S3-Uploads konfigurieren
- Rufen Sie in der AWS-Konsole IAM > Richtlinien > Richtlinie erstellen > JSON-Tab auf.
Geben Sie die folgende Richtlinie ein.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutPingOneAICObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::pingone-aic-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::pingone-aic-logs/pingone-aic/logs/state.json" } ] }
- Ersetzen Sie
pingone-aic-logs
, wenn Sie einen anderen Bucket-Namen eingegeben haben.
- Ersetzen Sie
Klicken Sie auf Weiter > Richtlinie erstellen.
Rufen Sie IAM > Rollen > Rolle erstellen > AWS-Service > Lambda auf.
Hängen Sie die neu erstellte Richtlinie an.
Geben Sie der Rolle den Namen
WritePingOneAICToS3Role
und klicken Sie auf Rolle erstellen.
Lambda-Funktion erstellen
- Rufen Sie in der AWS Console Lambda > Funktionen > Funktion erstellen auf.
- Klicken Sie auf Von Grund auf erstellen.
Geben Sie die folgenden Konfigurationsdetails an:
Einstellung Wert Name pingone_aic_to_s3
Laufzeit Python 3.13 Architektur x86_64 Ausführungsrolle WritePingOneAICToS3Role
Nachdem die Funktion erstellt wurde, öffnen Sie den Tab Code, löschen Sie den Stub und geben Sie den folgenden Code ein (
pingone_aic_to_s3.py
):#!/usr/bin/env python3 import os, json, time, urllib.parse from urllib.request import Request, urlopen from urllib.error import HTTPError, URLError import boto3 FQDN = os.environ["AIC_TENANT_FQDN"].strip("/") API_KEY_ID = os.environ["AIC_API_KEY_ID"] API_KEY_SECRET = os.environ["AIC_API_SECRET"] S3_BUCKET = os.environ["S3_BUCKET"] S3_PREFIX = os.environ.get("S3_PREFIX", "pingone-aic/logs/").strip("/") SOURCES = [s.strip() for s in os.environ.get("SOURCES", "am-everything,idm-everything").split(",") if s.strip()] PAGE_SIZE = min(int(os.environ.get("PAGE_SIZE", "500")), 1000) # hard cap per docs MAX_PAGES = int(os.environ.get("MAX_PAGES", "20")) STATE_KEY = os.environ.get("STATE_KEY", "pingone-aic/logs/state.json") LOOKBACK_SECONDS = int(os.environ.get("LOOKBACK_SECONDS", "3600")) s3 = boto3.client("s3") def _headers(): return {"x-api-key": API_KEY_ID, "x-api-secret": API_KEY_SECRET} def _iso(ts: float) -> str: return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts)) def _http_get(url: str, timeout: int = 60, max_retries: int = 5) -> dict: attempt, backoff = 0, 1.0 while True: req = Request(url, method="GET", headers=_headers()) try: with urlopen(req, timeout=timeout) as r: data = r.read() return json.loads(data.decode("utf-8")) except HTTPError as e: # 429: respect X-RateLimit-Reset (epoch seconds) if present if e.code == 429 and attempt < max_retries: reset = e.headers.get("X-RateLimit-Reset") now = int(time.time()) delay = max(1, int(reset) - now) if (reset and reset.isdigit()) else int(backoff) time.sleep(delay); attempt += 1; backoff *= 2; continue if 500 <= e.code <= 599 and attempt < max_retries: time.sleep(backoff); attempt += 1; backoff *= 2; continue raise except URLError: if attempt < max_retries: time.sleep(backoff); attempt += 1; backoff *= 2; continue raise def _load_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) return json.loads(obj["Body"].read()) except Exception: return {"sources": {}} def _save_state(state: dict): s3.put_object(Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(state, separators=(",", ":")).encode("utf-8"), ContentType="application/json") def _write_page(payload: dict, source: str) -> str: ts = time.gmtime() key = f"{S3_PREFIX}/{time.strftime('%Y/%m/%d/%H%M%S', ts)}-pingone-aic-{source}.json" s3.put_object(Bucket=S3_BUCKET, Key=key, Body=json.dumps(payload, separators=(",", ":")).encode("utf-8"), ContentType="application/json") return key def _bounded_begin_time(last_ts: str | None, now: float) -> str: # beginTime must be <= 24h before endTime (now if endTime omitted) # if last_ts older than 24h → cap to now-24h; else use last_ts; else lookback twenty_four_h_ago = now - 24*3600 if last_ts: try: t_struct = time.strptime(last_ts[:19] + "Z", "%Y-%m-%dT%H:%M:%SZ") t_epoch = int(time.mktime(t_struct)) except Exception: t_epoch = int(now - LOOKBACK_SECONDS) begin_epoch = max(t_epoch, int(twenty_four_h_ago)) else: begin_epoch = max(int(now - LOOKBACK_SECONDS), int(twenty_four_h_ago)) return _iso(begin_epoch) def fetch_source(source: str, last_ts: str | None): base = f"https://{FQDN}/monitoring/logs" now = time.time() params = { "source": source, "_pageSize": str(PAGE_SIZE), "_sortKeys": "timestamp", "beginTime": _bounded_begin_time(last_ts, now) } pages = 0 written = 0 newest_ts = last_ts cookie = None while pages < MAX_PAGES: if cookie: params["_pagedResultsCookie"] = cookie qs = urllib.parse.urlencode(params, quote_via=urllib.parse.quote) data = _http_get(f"{base}?{qs}") _write_page(data, source) results = data.get("result") or data.get("results") or [] for item in results: t = item.get("timestamp") or item.get("payload", {}).get("timestamp") if t and (newest_ts is None or t > newest_ts): newest_ts = t written += len(results) cookie = data.get("pagedResultsCookie") pages += 1 if not cookie: break return {"source": source, "pages": pages, "written": written, "newest_ts": newest_ts} def lambda_handler(event=None, context=None): state = _load_state() state.setdefault("sources", {}) summary = [] for source in SOURCES: last_ts = state["sources"].get(source, {}).get("last_ts") res = fetch_source(source, last_ts) if res.get("newest_ts"): state["sources"][source] = {"last_ts": res["newest_ts"]} summary.append(res) _save_state(state) return {"ok": True, "summary": summary} if __name__ == "__main__": print(lambda_handler())
Klicken Sie auf Konfiguration> Umgebungsvariablen> Bearbeiten> Neue Umgebungsvariable hinzufügen.
Geben Sie die folgenden Umgebungsvariablen ein und ersetzen Sie die Platzhalter durch Ihre Werte:
Schlüssel Beispiel S3_BUCKET
pingone-aic-logs
S3_PREFIX
pingone-aic/logs/
STATE_KEY
pingone-aic/logs/state.json
AIC_TENANT_FQDN
example.tomcat.pingone.com
AIC_API_KEY_ID
<api_key_id>
AIC_API_SECRET
<api_key_secret>
SOURCES
am-everything,idm-everything
PAGE_SIZE
500
MAX_PAGES
20
LOOKBACK_SECONDS
3600
Bleiben Sie nach dem Erstellen der Funktion auf der zugehörigen Seite oder öffnen Sie Lambda > Funktionen > Ihre Funktion.
Wählen Sie den Tab Konfiguration aus.
Klicken Sie im Bereich Allgemeine Konfiguration auf Bearbeiten.
Ändern Sie Zeitlimit in 5 Minuten (300 Sekunden) und klicken Sie auf Speichern.
EventBridge-Zeitplan erstellen
- Gehen Sie zu Amazon EventBridge > Scheduler > Create schedule (Amazon EventBridge > Scheduler > Zeitplan erstellen).
- Geben Sie die folgenden Konfigurationsdetails an:
- Wiederkehrender Zeitplan: Preis (
1 hour
). - Ziel: Ihre Lambda-Funktion.
- Name:
pingone-aic-1h
.
- Wiederkehrender Zeitplan: Preis (
- Klicken Sie auf Zeitplan erstellen.
Optional: IAM-Nutzer mit Lesezugriff und Schlüssel für Google SecOps erstellen
- Rufen Sie in der AWS-Konsole IAM > Nutzer auf und klicken Sie auf Nutzer hinzufügen.
- Geben Sie die folgenden Konfigurationsdetails an:
- Nutzer: Geben Sie einen eindeutigen Namen ein, z. B.
secops-reader
. - Zugriffstyp: Wählen Sie Zugriffsschlüssel – programmatischer Zugriff aus.
- Klicken Sie auf Nutzer erstellen.
- Nutzer: Geben Sie einen eindeutigen Namen ein, z. B.
- Richtlinie mit minimalen Leseberechtigungen anhängen (benutzerdefiniert): Nutzer >
secops-reader
auswählen > Berechtigungen > Berechtigungen hinzufügen > Richtlinien direkt anhängen > Richtlinie erstellen Geben Sie im JSON-Editor die folgende Richtlinie ein:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::<your-bucket>/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::<your-bucket>" } ] }
Legen Sie
secops-reader-policy
als Name fest.Gehen Sie zu Richtlinie erstellen> suchen/auswählen > Weiter > Berechtigungen hinzufügen.
Rufen Sie Sicherheitsanmeldedaten > Zugriffsschlüssel > Zugriffsschlüssel erstellen auf.
Laden Sie die CSV herunter (diese Werte werden in den Feed eingegeben).
Feed in Google SecOps konfigurieren, um PingOne Advanced Identity Cloud-Logs aufzunehmen
- Rufen Sie die SIEM-Einstellungen > Feeds auf.
- Klicken Sie auf Neuen Feed hinzufügen.
- Geben Sie im Feld Feed name (Feedname) einen Namen für den Feed ein, z. B.
PingOne Advanced Identity Cloud
. - Wählen Sie Amazon S3 V2 als Quelltyp aus.
- Wählen Sie PingOne Advanced Identity Cloud als Logtyp aus.
- Klicken Sie auf Weiter.
- Geben Sie Werte für die folgenden Eingabeparameter an:
- S3-URI:
s3://pingone-aic-logs/pingone-aic/logs/
- Optionen zum Löschen der Quelle: Wählen Sie die gewünschte Option zum Löschen aus.
- Maximales Dateialter: Standardmäßig 180 Tage.
- Zugriffsschlüssel-ID: Zugriffsschlüssel des Nutzers mit Zugriff auf den S3-Bucket.
- Secret Access Key (Geheimer Zugriffsschlüssel): Geheimer Nutzersicherheitsschlüssel mit Zugriff auf den S3-Bucket.
- Asset-Namespace: Der Asset-Namespace.
- Aufnahmelabels: Das Label, das auf die Ereignisse aus diesem Feed angewendet werden soll.
- S3-URI:
- Klicken Sie auf Weiter.
- Prüfen Sie die neue Feedkonfiguration auf dem Bildschirm Abschließen und klicken Sie dann auf Senden.
Benötigen Sie weitere Hilfe? Antworten von Community-Mitgliedern und Google SecOps-Experten erhalten