Mengumpulkan log operasi Zoom

Didukung di:

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)

  1. Login ke Zoom App Marketplace.
  2. Buka Develop > Build App > Server-to-Server OAuth.
  3. Buat aplikasi dan tambahkan cakupan berikut: report:read:operation_logs:admin (atau report:read:admin).
  4. 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

  1. Buat bucket Amazon S3 dengan mengikuti panduan pengguna ini: Membuat bucket
  2. Simpan Name dan Region bucket untuk referensi di masa mendatang (misalnya, zoom-operation-logs).
  3. Buat pengguna dengan mengikuti panduan pengguna ini: Membuat pengguna IAM.
  4. Pilih Pengguna yang dibuat.
  5. Pilih tab Kredensial keamanan.
  6. Klik Create Access Key di bagian Access Keys.
  7. Pilih Layanan pihak ketiga sebagai Kasus penggunaan.
  8. Klik Berikutnya.
  9. Opsional: tambahkan tag deskripsi.
  10. Klik Create access key.
  11. Klik Download CSV file untuk menyimpan Access Key dan Secret Access Key untuk digunakan nanti.
  12. Klik Selesai.
  13. Pilih tab Izin.
  14. Klik Tambahkan izin di bagian Kebijakan izin.
  15. Pilih Tambahkan izin.
  16. Pilih Lampirkan kebijakan secara langsung
  17. Telusuri dan pilih kebijakan AmazonS3FullAccess.
  18. Klik Berikutnya.
  19. Klik Add permissions.

Mengonfigurasi kebijakan dan peran IAM untuk upload S3

  1. Di konsol AWS, buka IAM > Policies > Create policy > JSON tab.
  2. 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.
  3. Klik Berikutnya > Buat kebijakan.

  4. Buka IAM > Roles > Create role > AWS service > Lambda.

  5. Lampirkan kebijakan yang baru dibuat.

  6. Beri nama peran WriteZoomOperationLogsToS3Role, lalu klik Buat peran.

Buat fungsi Lambda

  1. Di Konsol AWS, buka Lambda > Functions > Create function.
  2. Klik Buat dari awal.
  3. Berikan detail konfigurasi berikut:
Setelan Nilai
Nama zoom_operationlogs_to_s3
Runtime Python 3.13
Arsitektur x86_64
Peran eksekusi WriteZoomOperationLogsToS3Role
  1. 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())
    
  2. Buka Configuration > Environment variables > Edit > Add new environment variable.

  3. 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
  4. Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda > Functions > your-function).

  5. Pilih tab Configuration

  6. Di panel General configuration, klik Edit.

  7. Ubah Waktu tunggu menjadi 5 menit (300 detik), lalu klik Simpan.

Membuat jadwal EventBridge

  1. Buka Amazon EventBridge > Scheduler.
  2. Klik Buat jadwal.
  3. Berikan detail konfigurasi berikut:
    • Jadwal berulang: Tarif (15 min).
    • Target: Fungsi Lambda Anda zoom_operationlogs_to_s3.
    • Name: zoom-operationlogs-schedule-15min.
  4. Klik Buat jadwal.

Opsional: Buat pengguna & kunci IAM hanya baca untuk Google SecOps

  1. Di Konsol AWS, buka IAM > Pengguna > Tambahkan pengguna.
  2. Klik Add users.
  3. Berikan detail konfigurasi berikut:
    • Pengguna: secops-reader.
    • Jenis akses: Kunci akses — Akses terprogram.
  4. Klik Buat pengguna.
  5. Lampirkan kebijakan baca minimal (kustom): Pengguna > secops-reader > Izin > Tambahkan izin > Lampirkan kebijakan secara langsung > Buat kebijakan.
  6. 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"
        }
      ]
    }
    
  7. Tetapkan nama ke secops-reader-policy.

  8. Buka Buat kebijakan > cari/pilih > Berikutnya > Tambahkan izin.

  9. Buka Kredensial keamanan > Kunci akses > Buat kunci akses.

  10. Download CSV (nilai ini dimasukkan ke dalam feed).

Mengonfigurasi feed di Google SecOps untuk menyerap Log Operasi Zoom

  1. Buka Setelan SIEM > Feed.
  2. Klik + Tambahkan Feed Baru.
  3. Di kolom Nama feed, masukkan nama untuk feed (misalnya, Zoom Operation Logs).
  4. Pilih Amazon S3 V2 sebagai Jenis sumber.
  5. Pilih Zoom Operation Logs sebagai Log type.
  6. Klik Berikutnya.
  7. 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.
  8. Klik Berikutnya.
  9. 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.
email 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.