Mengumpulkan log CloudM
Versi Parser: 6.0
Dokumen ini menjelaskan cara menyerap log CloudM ke Google Security Operations menggunakan Google Cloud Storage V2.
CloudM adalah platform SaaS untuk Google Workspace dan Microsoft 365 yang menyediakan otomatisasi alur kerja untuk orientasi dan penghentian pengguna, pencadangan data, pengarsipan, dan migrasi. CloudM Automate menghasilkan log audit lengkap dari semua tindakan yang dilakukan di seluruh domain Anda, termasuk peristiwa pengelolaan pengguna, langkah-langkah alur kerja penghentian penggunaan, perubahan konfigurasi, dan operasi terkait keamanan. Data log audit dari tahun lalu dipertahankan.
Sebelum memulai
Pastikan Anda memiliki prasyarat berikut:
- Instance Google SecOps
- Project Google Cloud dengan Cloud Storage API diaktifkan
- Izin untuk membuat dan mengelola bucket Cloud Storage
- Izin untuk mengelola kebijakan Identity and Access Management (IAM) pada bucket Cloud Storage
- Izin untuk membuat layanan Cloud Run, topik Pub/Sub, dan tugas Cloud Scheduler
- Akses administrator ke instance CloudM Automate Anda dengan izin Edit Global Settings
- URL instance CloudM Automate Anda (misalnya,
yourcompany.cloudm.io) - ID domain CloudM Anda
Mengumpulkan kredensial CloudM Automate
Membuat peran khusus untuk akses log API
- Login ke instance CloudM Automate Anda.
- Buka Setelan > Peran.
- Klik Tambahkan Peran untuk membuat peran baru.
- Di kolom Role Name, masukkan nama deskriptif (misalnya,
Google SecOps Log Reader). Di daftar izin, aktifkan izin berikut:
- Melihat Log: Memberikan kemampuan untuk melihat semua log aplikasi.
Simpan peran.
Buat akun layanan dan tetapkan peran
- Di CloudM Automate, buka Settings > Roles.
- Buat atau identifikasi akun layanan yang akan digunakan untuk akses API.
- Tetapkan peran Google SecOps Log Reader ke akun layanan.
- Pastikan peran ditetapkan dengan cakupan global sehingga akun layanan dapat mengakses log di seluruh domain.
Mendapatkan token akses akun layanan
- Buat token akses untuk akun layanan.
- Token akses digunakan sebagai token Bearer di header
Authorizationsaat membuat permintaan API ke CloudM Logs API. Catat nilai berikut:
- URL Instance Automate: URL instance CloudM Automate Anda (misalnya,
yourcompany.cloudm.io) - ID Domain: ID domain CloudM Anda
- Token Akses Akun Layanan: Token Bearer untuk autentikasi API
- URL Instance Automate: URL instance CloudM Automate Anda (misalnya,
Verifikasi izin
Untuk memverifikasi bahwa akun memiliki izin yang diperlukan:
- Login ke CloudM Automate.
- Buka Setelan > Peran.
- Pastikan akun layanan memiliki izin Lihat Log yang ditetapkan dengan cakupan global.
- Jika Anda tidak dapat melihat opsi ini, hubungi administrator Anda untuk memberikan izin Edit Setelan Global dan Lihat Log.
Menguji akses API
Uji kredensial Anda sebelum melanjutkan integrasi:
# Replace with your actual credentials CLOUDM_INSTANCE="yourcompany.cloudm.io" DOMAIN_ID="your-domain-id" ACCESS_TOKEN="your-access-token" # Test API access curl -v -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://${CLOUDM_INSTANCE}/_ah/api/events/v1/${DOMAIN_ID}?from=$(date -u +%Y-%m-%d)&to=$(date -u +%Y-%m-%d)"Respons yang berhasil akan menampilkan array JSON peristiwa log audit.
Izin API yang diperlukan
Akun layanan memerlukan izin berikut:
Izin Tingkat Akses Tujuan Melihat Log Global Mengambil semua peristiwa log audit dari CloudM Automate
Membuat bucket Google Cloud Storage
- Buka Konsol Google Cloud.
- Pilih project Anda atau buat project baru.
- Di menu navigasi, buka Cloud Storage > Buckets.
- Klik Create bucket.
Berikan detail konfigurasi berikut:
Setelan Nilai Beri nama bucket Anda Masukkan nama yang unik secara global (misalnya, cloudm-audit-logs)Location type Pilih berdasarkan kebutuhan Anda (Region, Dual-region, Multi-region) Location Pilih lokasi (misalnya, us-central1)Kelas penyimpanan Standar (direkomendasikan untuk log yang sering diakses) Access control Seragam (direkomendasikan) Alat perlindungan Opsional: Aktifkan pembuatan versi objek atau kebijakan retensi Klik Create.
Buat akun layanan untuk Cloud Run Function
Fungsi Cloud Run memerlukan akun layanan dengan izin untuk menulis ke bucket Cloud Storage dan dipanggil oleh Pub/Sub.
Membuat akun layanan
- Di Google Cloud Console, buka IAM & Admin > Service Accounts.
- Klik Create Service Account.
- Berikan detail konfigurasi berikut:
- Nama akun layanan: Masukkan
cloudm-audit-collector-sa - Deskripsi akun layanan: Masukkan
Service account for Cloud Run function to collect CloudM audit logs
- Nama akun layanan: Masukkan
- Klik Create and Continue.
Di bagian Grant this service account access to project, tambahkan peran berikut:
- Klik Pilih peran.
- Telusuri dan pilih Storage Object Admin.
- Klik + Add another role.
- Telusuri dan pilih Cloud Run Invoker.
- Klik + Add another role.
- Telusuri dan pilih Cloud Functions Invoker.
Klik Lanjutkan.
Klik Done.
Peran ini diperlukan untuk:
- Storage Object Admin: Menulis log ke bucket Cloud Storage dan mengelola file status
- Cloud Run Invoker: Mengizinkan Pub/Sub memanggil fungsi
- Cloud Functions Invoker: Mengizinkan pemanggilan fungsi
Memberikan izin IAM pada bucket Cloud Storage
Beri akun layanan izin tulis di bucket Cloud Storage:
- Buka Cloud Storage > Buckets.
- Klik nama bucket Anda (
cloudm-audit-logs). - Buka tab Izin.
- Klik Grant access.
Berikan detail konfigurasi berikut:
- Tambahkan prinsipal: Masukkan email akun layanan (
cloudm-audit-collector-sa@PROJECT_ID.iam.gserviceaccount.com) - Tetapkan peran: Pilih Storage Object Admin
- Tambahkan prinsipal: Masukkan email akun layanan (
Klik Simpan.
Membuat topik Pub/Sub
Buat topik Pub/Sub yang akan dipublikasikan oleh Cloud Scheduler dan akan dilanggan oleh fungsi Cloud Run.
- Di Google Cloud Console, buka Pub/Sub > Topics.
- Klik Create topic.
Berikan detail konfigurasi berikut:
- ID Topik: Masukkan
cloudm-audit-trigger - Biarkan setelan lainnya menggunakan setelan default
- ID Topik: Masukkan
Klik Create.
Membuat fungsi Cloud Run untuk mengumpulkan log
Fungsi Cloud Run akan dipicu oleh pesan Pub/Sub dari Cloud Scheduler untuk mengambil log dari CloudM Automate Logs API dan menuliskannya ke Cloud Storage.
- Di Google Cloud Console, buka Cloud Run.
- Klik Create service.
- Pilih Function (gunakan editor inline untuk membuat fungsi).
Di bagian Konfigurasi, berikan detail konfigurasi berikut:
Setelan Nilai Nama layanan cloudm-audit-collectorRegion Pilih region yang cocok dengan bucket Cloud Storage Anda (misalnya, us-central1)Runtime Pilih Python 3.12 atau yang lebih baru Di bagian Pemicu (opsional):
- Klik + Tambahkan pemicu.
- Pilih Cloud Pub/Sub.
- Di Select a Cloud Pub/Sub topic, pilih
cloudm-audit-trigger. - Klik Simpan.
Di bagian Authentication:
- Pilih Wajibkan autentikasi.
- Periksa Identity and Access Management (IAM).
Scroll ke bawah dan luaskan Containers, Networking, Security.
Buka tab Security:
- Akun layanan: Pilih
cloudm-audit-collector-sa
- Akun layanan: Pilih
Buka tab Containers:
- Klik Variables & Secrets.
Klik + Add variable untuk setiap variabel lingkungan:
Nama Variabel Nilai Contoh Deskripsi GCS_BUCKETcloudm-audit-logsNama bucket Cloud Storage GCS_PREFIXcloudm-auditAwalan untuk file log STATE_KEYcloudm-audit/state.jsonJalur file status CLOUDM_INSTANCE_URLyourcompany.cloudm.ioURL instance CloudM Automate CLOUDM_DOMAIN_IDyour-domain-idID domain CloudM CLOUDM_ACCESS_TOKENyour-access-tokenToken Bearer akun layanan CloudM LOOKBACK_HOURS24Periode lihat balik awal
Di bagian Variables & Secrets, scroll ke bawah ke Requests:
- Waktu tunggu permintaan: Masukkan
600detik (10 menit)
- Waktu tunggu permintaan: Masukkan
Buka tab Setelan:
Di bagian Resources:
- Memori: Pilih 512 MiB atau yang lebih tinggi
- CPU: Pilih 1
Di bagian Penskalaan revisi:
- Minimum number of instances: Masukkan
0 - Maximum number of instances: Masukkan
100
- Minimum number of instances: Masukkan
Klik Create.
Tunggu hingga layanan dibuat (1-2 menit).
Setelah layanan dibuat, editor kode inline akan terbuka secara otomatis.
Menambahkan kode fungsi
- Masukkan main di kolom Entry point.
Di editor kode inline, buat dua file:
main.py:
import functions_framework from google.cloud import storage import json import os import urllib3 from datetime import datetime, timezone, timedelta http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=10.0, read=60.0), retries=False, ) storage_client = storage.Client() GCS_BUCKET = os.environ.get('GCS_BUCKET') GCS_PREFIX = os.environ.get('GCS_PREFIX', 'cloudm-audit') STATE_KEY = os.environ.get('STATE_KEY', 'cloudm-audit/state.json') CLOUDM_INSTANCE_URL = os.environ.get('CLOUDM_INSTANCE_URL', '').rstrip('/') CLOUDM_DOMAIN_ID = os.environ.get('CLOUDM_DOMAIN_ID') CLOUDM_ACCESS_TOKEN = os.environ.get('CLOUDM_ACCESS_TOKEN') LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24')) @functions_framework.cloud_event def main(cloud_event): if not all([GCS_BUCKET, CLOUDM_INSTANCE_URL, CLOUDM_DOMAIN_ID, CLOUDM_ACCESS_TOKEN]): print('Error: Missing required environment variables') return try: bucket = storage_client.bucket(GCS_BUCKET) state = load_state(bucket) now = datetime.now(timezone.utc) if isinstance(state, dict) and state.get('last_event_date'): try: last_date = state['last_event_date'] last_time = datetime.strptime(last_date, '%Y-%m-%d').replace(tzinfo=timezone.utc) except Exception as e: print(f"Warning: Could not parse last_event_date: {e}") last_time = now - timedelta(hours=LOOKBACK_HOURS) else: last_time = now - timedelta(hours=LOOKBACK_HOURS) from_date = last_time.strftime('%Y-%m-%d') to_date = now.strftime('%Y-%m-%d') print(f"Fetching logs from {from_date} to {to_date}") records = fetch_logs(from_date, to_date) if not records: print("No new log records found.") save_state(bucket, to_date) return timestamp = now.strftime('%Y%m%d_%H%M%S') object_key = f"{GCS_PREFIX}/cloudm_audit_{timestamp}.ndjson" blob = bucket.blob(object_key) ndjson = '\n'.join( [json.dumps(record, ensure_ascii=False, default=str) for record in records] ) + '\n' blob.upload_from_string(ndjson, content_type='application/x-ndjson') print(f"Wrote {len(records)} records to gs://{GCS_BUCKET}/{object_key}") save_state(bucket, to_date) print(f"Successfully processed {len(records)} records") except Exception as e: print(f'Error processing logs: {str(e)}') raise def fetch_logs(from_date, to_date): instance = CLOUDM_INSTANCE_URL if not instance.startswith('https://'): instance = f"https://{instance}" endpoint = f"{instance}/_ah/api/events/v1/{CLOUDM_DOMAIN_ID}" headers = { 'Authorization': f'Bearer {CLOUDM_ACCESS_TOKEN}', 'Accept': 'application/json', 'User-Agent': 'GoogleSecOps-CloudMCollector/1.0' } url = f"{endpoint}?from={from_date}&to={to_date}" try: response = http.request('GET', url, headers=headers) if response.status == 429: retry_after = int(response.headers.get('Retry-After', '60')) print(f"Rate limited (429). Retry after {retry_after}s.") return [] if response.status != 200: print(f"HTTP Error: {response.status}") response_text = response.data.decode('utf-8') print(f"Response body: {response_text}") return [] data = json.loads(response.data.decode('utf-8')) if isinstance(data, list): records = data elif isinstance(data, dict): records = data.get('items', data.get('events', [data])) else: records = [] print(f"Retrieved {len(records)} events") return records except Exception as e: print(f"Error fetching logs: {e}") return [] def load_state(bucket): try: blob = bucket.blob(STATE_KEY) if blob.exists(): return json.loads(blob.download_as_text()) except Exception as e: print(f"Warning: Could not load state: {e}") return {} def save_state(bucket, last_event_date): try: state = { 'last_event_date': last_event_date, 'last_run': datetime.now(timezone.utc).isoformat() } blob = bucket.blob(STATE_KEY) blob.upload_from_string( json.dumps(state, indent=2), content_type='application/json' ) print(f"Saved state: last_event_date={last_event_date}") except Exception as e: print(f"Warning: Could not save state: {e}")requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0
Klik Deploy untuk menyimpan dan men-deploy fungsi.
Tunggu hingga deployment selesai (2-3 menit).
Buat tugas Cloud Scheduler
Cloud Scheduler akan memublikasikan pesan ke topik Pub/Sub secara berkala, sehingga memicu fungsi Cloud Run.
- Di Google Cloud Console, buka Cloud Scheduler.
- Klik Create Job.
Berikan detail konfigurasi berikut:
Setelan Nilai Nama cloudm-audit-collector-hourlyRegion Pilih region yang sama dengan fungsi Cloud Run Frekuensi 0 * * * *(setiap jam, tepat pada waktunya)Zona Waktu Pilih zona waktu (UTC direkomendasikan) Jenis target Pub/Sub Topik Pilih cloudm-audit-triggerIsi pesan {}(objek JSON kosong)Klik Create.
Menguji integrasi
- Di konsol Cloud Scheduler, temukan tugas Anda (
cloudm-audit-collector-hourly). - Klik Force run untuk memicu tugas secara manual.
- Tunggu beberapa detik.
- Buka Cloud Run > Services.
- Klik
cloudm-audit-collector. - Klik tab Logs.
Pastikan fungsi berhasil dieksekusi. Cari:
Fetching logs from YYYY-MM-DD to YYYY-MM-DD Retrieved X events Wrote X records to gs://cloudm-audit-logs/cloudm-audit/cloudm_audit_YYYYMMDD_HHMMSS.ndjson Successfully processed X recordsBuka Cloud Storage > Buckets.
Klik
cloudm-audit-logs.Buka folder
cloudm-audit/.Pastikan file
.ndjsonbaru dibuat dengan stempel waktu saat ini.
Jika Anda melihat error dalam log:
- HTTP 401: Pastikan variabel lingkungan
CLOUDM_ACCESS_TOKENsudah benar. - HTTP 403: Pastikan akun layanan memiliki izin Lihat Log dengan cakupan global.
- HTTP 429: Pembatasan kecepatan—fungsi akan berhenti dan dilanjutkan pada jadwal berikutnya.
- Variabel lingkungan tidak ada: Pastikan semua variabel yang diperlukan ditetapkan dalam konfigurasi fungsi Cloud Run
Mengambil akun layanan Google SecOps
Google SecOps menggunakan akun layanan unik untuk membaca data dari bucket Cloud Storage Anda. Anda harus memberi akun layanan ini akses ke bucket Anda.
Dapatkan email akun layanan
- Buka Setelan SIEM > Feed.
- Klik Tambahkan Feed Baru.
- Klik Konfigurasi satu feed.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
CloudM Audit Logs). - Pilih Google Cloud Storage V2 sebagai Source type.
- Pilih CloudM sebagai Log type.
Klik Get Service Account.
Email akun layanan yang unik akan ditampilkan, misalnya:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comSalin alamat email ini untuk digunakan di langkah berikutnya.
Klik Berikutnya.
Tentukan nilai untuk parameter input berikut:
URL bucket penyimpanan: Masukkan URI bucket Cloud Storage dengan jalur awalan:
gs://cloudm-audit-logs/cloudm-audit/Opsi penghapusan sumber: Pilih opsi penghapusan sesuai preferensi Anda:
- Jangan pernah: Tidak pernah menghapus file apa pun setelah transfer (direkomendasikan untuk pengujian).
- Hapus file yang ditransfer: Menghapus file setelah transfer berhasil.
Hapus file yang ditransfer dan direktori kosong: Menghapus file dan direktori kosong setelah transfer berhasil.
Usia File Maksimum: Sertakan file yang dimodifikasi dalam beberapa hari terakhir (defaultnya adalah 180 hari).
Namespace aset: Namespace aset.
Label penyerapan: Label yang akan diterapkan ke peristiwa dari feed ini.
Klik Berikutnya.
Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.
Memberikan izin IAM ke akun layanan Google SecOps
Akun layanan Google SecOps memerlukan peran Storage Object Viewer di bucket Cloud Storage Anda.
- Buka Cloud Storage > Buckets.
- Klik
cloudm-audit-logs. - Buka tab Izin.
- Klik Grant access.
Berikan detail konfigurasi berikut:
- Add principals: Tempel email akun layanan Google SecOps
- Tetapkan peran: Pilih Storage Object Viewer
Klik Simpan.
Parameter CloudM Logs API
CloudM Logs API mendukung parameter kueri berikut untuk memfilter peristiwa log:
| Parameter | Format | Deskripsi |
|---|---|---|
| byUser | Alamat email | Memfilter peristiwa menurut pengguna yang melakukan tindakan (mirip dengan Pengguna di UI CloudM) |
| dari | yyyy-MM-dd |
Tanggal mulai untuk filter rentang tanggal |
| hingga | yyyy-MM-dd |
Tanggal akhir untuk filter rentang tanggal |
| contextType | String | Memfilter menurut jenis konteks (misalnya, profil, grup, OU) |
| contextName | String | Memfilter menurut target tindakan (misalnya, pengguna tertentu yang dihentikan aksesnya) |
| operasi | String | Filter menurut jenis operasi (misalnya, tetapkan alias, tangguhkan pengguna) |
| country | Kode negara | Memfilter menurut kode negara geolokasi |
Tabel pemetaan UDM
| Kolom Log | Pemetaan UDM | Logika |
|---|---|---|
| tentang | tentang | Nilai disalin secara langsung |
| Context_Name | about.labels | Digabungkan sebagai pasangan kunci-nilai dari about_Context_Name, about_Context_Type, labels0 |
| Context_Type | about.labels | |
| Login_Type | about.labels | |
| Penerbit | additional.fields | Digabungkan dari additional_field0, additional_field1, additional_field2 |
| SAML_code | additional.fields | |
| SAML_ACS_Url | additional.fields | |
| Operasi | extensions.auth.type | Disetel ke SSO jika Operasi cocok dengan SSORequest, AUTHTYPE_UNSPECIFIED jika Context_Type adalah LoginUser |
| Context_Type | extensions.auth.type | |
| Stempel waktu | metadata.event_timestamp | Mengekstrak tanggal dan waktu serta zona waktu dari Stempel Waktu, zona waktu dikonversi menjadi offset, digabungkan, dan diuraikan sebagai stempel waktu |
| Operasi | metadata.event_type | Ditetapkan ke USER_UNCATEGORIZED jika Operasi cocok dengan Update/Delete/SuspendUser/UnsuspendUser/Create, USER_LOGIN jika Operasi cocok dengan SSORequest/SSORequestFail atau Context_Type adalah LoginUser, STATUS_UPDATE jika IP tidak kosong, atau GENERIC_EVENT |
| Context_Type | metadata.event_type | |
| User_Agent | network.http.user_agent | Nilai disalin secara langsung |
| utama | utama | Diganti namanya dari principal jika Context_Type != LoginUser, atau dari target |
| target | utama | |
| Organization_Unit | principal.administrative_domain | Nilai disalin secara langsung |
| IP | principal.ip | Nilai disalin secara langsung |
| Kota | principal.location.city | Nilai disalin secara langsung |
| Negara | principal.location.country_or_region | Nilai disalin secara langsung |
| Geolokasi | principal.location.region_latitude | Mengekstrak garis lintang dari Geolokasi menggunakan grok |
| Geolokasi | principal.location.region_longitude | Mengekstrak bujur dari Geolokasi menggunakan grok |
| Wilayah | principal.location.state | Nilai disalin secara langsung |
| Pelaku | principal.user.attribute.roles | Disetel ke role.name jika Aktor bukan email dan tidak kosong, lalu digabungkan |
| Pelaku | principal.user.email_addresses | Nilai disalin langsung jika Pelaku cocok dengan regex email |
| Pesan | principal.user.userid | Mengekstrak nama pengguna dari Pesan menggunakan grok |
| security_result | security_result | Menggabungkan objek security_result |
| SAML_code | security_result.action | Ditetapkan ke ALLOW jika SAML_code cocok dengan Success, BLOCK jika RequestDenied |
| Pesan | security_result.description | Nilai disalin secara langsung |
| Keparahan | security_result.severity | Setel ke huruf besar jika Error/Kritis, INFORMATIONAL jika Info, MEDIUM jika Peringatan, atau UNKNOWN_SEVERITY |
| Operasi | security_result.summary | Nilai disalin secara langsung |
| target | target | Diganti namanya dari target jika Context_Type != LoginUser, atau dari akun utama |
| utama | target | |
| metadata.product_name | metadata.product_name | Tetapkan ke "CLOUDM" |
| metadata.vendor_name | metadata.vendor_name | Tetapkan ke "CLOUDM" |
Log Perubahan
Melihat Log Perubahan untuk parser ini
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.