Mengumpulkan log Aktivitas Pengguna Workday

Didukung di:

Dokumen ini menjelaskan cara menyerap log Aktivitas Pengguna Workday ke Google Security Operations menggunakan Cloud Storage V2.

Workday adalah platform keuangan dan pengelolaan sumber daya manusia (HCM) berbasis cloud yang menghasilkan log audit keamanan, autentikasi, dan aktivitas pengguna. REST API Logging Aktivitas Pengguna menyediakan akses terprogram ke log ini.

Sebelum memulai

Pastikan Anda memiliki prasyarat berikut:

  • Instance Google SecOps
  • Google Cloud Project dengan {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 istimewa ke Workday dengan izin Administrator Keamanan atau yang setara
  • Pencatatan Log Aktivitas Pengguna diaktifkan di tenant Workday Anda

Membuat bucket Cloud Storage

  1. Buka Google Cloud console.
  2. Pilih project Anda atau buat project baru.
  3. Di menu navigasi, buka Cloud Storage > Buckets.
  4. Klik Create bucket.
  5. Berikan detail konfigurasi berikut:

    Setelan Nilai
    Beri nama bucket Anda Masukkan nama yang unik secara global (misalnya, workday-activity-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)
    Kontrol akses Seragam (direkomendasikan)
    Alat perlindungan Opsional: Aktifkan pembuatan versi objek atau kebijakan retensi
  6. Klik Create.

Kumpulkan kredensial Workday API

Aktifkan logging aktivitas pengguna

  1. Login ke tenant Workday Anda sebagai Administrator Keamanan.
  2. Di kotak penelusuran Workday, telusuri dan buka tugas Edit Tenant Setup - System.
  3. Di bagian Security, centang kotak Enable User Activity Logging.
  4. Klik OK untuk menyimpan perubahan.

Membuat Pengguna Sistem Integrasi (ISU)

  1. Di kotak penelusuran Workday, telusuri Create Integration System User.
  2. Masukkan Nama Pengguna (misalnya, ISU_SIEM_Export) dan Sandi yang kuat.
  3. Hapus centang pada kotak Require New Password at Next Sign In.
  4. Klik Oke.
  5. Cari Create Security Group, lalu buat Integration System Security Group (Unconstrained).
  6. Tambahkan ISU (ISU_SIEM_Export) ke grup keamanan ini.
  7. Telusuri Lihat Domain dan temukan domain Pencatatan Aktivitas Pengguna. Berikan akses Get ke grup keamanan ISU untuk domain ini.
  8. Cari Activate Pending Security Policy Changes dan aktifkan perubahan.

Mendaftarkan klien API untuk OAuth

  1. Di kotak penelusuran Workday, telusuri Register API Client for Integrations.
  2. Masukkan Nama Klien (misalnya, SIEM_OAuth_Client).
  3. Pilih opsi Non-Expiring Refresh Tokens.
  4. Tambahkan cakupan: System (atau cakupan yang diperlukan untuk User Activity Logging API).
  5. Klik Oke.
  6. Salin dan simpan detail berikut di lokasi yang aman:
    • Client ID: ID klien API.
    • Client Secret: Rahasia klien API.
  7. Telusuri Manage Refresh Tokens for Integrations.
  8. Pilih akun ISU (ISU_SIEM_Export).
  9. Buat token refresh baru untuk klien API.
  10. Salin dan simpan Refresh Token.

Menentukan URL tenant

Endpoint API didasarkan pada tenant Workday Anda. Formatnya adalah:

Komponen Nilai
Endpoint token https://HOST/ccx/oauth2/TENANT/token
Activity Logging API https://HOST/ccx/api/privacy/v1/TENANT/activityLogging

Memverifikasi izin

Untuk memverifikasi bahwa ISU memiliki izin yang diperlukan:

  1. Login ke Workday sebagai Administrator Keamanan.
  2. Cari View Integration System User, lalu pilih ISU (ISU_SIEM_Export).
  3. Pastikan pengguna adalah anggota grup keamanan dengan akses Get ke domain User Activity Logging.
  4. Jika akses tidak ada, perbarui grup keamanan dan aktifkan perubahan kebijakan keamanan yang tertunda.

Menguji akses API

  • Uji kredensial Anda sebelum melanjutkan integrasi:

    ```bash
    # Replace with your actual credentials
    CLIENT_ID="your-client-id"
    CLIENT_SECRET="your-client-secret"
    REFRESH_TOKEN="your-refresh-token"
    HOST="your-workday-host"
    TENANT="your-tenant"
    
    # Get OAuth access token
    TOKEN=$(curl -s -X POST "https://${HOST}/ccx/oauth2/${TENANT}/token" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "grant_type=refresh_token&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&refresh_token=${REFRESH_TOKEN}" \
        | jq -r '.access_token')
    
    # Test API access
    curl -v -H "Authorization: Bearer ${TOKEN}" \
        "https://${HOST}/ccx/api/privacy/v1/${TENANT}/activityLogging?from=2024-01-01T00:00:00Z&to=2024-01-01T01:00:00Z"
    ```
    

Buat akun layanan untuk fungsi Cloud Run

Fungsi Cloud Run memerlukan akun layanan dengan izin untuk menulis ke bucket Cloud Storage dan dipanggil oleh Pub/Sub.

Membuat akun layanan

  1. Di Konsol GCP, buka IAM & Admin > Service Accounts.
  2. Klik Create Service Account.
  3. Berikan detail konfigurasi berikut:
    • Nama akun layanan: Masukkan workday-activity-collector-sa.
    • Deskripsi akun layanan: Masukkan Service account for Cloud Run function to collect Workday User Activity logs.
  4. Klik Create and Continue.
  5. Di bagian Grant this service account access to project, tambahkan peran berikut:
    1. Klik Pilih peran.
    2. Telusuri dan pilih Storage Object Admin.
    3. Klik + Add another role.
    4. Telusuri dan pilih Cloud Run Invoker.
    5. Klik + Add another role.
    6. Telusuri dan pilih Cloud Functions Invoker.
  6. Klik Lanjutkan.
  7. Klik Done.

Peran ini diperlukan untuk:

  • Storage Object Admin: Menulis log ke bucket Cloud Storage dan mengelola file status
  • Cloud Run Invoker: Izinkan Pub/Sub memanggil fungsi
  • Cloud Functions Invoker: Mengizinkan pemanggilan fungsi

Memberikan izin IAM pada bucket Cloud Storage

Beri izin tulis akun layanan di bucket Cloud Storage sebagai berikut:

  1. Buka Cloud Storage > Buckets.
  2. Klik nama bucket Anda (misalnya, workday-activity-logs).
  3. Buka tab Izin.
  4. Klik Grant access.
  5. Berikan detail konfigurasi berikut:
    • Tambahkan prinsipal: Masukkan email akun layanan (misalnya, workday-activity-collector-sa@your-project.iam.gserviceaccount.com).
    • Tetapkan peran: Pilih Storage Object Admin.
  6. Klik Simpan.

Membuat topik Pub/Sub

Buat topik Pub/Sub yang akan dipublikasikan oleh Cloud Scheduler dan yang akan dilanggan oleh fungsi Cloud Run.

  1. Di Konsol GCP, buka Pub/Sub > Topics.
  2. Klik Create topic.
  3. Berikan detail konfigurasi berikut:
    • ID Topik: Masukkan workday-activity-trigger.
    • Biarkan setelan lainnya sesuai default.
  4. 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 Workday User Activity Logging API dan menuliskannya ke Cloud Storage.

  1. Di Konsol GCP, buka Cloud Run.
  2. Klik Write a function.
  3. Di bagian Konfigurasi, berikan detail konfigurasi berikut:

    Setelan Nilai
    Nama layanan workday-activity-collector
    Region Pilih region yang cocok dengan bucket Cloud Storage Anda (misalnya, us-central1)
    Runtime Pilih Python 3.12 atau yang lebih baru
  4. Di bagian Pemicu, klik Tambahkan pemicu, lalu pilih Pemicu Pub/Sub.

  5. Di panel Pemicu Eventarc, berikan detail konfigurasi berikut:

    • Nama pemicu: Biarkan nama yang dibuat atau masukkan nama untuk pemicu.
    • Jenis pemicu: Pilih Sumber Google.
    • Penyedia peristiwa: Pilih Pub/Sub.
    • Jenis peristiwa: Pilih google.cloud.pubsub.topic.v1.messagePublished.
    • Select a Cloud Pub/Sub topic: Pilih topik workday-activity-trigger.
    • Region: Pilih region yang sama dengan fungsi.
    • Akun layanan: Pilih akun layanan workday-activity-collector-sa.
  6. Klik Save trigger.

  7. Di bagian Authentication:

    1. Pilih Wajibkan autentikasi.
    2. Pilih Identity and Access Management (IAM).
  8. Buka dan luaskan Containers, Networking, Security.

  9. Buka tab Security:

    • Akun layanan: Pilih akun layanan workday-activity-collector-sa.
  10. Buka tab Containers:

    1. Klik Variables & Secrets.
    2. Klik + Tambahkan variabel untuk setiap variabel lingkungan:
    Nama Variabel Nilai Contoh Deskripsi
    GCS_BUCKET workday-activity-logs Nama bucket Cloud Storage
    GCS_PREFIX workday Awalan untuk file log
    STATE_KEY workday-state.json Jalur status, di luar awalan log
    WORKDAY_HOST your-workday-host Nama host Workday
    WORKDAY_TENANT your-tenant Nama tenant Workday
    CLIENT_ID your-client-id Client ID OAuth
    CLIENT_SECRET your-client-secret Rahasia klien OAuth
    REFRESH_TOKEN your-refresh-token Token refresh OAuth
    MAX_RECORDS 10000 Jumlah data maks per proses
    PAGE_SIZE 100 Data per halaman
    LOOKBACK_HOURS 24 Periode lihat balik awal
  11. Di tab Variables & Secrets, buka Requests:

    • Waktu tunggu permintaan: Masukkan 600 detik (10 menit).
  12. Buka tab Setelan di Penampung:

    • Di bagian Referensi:
      • Memori: Pilih 512 MiB atau lebih tinggi.
      • CPU: Pilih 1.
  13. Di bagian Penskalaan revisi:

    • Minimum number of instances: Masukkan 0.
    • Maximum number of instances: Masukkan 100 (atau sesuaikan berdasarkan perkiraan beban).
  14. Klik Create.

  15. Tunggu hingga layanan dibuat (1-2 menit).

  16. Setelah layanan dibuat, editor kode inline akan terbuka secara otomatis.

Tambahkan kode fungsi

  1. Masukkan main di Function entry point.
  2. Di editor kode inline, buat dua file:

    • File pertama - main.py:

      import functions_framework
      from google.cloud import storage
      import json
      import os
      import urllib3
      from datetime import datetime, timezone, timedelta
      import time
      import hashlib
      
      # Initialize HTTP client with timeouts
      http = urllib3.PoolManager(
          timeout=urllib3.Timeout(connect=5.0, read=30.0),
          retries=False,
      )
      
      # Initialize Storage client
      storage_client = storage.Client()
      
      # Environment variables
      GCS_BUCKET = os.environ.get('GCS_BUCKET')
      GCS_PREFIX = os.environ.get('GCS_PREFIX', 'workday')
      STATE_KEY = os.environ.get('STATE_KEY', 'workday-state.json')
      WORKDAY_HOST = os.environ.get('WORKDAY_HOST', '')
      WORKDAY_TENANT = os.environ.get('WORKDAY_TENANT', '')
      CLIENT_ID = os.environ.get('CLIENT_ID', '')
      CLIENT_SECRET = os.environ.get('CLIENT_SECRET', '')
      REFRESH_TOKEN = os.environ.get('REFRESH_TOKEN', '')
      MAX_RECORDS = int(os.environ.get('MAX_RECORDS', '10000'))
      PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '100'))
      LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24'))
      
      def parse_datetime(value: str) -> datetime:
          """Parse ISO datetime string to datetime object."""
          if value.endswith("Z"):
              value = value[:-1] + "+00:00"
          return datetime.fromisoformat(value)
      
      # Activity Logging entries carry no per-event identifier (taskId and
      # sessionId are reused), so the content hash is the identity.
      ID_FIELDS = ()
      
      def event_keys(event):
          """Return every identity this event can be recognised by."""
          keys = {'sha256:' + hashlib.sha256(
              json.dumps(event, sort_keys=True, ensure_ascii=False).encode('utf-8')
          ).hexdigest()}
          for field in ID_FIELDS:
              value = event.get(field)
              if value:
                  keys.add(f'{field}:{value}')
          return keys
      
      def get_token():
          """Get OAuth 2.0 access token using refresh token flow."""
          token_url = f"https://{WORKDAY_HOST}/ccx/oauth2/{WORKDAY_TENANT}/token"
      
          body = f"grant_type=refresh_token&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&refresh_token={REFRESH_TOKEN}"
      
          headers = {
              'Content-Type': 'application/x-www-form-urlencoded',
              'Accept': 'application/json'
          }
      
          backoff = 1.0
          max_retries = 3
      
          for attempt in range(max_retries):
              response = http.request('POST', token_url, body=body.encode('utf-8'), headers=headers)
      
              if response.status == 429:
                  retry_after = int(response.headers.get('Retry-After', str(int(backoff))))
                  print(f"Rate limited (429) on token request. Retrying after {retry_after}s...")
                  time.sleep(retry_after)
                  backoff = min(backoff * 2, 30.0)
                  continue
      
              if response.status != 200:
                  raise RuntimeError(f"Failed to get access token: {response.status} - {response.data.decode('utf-8')}")
      
              data = json.loads(response.data.decode('utf-8'))
              return data['access_token']
      
          raise RuntimeError(f"Failed to get token after {max_retries} retries due to rate limiting")
      
      @functions_framework.cloud_event
      def main(cloud_event):
          """
          Cloud Run function triggered by Pub/Sub to fetch Workday User Activity logs and write to GCS.
      
          Args:
              cloud_event: CloudEvent object containing Pub/Sub message
          """
      
          if not all([GCS_BUCKET, WORKDAY_HOST, WORKDAY_TENANT, CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN]):
              print('Error: Missing required environment variables')
              return
      
          try:
              bucket = storage_client.bucket(GCS_BUCKET)
      
              # Load state
              state = load_state(bucket, STATE_KEY)
              seen_keys = load_seen(bucket, STATE_KEY)
      
              # Determine time window
              now = datetime.now(timezone.utc)
              last_time = None
      
              if isinstance(state, dict) and state.get("last_event_time"):
                  try:
                      last_time = parse_datetime(state["last_event_time"])
                      last_time = last_time - timedelta(minutes=2)
                  except Exception as e:
                      print(f"Warning: Could not parse last_event_time: {e}")
      
              if last_time is None:
                  last_time = now - timedelta(hours=LOOKBACK_HOURS)
      
              print(f"Fetching logs from {last_time.isoformat()} to {now.isoformat()}")
      
              # Get access token
              token = get_token()
      
              # Fetch logs
              records, newest_event_time = fetch_logs(
                  token=token,
                  start_time=last_time,
                  end_time=now,
                  page_size=PAGE_SIZE,
                  max_records=MAX_RECORDS,
              )
      
              # Drop the events the overlap window re-read from the previous run
              fresh = [r for r in records if not (event_keys(r) & seen_keys)]
              all_keys = set()
              for record in records:
                  all_keys |= event_keys(record)
      
              if not fresh:
                  print("No new log records found.")
                  return
      
              # Write to GCS as NDJSON
              timestamp = now.strftime('%Y%m%d_%H%M%S')
              object_key = f"{GCS_PREFIX}/logs_{timestamp}.ndjson"
              blob = bucket.blob(object_key)
      
              ndjson = '\n'.join([json.dumps(record, ensure_ascii=False) for record in fresh]) + '\n'
              blob.upload_from_string(ndjson, content_type='application/x-ndjson')
      
              print(f"Wrote {len(fresh)} records to gs://{GCS_BUCKET}/{object_key}")
      
              # Remember every fetched record so the next overlap window skips it
              save_seen(bucket, STATE_KEY, all_keys)
      
              if newest_event_time:
                  save_state(bucket, STATE_KEY, newest_event_time)
              else:
                  save_state(bucket, STATE_KEY, now.isoformat())
      
              print(f"Successfully processed {len(fresh)} records")
      
          except Exception as e:
              print(f'Error processing logs: {str(e)}')
              raise
      
      def load_state(bucket, key):
          """Load state from GCS."""
          try:
              blob = bucket.blob(key)
              if blob.exists():
                  state_data = blob.download_as_text()
                  return json.loads(state_data)
          except Exception as e:
              print(f"Warning: Could not load state: {e}")
              raise
      
          return {}
      
      def save_state(bucket, key, last_event_time_iso: str):
          """Save the last event timestamp to GCS state file."""
          try:
              state = {'last_event_time': last_event_time_iso}
              blob = bucket.blob(key)
              blob.upload_from_string(
                  json.dumps(state, indent=2),
                  content_type='application/json'
              )
              print(f"Saved state: last_event_time={last_event_time_iso}")
          except Exception as e:
              print(f"Warning: Could not save state: {e}")
              raise
      
      def load_seen(bucket, key):
          """Load the event keys written by the previous run (sidecar object)."""
          try:
              blob = bucket.blob(key + '.seen')
              if blob.exists():
                  return set(json.loads(blob.download_as_text()))
          except Exception as e:
              print(f"Warning: Could not load seen keys: {e}")
              raise
      
          return set()
      
      def save_seen(bucket, key, keys):
          """Save the event keys of this run to the sidecar object."""
          try:
              blob = bucket.blob(key + '.seen')
              blob.upload_from_string(
                  json.dumps(sorted(keys)),
                  content_type='application/json'
              )
              print(f"Saved {len(keys)} event keys for deduplication")
          except Exception as e:
              print(f"Warning: Could not save seen keys: {e}")
              raise
      
      def fetch_logs(token: str, start_time: datetime, end_time: datetime, page_size: int, max_records: int):
          """
          Fetch user activity logs from Workday Activity Logging API with pagination and rate limiting.
      
          Args:
              token: OAuth 2.0 access token
              start_time: Start time for log query
              end_time: End time for log query
              page_size: Number of records per page
              max_records: Maximum total records to fetch
      
          Returns:
              Tuple of (records list, newest_event_time ISO string)
          """
          base_url = f"https://{WORKDAY_HOST}/ccx/api/privacy/v1/{WORKDAY_TENANT}/activityLogging"
      
          headers = {
              'Authorization': f'Bearer {token}',
              'Accept': 'application/json',
              'User-Agent': 'GoogleSecOps-WorkdayCollector/1.0'
          }
      
          records = []
          newest_time = None
          page_num = 0
          backoff = 1.0
          offset = 0
      
          from_iso = start_time.strftime('%Y-%m-%dT%H:%M:%SZ')
          to_iso = end_time.strftime('%Y-%m-%dT%H:%M:%SZ')
      
          while True:
              page_num += 1
      
              if len(records) >= max_records:
                  print(f"Reached max_records limit ({max_records})")
                  break
      
              current_limit = min(page_size, max_records - len(records))
              url = f"{base_url}?from={from_iso}&to={to_iso}&limit={current_limit}&offset={offset}"
      
              try:
                  response = http.request('GET', url, headers=headers)
      
                  if response.status == 429:
                      retry_after = int(response.headers.get('Retry-After', str(int(backoff))))
                      print(f"Rate limited (429). Retrying after {retry_after}s...")
                      time.sleep(retry_after)
                      backoff = min(backoff * 2, 30.0)
                      continue
      
                  backoff = 1.0
      
                  if response.status != 200:
                      print(f"HTTP Error: {response.status}")
                      response_text = response.data.decode('utf-8')
                      print(f"Response body: {response_text}")
                      return [], None
      
                  data = json.loads(response.data.decode('utf-8'))
      
                  page_results = data.get('data', [])
      
                  if not page_results:
                      print(f"No more results (empty page)")
                      break
      
                  print(f"Page {page_num}: Retrieved {len(page_results)} events")
                  records.extend(page_results)
      
                  # Track newest event time
                  for event in page_results:
                      try:
                          event_time = event.get('requestTime') or event.get('systemAccount', {}).get('signOnDateTime')
                          if event_time:
                              if newest_time is None or parse_datetime(event_time) > parse_datetime(newest_time):
                                  newest_time = event_time
                      except Exception as e:
                          print(f"Warning: Could not parse event time: {e}")
      
                  # Check pagination
                  total = data.get('total', 0)
                  if offset + len(page_results) >= total:
                      print(f"Reached end of results (offset={offset + len(page_results)}, total={total})")
                      break
      
                  offset += len(page_results)
      
              except Exception as e:
                  print(f"Error fetching logs: {e}")
                  return [], None
      
          print(f"Retrieved {len(records)} total records from {page_num} pages")
          return records, newest_time
      

    • File kedua: requirements.txt:

      functions-framework==3.*
      google-cloud-storage==2.*
      urllib3>=2.0.0
      
  3. Klik Deploy untuk menyimpan dan men-deploy fungsi.

  4. Tunggu hingga deployment selesai (2-3 menit).

