Mengumpulkan log Zendesk CRM
Dokumen ini menjelaskan cara menyerap log Pengelolaan Hubungan Pelanggan (CRM) Zendesk ke Google Security Operations menggunakan Amazon S3.
Sebelum memulai
Pastikan Anda memenuhi prasyarat berikut:
- Instance Google SecOps.
- Akses istimewa ke Zendesk.
- Akses istimewa ke AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).
Mendapatkan prasyarat Zendesk
- Konfirmasi paket & peran
- Anda harus menjadi Admin Zendesk untuk membuat token API / klien OAuth. Audit Logs API hanya tersedia di paket Enterprise. (Jika akun Anda bukan akun Enterprise, lewati
audit_logs
diRESOURCES
.)
- Anda harus menjadi Admin Zendesk untuk membuat token API / klien OAuth. Audit Logs API hanya tersedia di paket Enterprise. (Jika akun Anda bukan akun Enterprise, lewati
- Mengaktifkan akses token API (sekali)
- Di Pusat Admin, buka Aplikasi dan integrasi > API > Konfigurasi API.
- Aktifkan Izinkan akses token API.
- Membuat token API (untuk autentikasi Dasar)
- Buka Aplikasi dan integrasi > API > Token API.
- Klik Tambahkan token API > (opsional) tambahkan Deskripsi > Simpan.
- Salin dan simpan token API sekarang (Anda tidak akan dapat melihatnya lagi).
- Simpan email admin yang akan diautentikasi dengan token ini.
- Format autentikasi dasar yang digunakan oleh Lambda:
email_address/token:api_token
- Format autentikasi dasar yang digunakan oleh Lambda:
- (Opsional) Buat klien OAuth (untuk autentikasi Bearer, bukan token API)
- Buka Aplikasi dan integrasi > API > Klien OAuth > Tambahkan klien OAuth.
- Isi Nama, ID Unik (otomatis), URL Pengalihan (dapat berupa placeholder jika Anda hanya mencetak token dengan API), dan Simpan.
- Buat token akses untuk integrasi dan berikan cakupan minimum yang diperlukan oleh panduan ini:
tickets:read
(untuk Tiket Bertahap)auditlogs:read
(untuk Log Audit; hanya untuk Enterprise)- Jika tidak yakin,
read
juga berfungsi untuk akses hanya baca.
- Salin token akses (tempel ke
ZENDESK_BEARER_TOKEN
) dan catat ID/rahasia klien dengan aman (untuk alur refresh token pada masa mendatang).
Mencatat URL dasar Zendesk Anda
- Gunakan
https://<your_subdomain>.zendesk.com
(tempel ke variabel lingkunganZENDESK_BASE_URL
).
Data yang perlu disalin & disimpan untuk digunakan nanti
- URL Dasar (misalnya,
https://acme.zendesk.com
) - Alamat Email pengguna administrator (untuk autentikasi token API)
- Token API (jika menggunakan
AUTH_MODE=token
) - atau token akses OAuth (jika menggunakan
AUTH_MODE=bearer
) - (Opsional): ID/rahasia klien OAuth untuk pengelolaan siklus proses
- Gunakan
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,
zendesk-crm-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 file .CSV untuk menyimpan Kunci Akses dan Kunci Akses Rahasia untuk referensi di masa mendatang.
- Klik Selesai.
- Pilih tab Permissions.
- Klik Tambahkan izin di bagian Kebijakan izin.
- Pilih Tambahkan izin.
- Pilih Lampirkan kebijakan secara langsung.
- Cari kebijakan AmazonS3FullAccess.
- Pilih kebijakan.
- Klik Berikutnya.
- Klik Add permissions.
Mengonfigurasi kebijakan dan peran IAM untuk upload S3
- Di konsol AWS, buka IAM > Policies.
- Klik Buat kebijakan > tab JSON.
- Salin dan tempel kebijakan berikut.
Policy JSON (ganti
zendesk-crm-logs
jika Anda memasukkan nama bucket yang berbeda):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::zendesk-crm-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::zendesk-crm-logs/zendesk/crm/state.json" } ] }
Klik Berikutnya > Buat kebijakan.
Buka IAM > Roles > Create role > AWS service > Lambda.
Lampirkan kebijakan yang baru dibuat.
Beri nama peran
ZendeskCRMToS3Role
, 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 zendesk_crm_to_s3
Runtime Python 3.13 Arsitektur x86_64 Peran eksekusi ZendeskCRMToS3Role
Setelah fungsi dibuat, buka tab Code, hapus stub, dan tempelkan kode berikut (
zendesk_crm_to_s3.py
).#!/usr/bin/env python3 import os, json, time, base64 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", "zendesk/crm/") STATE_KEY = os.environ.get("STATE_KEY", "zendesk/crm/state.json") BASE_URL = os.environ["ZENDESK_BASE_URL"].rstrip("/") # e.g. https://your_subdomain.zendesk.com AUTH_MODE = os.environ.get("AUTH_MODE", "token").lower() # token|bearer EMAIL = os.environ.get("ZENDESK_EMAIL", "") API_TOKEN = os.environ.get("ZENDESK_API_TOKEN", "") BEARER = os.environ.get("ZENDESK_BEARER_TOKEN", "") RESOURCES = [r.strip() for r in os.environ.get("RESOURCES", "audit_logs,incremental_tickets").split(",") if r.strip()] MAX_PAGES = int(os.environ.get("MAX_PAGES", "20")) LOOKBACK = int(os.environ.get("LOOKBACK_SECONDS", "3600")) # 1h default HTTP_TIMEOUT = int(os.environ.get("HTTP_TIMEOUT", "60")) HTTP_RETRIES = int(os.environ.get("HTTP_RETRIES", "3")) s3 = boto3.client("s3") def _headers() -> dict: if AUTH_MODE == "bearer" and BEARER: return {"Authorization": f"Bearer {BEARER}", "Accept": "application/json"} if AUTH_MODE == "token" and EMAIL and API_TOKEN: token = base64.b64encode(f"{EMAIL}/token:{API_TOKEN}".encode()).decode() return {"Authorization": f"Basic {token}", "Accept": "application/json"} raise RuntimeError("Invalid auth settings: provide token (EMAIL + API_TOKEN) or BEARER") def _get_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) b = obj["Body"].read() return json.loads(b) if b else {"audit_logs": {}, "incremental_tickets": {}} except Exception: return {"audit_logs": {}, "incremental_tickets": {}} def _put_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 _http_get_json(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: return json.loads(r.read().decode("utf-8")) except HTTPError as e: if e.code in (429, 500, 502, 503, 504) and attempt < HTTP_RETRIES: ra = 1 + attempt try: ra = int(e.headers.get("Retry-After", ra)) except Exception: pass time.sleep(max(1, ra)) attempt += 1 continue raise except URLError: if attempt < HTTP_RETRIES: time.sleep(1 + attempt) attempt += 1 continue raise def _put_page(payload: dict, resource: str) -> str: ts = time.gmtime() key = f"{S3_PREFIX}/{time.strftime('%Y/%m/%d/%H%M%S', ts)}-zendesk-{resource}.json" s3.put_object( Bucket=S3_BUCKET, Key=key, Body=json.dumps(payload, separators=(",", ":")).encode("utf-8"), ContentType="application/json", ) return key def fetch_audit_logs(state: dict): """GET /api/v2/audit_logs.json with pagination via `next_page` (Zendesk).""" next_url = state.get("next_url") or f"{BASE_URL}/api/v2/audit_logs.json?page=1" pages = 0 written = 0 last_next = None while pages < MAX_PAGES and next_url: data = _http_get_json(next_url) _put_page(data, "audit_logs") written += len(data.get("audit_logs", [])) last_next = data.get("next_page") next_url = last_next pages += 1 return {"resource": "audit_logs", "pages": pages, "written": written, "next_url": last_next} def fetch_incremental_tickets(state: dict): """Cursor-based incremental export: /api/v2/incremental/tickets/cursor.json (pagination via `links.next`).""" next_link = state.get("next") if not next_link: start = int(time.time()) - LOOKBACK next_link = f"{BASE_URL}/api/v2/incremental/tickets/cursor.json?start_time={start}" pages = 0 written = 0 last_next = None while pages < MAX_PAGES and next_link: data = _http_get_json(next_link) _put_page(data, "incremental_tickets") written += len(data.get("tickets", [])) links = data.get("links") or {} next_link = links.get("next") last_next = next_link pages += 1 return {"resource": "incremental_tickets", "pages": pages, "written": written, "next": last_next} def lambda_handler(event=None, context=None): state = _get_state() summary = [] if "audit_logs" in RESOURCES: res = fetch_audit_logs(state.get("audit_logs", {})) state["audit_logs"] = {"next_url": res.get("next_url")} summary.append(res) if "incremental_tickets" in RESOURCES: res = fetch_incremental_tickets(state.get("incremental_tickets", {})) state["incremental_tickets"] = {"next": res.get("next")} summary.append(res) _put_state(state) return {"ok": True, "summary": summary} if __name__ == "__main__": print(lambda_handler())
Buka Configuration > Environment variables.
Klik Edit > Tambahkan variabel lingkungan baru.
Masukkan variabel lingkungan yang diberikan dalam tabel berikut, dengan mengganti nilai contoh dengan nilai Anda.
Variabel lingkungan
Kunci Nilai contoh S3_BUCKET
zendesk-crm-logs
S3_PREFIX
zendesk/crm/
STATE_KEY
zendesk/crm/state.json
ZENDESK_BASE_URL
https://your_subdomain.zendesk.com
AUTH_MODE
token
ZENDESK_EMAIL
analyst@example.com
ZENDESK_API_TOKEN
<api_token>
ZENDESK_BEARER_TOKEN
<leave empty unless using OAuth bearer>
RESOURCES
audit_logs,incremental_tickets
MAX_PAGES
20
LOOKBACK_SECONDS
3600
HTTP_TIMEOUT
60
Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda > Functions > your-function).
Pilih tab Configuration
Di panel Konfigurasi umum, klik Edit.
Ubah Waktu Tunggu menjadi 5 menit (300 detik), lalu klik Simpan.
Membuat jadwal EventBridge
- Buka Amazon EventBridge > Scheduler > Create schedule.
- Berikan detail konfigurasi berikut:
- Jadwal berulang: Tarif (
1 hour
). - Target: Fungsi Lambda Anda
zendesk_crm_to_s3
. - Name:
zendesk_crm_to_s3-1h
.
- Jadwal berulang: Tarif (
- Klik Buat jadwal.
(Opsional) Buat pengguna & kunci IAM hanya baca untuk Google SecOps
- Buka Konsol AWS > IAM > Pengguna > Tambahkan pengguna.
- Klik Add users.
- Berikan detail konfigurasi berikut:
- Pengguna: Masukkan
secops-reader
. - Jenis akses: Pilih Kunci akses – Akses terprogram.
- Pengguna: Masukkan
- Klik Buat pengguna.
- Lampirkan kebijakan baca minimal (kustom): Pengguna > secops-reader > Izin > Tambahkan izin > Lampirkan kebijakan secara langsung > Buat kebijakan.
JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::zendesk-crm-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::zendesk-crm-logs" } ] }
Nama =
secops-reader-policy
.Klik Buat kebijakan > cari/pilih > Berikutnya > Tambahkan izin.
Buat kunci akses untuk
secops-reader
: Kredensial keamanan > Kunci akses.Klik Create access key.
Download
.CSV
. (Anda akan menempelkan nilai ini ke feed).
Mengonfigurasi feed di Google SecOps untuk menyerap log CRM Zendesk
- Buka Setelan SIEM > Feed.
- Klik + Tambahkan Feed Baru.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
Zendesk CRM logs
). - Pilih Amazon S3 V2 sebagai Jenis sumber.
- Pilih Zendesk CRM sebagai Jenis log.
- Klik Berikutnya.
- Tentukan nilai untuk parameter input berikut:
- URI S3:
s3://zendesk-crm-logs/zendesk/crm/
- 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.
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.