Mengumpulkan log operasi Zoom
Dokumen ini menjelaskan cara menyerap log operasi Zoom ke Google Security Operations menggunakan Amazon S3. Parser mengubah log mentah menjadi model data terpadu (UDM). Proses ini mengekstrak kolom dari pesan log mentah, melakukan pembersihan dan normalisasi data, serta memetakan informasi yang diekstrak ke kolom UDM yang sesuai, yang pada akhirnya memperkaya data untuk analisis dan korelasi dalam sistem SIEM.
Sebelum memulai
Pastikan Anda memenuhi prasyarat berikut:
- Instance Google SecOps
- Akses istimewa ke Zoom
- Akses istimewa ke AWS (S3, IAM, Lambda, EventBridge)
Mengumpulkan prasyarat Log Operasi Zoom (ID, kunci API, ID org, token)
- Login ke Zoom App Marketplace.
- Buka Develop > Build App > Server-to-Server OAuth.
- Buat aplikasi dan tambahkan cakupan berikut:
report:read:operation_logs:admin
(ataureport:read:admin
). - Di Kredensial Aplikasi, salin dan simpan detail berikut di lokasi yang aman:
- ID Akun.
- Client ID.
- Rahasia Klien.
Mengonfigurasi bucket AWS S3 dan IAM untuk Google SecOps
- Buat bucket Amazon S3 dengan mengikuti panduan pengguna ini: Membuat bucket
- Simpan Name dan Region bucket untuk referensi di masa mendatang (misalnya,
zoom-operation-logs
). - Buat pengguna dengan mengikuti panduan pengguna ini: Membuat pengguna IAM.
- Pilih Pengguna yang dibuat.
- Pilih tab Kredensial keamanan.
- Klik Create Access Key di bagian Access Keys.
- Pilih Layanan pihak ketiga sebagai Kasus penggunaan.
- Klik Berikutnya.
- Opsional: tambahkan tag deskripsi.
- Klik Create access key.
- Klik Download CSV file untuk menyimpan Access Key dan Secret Access Key untuk digunakan nanti.
- Klik Selesai.
- Pilih tab Izin.
- Klik Tambahkan izin di bagian Kebijakan izin.
- Pilih Tambahkan izin.
- Pilih Lampirkan kebijakan secara langsung
- Telusuri dan pilih kebijakan AmazonS3FullAccess.
- Klik Berikutnya.
- Klik Add permissions.
Mengonfigurasi kebijakan dan peran IAM untuk upload S3
- Di konsol AWS, buka IAM > Policies > Create policy > JSON tab.
Masukkan kebijakan berikut:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutZoomOperationLogs", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::zoom-operation-logs/zoom/operationlogs/*" }, { "Sid": "AllowStateReadWrite", "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::zoom-operation-logs/zoom/operationlogs/state.json" } ] }
- Ganti
zoom-operation-logs
jika Anda memasukkan nama bucket yang berbeda.
- Ganti
Klik Berikutnya > Buat kebijakan.
Buka IAM > Roles > Create role > AWS service > Lambda.
Lampirkan kebijakan yang baru dibuat.
Beri nama peran
WriteZoomOperationLogsToS3Role
, lalu klik Buat peran.
Buat fungsi Lambda
- Di Konsol AWS, buka Lambda > Functions > Create function.
- Klik Buat dari awal.
- Berikan detail konfigurasi berikut:
Setelan | Nilai |
---|---|
Nama | zoom_operationlogs_to_s3 |
Runtime | Python 3.13 |
Arsitektur | x86_64 |
Peran eksekusi | WriteZoomOperationLogsToS3Role |
Setelah fungsi dibuat, buka tab Code, hapus stub, lalu masukkan kode berikut(
zoom_operationlogs_to_s3.py
):#!/usr/bin/env python3 import os, json, gzip, io, uuid, datetime as dt, base64, urllib.parse, urllib.request import boto3 # ---- Environment ---- S3_BUCKET = os.environ["S3_BUCKET"] S3_PREFIX = os.environ.get("S3_PREFIX", "zoom/operationlogs/") STATE_KEY = os.environ.get("STATE_KEY", S3_PREFIX + "state.json") ZOOM_ACCOUNT_ID = os.environ["ZOOM_ACCOUNT_ID"] ZOOM_CLIENT_ID = os.environ["ZOOM_CLIENT_ID"] ZOOM_CLIENT_SECRET = os.environ["ZOOM_CLIENT_SECRET"] PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "300")) # API default 30; max may vary TIMEOUT = int(os.environ.get("TIMEOUT", "30")) TOKEN_URL = "https://zoom.us/oauth/token" REPORT_URL = "https://api.zoom.us/v2/report/operationlogs" s3 = boto3.client("s3") # ---- Helpers ---- def _http(req: urllib.request.Request): return urllib.request.urlopen(req, timeout=TIMEOUT) def get_token() -> str: params = urllib.parse.urlencode({ "grant_type": "account_credentials", "account_id": ZOOM_ACCOUNT_ID, }).encode() basic = base64.b64encode(f"{ZOOM_CLIENT_ID}:{ZOOM_CLIENT_SECRET}".encode()).decode() req = urllib.request.Request( TOKEN_URL, data=params, headers={ "Authorization": f"Basic {basic}", "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Host": "zoom.us", }, method="POST", ) with _http(req) as r: body = json.loads(r.read()) return body["access_token"] def get_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) return json.loads(obj["Body"].read()) except Exception: # initial state: start today today = dt.date.today().isoformat() return {"cursor_date": today, "next_page_token": None} def put_state(state: dict): state["updated_at"] = dt.datetime.utcnow().isoformat() + "Z" s3.put_object(Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(state).encode()) def write_chunk(items: list[dict], ts: dt.datetime) -> str: key = f"{S3_PREFIX}{ts:%Y/%m/%d}/zoom-operationlogs-{uuid.uuid4()}.json.gz" buf = io.BytesIO() with gzip.GzipFile(fileobj=buf, mode="w") as gz: for rec in items: gz.write((json.dumps(rec) + "n").encode()) buf.seek(0) s3.upload_fileobj(buf, S3_BUCKET, key) return key def fetch_page(token: str, from_date: str, to_date: str, next_page_token: str | None) -> dict: q = { "from": from_date, "to": to_date, "page_size": str(PAGE_SIZE), } if next_page_token: q["next_page_token"] = next_page_token url = REPORT_URL + "?" + urllib.parse.urlencode(q) req = urllib.request.Request(url, headers={ "Authorization": f"Bearer {token}", "Accept": "application/json", }) with _http(req) as r: return json.loads(r.read()) def lambda_handler(event=None, context=None): token = get_token() state = get_state() cursor_date = state.get("cursor_date") # YYYY-MM-DD # API requires from/to in yyyy-mm-dd, max one month per request from_date = cursor_date to_date = cursor_date total_written = 0 next_token = state.get("next_page_token") while True: page = fetch_page(token, from_date, to_date, next_token) items = page.get("operation_logs", []) or [] if items: write_chunk(items, dt.datetime.utcnow()) total_written += len(items) next_token = page.get("next_page_token") if not next_token: break # Advance to next day if we've finished this date today = dt.date.today().isoformat() if cursor_date < today: nxt = (dt.datetime.fromisoformat(cursor_date) + dt.timedelta(days=1)).date().isoformat() state["cursor_date"] = nxt state["next_page_token"] = None else: # stay on today; continue later with next_page_token=None state["next_page_token"] = None put_state(state) return {"ok": True, "written": total_written, "date": from_date} if __name__ == "__main__": print(lambda_handler())
Buka Configuration > Environment variables > Edit > Add new environment variable.
Masukkan variabel lingkungan berikut, ganti dengan nilai Anda:
Kunci Nilai contoh S3_BUCKET
zoom-operation-logs
S3_PREFIX
zoom/operationlogs/
STATE_KEY
zoom/operationlogs/state.json
ZOOM_ACCOUNT_ID
<your-zoom-account-id>
ZOOM_CLIENT_ID
<your-zoom-client-id>
ZOOM_CLIENT_SECRET
<your-zoom-client-secret>
PAGE_SIZE
300
TIMEOUT
30
Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda > Functions > your-function).
Pilih tab Configuration
Di panel General configuration, klik Edit.
Ubah Waktu tunggu menjadi 5 menit (300 detik), lalu klik Simpan.
Membuat jadwal EventBridge
- Buka Amazon EventBridge > Scheduler.
- Klik Buat jadwal.
- Berikan detail konfigurasi berikut:
- Jadwal berulang: Tarif (
15 min
). - Target: Fungsi Lambda Anda
zoom_operationlogs_to_s3
. - Name:
zoom-operationlogs-schedule-15min
.
- Jadwal berulang: Tarif (
- Klik Buat jadwal.
Opsional: Buat pengguna & kunci IAM hanya baca untuk Google SecOps
- Di Konsol AWS, buka IAM > Pengguna > Tambahkan pengguna.
- Klik Add users.
- Berikan detail konfigurasi berikut:
- Pengguna:
secops-reader
. - Jenis akses: Kunci akses — Akses terprogram.
- Pengguna:
- Klik Buat pengguna.
- Lampirkan kebijakan baca minimal (kustom): Pengguna > secops-reader > Izin > Tambahkan izin > Lampirkan kebijakan secara langsung > Buat kebijakan.
Di editor JSON, masukkan kebijakan berikut:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::zoom-operation-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::zoom-operation-logs" } ] }
Tetapkan nama ke
secops-reader-policy
.Buka Buat kebijakan > cari/pilih > Berikutnya > Tambahkan izin.
Buka Kredensial keamanan > Kunci akses > Buat kunci akses.
Download CSV (nilai ini dimasukkan ke dalam feed).
Mengonfigurasi feed di Google SecOps untuk menyerap Log Operasi Zoom
- Buka Setelan SIEM > Feed.
- Klik + Tambahkan Feed Baru.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
Zoom Operation Logs
). - Pilih Amazon S3 V2 sebagai Jenis sumber.
- Pilih Zoom Operation Logs sebagai Log type.
- Klik Berikutnya.
- Tentukan nilai untuk parameter input berikut:
- URI S3:
s3://zoom-operation-logs/zoom/operationlogs/
- Opsi penghapusan sumber: Pilih opsi penghapusan sesuai preferensi Anda.
- Usia File Maksimum: Menyertakan file yang diubah dalam jumlah hari terakhir. Defaultnya adalah 180 hari.
- ID Kunci Akses: Kunci akses pengguna dengan akses ke bucket S3.
- Kunci Akses Rahasia: Kunci rahasia pengguna dengan akses ke bucket S3.
- Namespace aset: Namespace aset.
- Label penyerapan: Label yang diterapkan ke peristiwa dari feed ini.
- URI S3:
- Klik Berikutnya.
- Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.
Tabel Pemetaan UDM
Kolom Log | Pemetaan UDM | Logika |
---|---|---|
tindakan | metadata.product_event_type | Kolom log mentah "action" dipetakan ke kolom UDM ini. |
category_type | additional.fields.key | Kolom log mentah "category_type" dipetakan ke kolom UDM ini. |
category_type | additional.fields.value.string_value | Kolom log mentah "category_type" dipetakan ke kolom UDM ini. |
Departemen | target.user.department | Kolom log mentah "Department" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Deskripsi | target.user.role_description | Kolom log mentah "Description" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Nama Tampilan | target.user.user_display_name | Kolom log mentah "Nama Tampilan" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Alamat Email | target.user.email_addresses | Kolom log mentah "Alamat Email" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Nama Depan | target.user.first_name | Kolom log mentah "Nama Depan" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Jabatan | target.user.title | Kolom log mentah "Job Title" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Nama Belakang | target.user.last_name | Kolom log mentah "Nama Belakang" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Lokasi | target.location.name | Kolom log mentah "Location" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
operation_detail | metadata.description | Kolom log mentah "operation_detail" dipetakan ke kolom UDM ini. |
operator | principal.user.email_addresses | Kolom log mentah "operator" dipetakan ke kolom UDM ini jika cocok dengan regex email. |
operator | principal.user.userid | Kolom log mentah "operator" dipetakan ke kolom UDM ini jika tidak cocok dengan regex email. |
Nama Ruang | target.user.attribute.labels.value | Kolom log mentah "Room Name" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Nama Peran | target.user.attribute.roles.name | Kolom log mentah "Nama Peran" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
waktu | metadata.event_timestamp.seconds | Kolom log mentah "time" diuraikan dan dipetakan ke kolom UDM ini. |
Jenis | target.user.attribute.labels.value | Kolom log mentah "Type" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Peran Pengguna | target.user.attribute.roles.name | Kolom log mentah "User Role" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Jenis Pengguna | target.user.attribute.labels.value | Kolom log mentah "Jenis Pengguna" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
metadata.log_type | Nilai "ZOOM_OPERATION_LOGS" ditetapkan ke kolom UDM ini. | |
metadata.vendor_name | Nilai "ZOOM" ditetapkan ke kolom UDM ini. | |
metadata.product_name | Nilai "ZOOM_OPERATION_LOGS" ditetapkan ke kolom UDM ini. | |
metadata.event_type | Nilai ditentukan berdasarkan logika berikut: 1. Jika kolom "event_type" tidak kosong, nilainya akan digunakan. 2. Jika kolom "operator", "email", atau "email2" tidak kosong, nilai akan ditetapkan ke "USER_UNCATEGORIZED". 3. Jika tidak, nilai ditetapkan ke "GENERIC_EVENT". |
|
json_data | about.user.attribute.labels.value | Kolom log mentah "json_data" (diekstrak dari kolom "operation_detail") diuraikan sebagai JSON. Kolom "assistant" dan "options" dari setiap elemen array JSON yang diuraikan dipetakan ke kolom "value" dari array "labels" di UDM. |
json_data | about.user.userid | Kolom log mentah "json_data" (diekstrak dari kolom "operation_detail") diuraikan sebagai JSON. Kolom "userId" dari setiap elemen array JSON yang diuraikan (kecuali yang pertama) dipetakan ke kolom "userid" objek "about.user" di UDM. |
json_data | target.user.attribute.labels.value | Kolom log mentah "json_data" (diekstrak dari kolom "operation_detail") diuraikan sebagai JSON. Kolom "assistant" dan "options" dari elemen pertama array JSON yang diuraikan dipetakan ke kolom "value" dari array "labels" di UDM. |
json_data | target.user.userid | Kolom log mentah "json_data" (diekstrak dari kolom "operation_detail") diuraikan sebagai JSON. Kolom "userId" dari elemen pertama array JSON yang diuraikan dipetakan ke kolom "userid" objek "target.user" di UDM. |
target.user.email_addresses | Kolom log mentah "email" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. | |
email2 | target.user.email_addresses | Kolom log mentah "email2" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
peran | target.user.attribute.roles.name | Kolom log mentah "role" (diekstrak dari kolom "operation_detail") dipetakan ke kolom UDM ini. |
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.