Buat tugas Cloud Scheduler

Cloud Scheduler memublikasikan pesan ke topik Pub/Sub secara berkala, sehingga memicu fungsi Cloud Run.

  1. Di Konsol GCP, buka Cloud Scheduler.
  2. Klik Create Job.
  3. Berikan detail konfigurasi berikut:

    Setelan Nilai
    Nama workday-activity-collector-hourly
    Region 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 topik workday-activity-trigger
    Isi pesan {} (objek JSON kosong)
  4. Klik Create.

Opsi frekuensi jadwal

Pilih frekuensi berdasarkan volume log dan persyaratan latensi:

Frekuensi Ekspresi Cron Kasus Penggunaan
Setiap 5 menit */5 * * * * Volume tinggi, latensi rendah
Setiap 15 menit */15 * * * * Volume sedang
Setiap jam 0 * * * * Standar (direkomendasikan)
Setiap 6 jam 0 */6 * * * Volume rendah, pemrosesan batch
Harian 0 0 * * * Pengumpulan data historis

Menguji integrasi

  1. Di konsol Cloud Scheduler, temukan tugas Anda (workday-activity-collector-hourly).
  2. Klik Jalankan paksa untuk memicu secara manual.
  3. Tunggu beberapa detik, lalu buka Cloud Run > Services > workday-activity-collector > Logs.
  4. Pastikan fungsi berhasil dieksekusi. Cari:

    Fetching logs from YYYY-MM-DDTHH:MM:SS+00:00 to YYYY-MM-DDTHH:MM:SS+00:00
    Page 1: Retrieved X events
    Wrote X records to gs://workday-activity-logs/workday/logs_YYYYMMDD_HHMMSS.ndjson
    Successfully processed X records
    
  5. Periksa bucket Cloud Storage (workday-activity-logs) untuk mengonfirmasi bahwa log telah ditulis.

Jika Anda melihat error dalam log:

  • HTTP 401: Periksa kredensial OAuth di variabel lingkungan
  • HTTP 403: Verifikasi bahwa ISU memiliki izin yang diperlukan untuk domain Pencatatan Aktivitas Pengguna
  • HTTP 429: Pembatasan kecepatan - fungsi akan otomatis mencoba lagi dengan penundaan
  • Gagal mendapatkan token akses: Pastikan WORKDAY_HOST, WORKDAY_TENANT, CLIENT_ID, CLIENT_SECRET, dan REFRESH_TOKEN sudah benar

Mengonfigurasi feed di Google SecOps untuk menyerap log Aktivitas Pengguna Workday

  1. Buka Setelan SIEM > Feed.
  2. Klik Tambahkan Feed Baru.
  3. Klik Konfigurasi satu feed.
  4. Di kolom Nama feed, masukkan nama untuk feed (misalnya, Workday User Activity Logs).
  5. Pilih Google Cloud Storage V2 sebagai Source type.
  6. Pilih Aktivitas Pengguna Workday sebagai Jenis log.
  7. Klik Get Service Account. Email akun layanan yang unik akan ditampilkan, misalnya:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. Salin alamat email ini. Anda akan menggunakannya pada langkah berikutnya.

  9. Klik Berikutnya.

  10. Tentukan nilai untuk parameter input berikut:

    • URL bucket penyimpanan: Masukkan URI bucket Cloud Storage dengan jalur awalan:

      gs://workday-activity-logs/workday/
      
      • Ganti:
        • workday-activity-logs: Nama bucket Cloud Storage Anda.
        • workday: Awalan atau jalur opsional tempat log disimpan (kosongkan untuk root).
    • Opsi penghapusan sumber: Pilih opsi penghapusan sesuai preferensi Anda:

      • Jangan pernah menghapus file: Jangan pernah menghapus file dari sumber (direkomendasikan untuk pengujian).
      • Hapus file yang ditransfer dan direktori kosong: Menghapus file dan direktori kosong dari sumber setelah pengambilan yang berhasil selesai.
    • Usia File Maksimum: Sertakan file yang diubah dalam beberapa hari terakhir (defaultnya adalah 180 hari).

    • Namespace aset: Namespace aset.

    • Label penyerapan: Label yang akan diterapkan ke peristiwa dari feed ini.

  11. Klik Berikutnya.

  12. Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.

Memberikan izin IAM ke akun layanan Google SecOps

Akun layanan Google SecOps memerlukan dua peran di bucket Cloud Storage Anda: Storage Object Viewer untuk membaca objek log, dan peran tingkat bucket untuk membaca metadata bucket.

  1. Buka Cloud Storage > Buckets.
  2. Klik nama bucket Anda (workday-activity-logs).
  3. Buka tab Izin.
  4. Klik Grant access.
  5. Berikan detail konfigurasi berikut:
    • Add principals: Tempel email akun layanan Google SecOps.
    • Tetapkan peran: Pilih kedua opsi berikut:
      • Storage Object Viewer: membaca objek log.
      • Storage Legacy Bucket Reader: membaca metadata bucket. Jika Anda memilih opsi penghapusan Hapus file yang ditransfer dan direktori kosong, pilih Storage Legacy Bucket Writer, yang juga memberikan izin penghapusan.
  6. Klik Simpan.

Tabel pemetaan UDM

Kolom Log Pemetaan UDM Logika
Account_Locked__Disabled_or_Expired_label additional.fields Digabung
Authentication_Type_label additional.fields Digabung
Created_Moment_label additional.fields Digabung
Current_OMS_Version_label additional.fields Digabung
Device_is_Trusted_label additional.fields Digabung
Elapsed_Time__Minutes_label additional.fields Digabung
Failed_Sign_On_label additional.fields Digabung
Hire_Date_for_Signon_Worker_label additional.fields Digabung
Job_Family_for_Signon_Worker_label additional.fields Digabung
Operating_System_label additional.fields Digabung
Pay_Rate_type_label additional.fields Digabung
Request_Originator_label additional.fields Digabung
SAML_Identity_Provider_label additional.fields Digabung
Sign-on_Time_label additional.fields Digabung
Signoff_Time_label additional.fields Digabung
Signon_Day_of_the_Week_label additional.fields Digabung
Signon_Hour_of_Day_label additional.fields Digabung
Tenant_Access_Read_Only_label additional.fields Digabung
UI_Client_Type_label additional.fields Digabung
Worker_Active_for_Signon_Worker_label additional.fields Digabung
Worker_Type_for_Signon_Worker_label additional.fields Digabung
awaiting_details_label additional.fields Digabung
data_label additional.fields Digabung
evp_nme_label additional.fields Digabung
evpminus_label additional.fields Digabung
signon_ip_address_label additional.fields Digabung
@requestTime metadata.event_timestamp Diuraikan sebagai ISO8601
Sign-on_Time metadata.event_timestamp Diuraikan sebagai yyyy-MM-ddTHH:mm:ssZZ
event_type metadata.event_type Dipetakan secara langsung
status_code metadata.product_event_type Dipetakan secara langsung
tenant metadata.product_event_type Dipetakan secara langsung
taskId metadata.product_log_id Dipetakan secara langsung
User_Agent network.http.parsed_user_agent Diganti nama/dipetakan
Browser_Type network.http.user_agent Dipetakan secara langsung
User_Agent network.http.user_agent Dipetakan secara langsung
userAgent network.http.user_agent Dipetakan secara langsung
Session_ID network.session_id Dipetakan secara langsung
sessionId network.session_id Dipetakan secara langsung
Client_TLS_Version network.tls.version Diganti nama/dipetakan
Worker principal.asset.hostname Dipetakan secara langsung
ip principal.asset.ip Digabung
ipaddress principal.asset.ip Digabung
Worker principal.hostname Dipetakan secara langsung
manager_name principal.hostname Dipetakan secara langsung
ip principal.ip Digabung
ipaddress principal.ip Digabung
Signon_Worker_label principal.user.attribute.labels Digabung
Supervisory_Organization_of_Signon_Worker_label principal.user.attribute.labels Digabung
System_Account_label principal.user.attribute.labels Digabung
Workday_Account_Sign_On_label principal.user.attribute.labels Digabung
user_credentials_label principal.user.attribute.labels Digabung
User_Name principal.user.email_addresses Digabung
Role principal.user.user_display_name Dipetakan secara langsung
manager_id principal.user.userid Dipetakan secara langsung
Active_Session_label security_result.detection_fields Digabung
Authentication_Failure_Message_label security_result.detection_fields Digabung
Forgotten_Password_Reset_Request_label security_result.detection_fields Digabung
Invalid_Credentials_label security_result.detection_fields Digabung
Is_Device_Managed_label security_result.detection_fields Digabung
MFA_Exempt_label security_result.detection_fields Digabung
Password_Changed_label security_result.detection_fields Digabung
Position_label security_result.detection_fields Digabung
taskDisplayName security_result.summary Dipetakan secara langsung
hostname target.hostname Dipetakan secara langsung
CITY_NME target.location.city Dipetakan secara langsung
Location target.location.city Dipetakan secara langsung
COUNTRY_NME target.location.country_or_region Dipetakan secara langsung
Location_Country_for_Signon_Worker target.location.country_or_region Dipetakan secara langsung
Location_State target.location.state Dipetakan secara langsung
STATE_NME target.location.state Dipetakan secara langsung
Exempt_labels target.resource.attribute.labels Digabung
browser_type_label target.resource.attribute.labels Digabung
class_of_instance_label target.resource.attribute.labels Digabung
device target.resource.resource_subtype Dipetakan secara langsung
device1 target.resource.resource_subtype Dipetakan secara langsung
device target.resource.type Dipetakan: (?i)(Desktop/Laptop/Mobile/Phone) → DEVICE
device1 target.resource.type Dipetakan: (?i)(Desktop/Laptop/Mobile/Phone) → DEVICE
BUSINESSGROUP_NME target.user.department Digabung
email_name target.user.email_addresses Digabung
EMPLOYEE_ID target.user.employee_id Dipetakan secara langsung
Employee_ID target.user.employee_id Dipetakan secara langsung
FIRST_NME target.user.first_name Dipetakan secara langsung
EMPLOYEE_TYPE target.user.group_identifiers Digabung
LAST_NME target.user.last_name Dipetakan secara langsung
JOBTITLE_NME target.user.title Dipetakan secara langsung
title target.user.title Dipetakan secara langsung
target.id target.user.userid Dipetakan secara langsung
T/A metadata.product_name Konstanta: WORKDAY_USER_ACTIVITY
T/A metadata.vendor_name Konstanta: WORKDAY_USER_ACTIVITY
T/A target.resource.type Konstanta: DEVICE

Log Perubahan

Melihat Log Perubahan untuk parser ini

Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.