Mengumpulkan log Oracle NetSuite - NetSuite Applications Suite

Didukung di:

Dokumen ini menjelaskan cara menyerap log Oracle NetSuite - NetSuite Applications Suite ke Google Security Operations menggunakan Google Cloud Storage V2.

Oracle NetSuite adalah platform perencanaan sumber daya perusahaan (ERP) berbasis cloud yang menyediakan kemampuan pengelolaan bisnis yang komprehensif, termasuk pengelolaan keuangan, pengelolaan hubungan pelanggan (CRM), e-commerce, dan pengelolaan inventaris. NetSuite menghasilkan log audit ekstensif, termasuk jejak login pengguna, catatan sistem yang melacak perubahan catatan, dan log akses API yang dapat diambil melalui SuiteQL REST API untuk tujuan pemantauan keamanan dan kepatuhan.

Sebelum memulai

Pastikan Anda memiliki prasyarat berikut:

  • Instance Google SecOps
  • Project GCP dengan Cloud Storage, Cloud Run, Pub/Sub, dan Cloud Scheduler API yang diaktifkan
  • Izin untuk membuat dan mengelola bucket GCS
  • Izin untuk mengelola kebijakan IAM di bucket GCS
  • Izin untuk membuat layanan Cloud Run, topik Pub/Sub, dan tugas Cloud Scheduler
  • Akun NetSuite dengan akses Administrator
  • Layanan Web REST NetSuite diaktifkan
  • Izin untuk membuat data integrasi dan token akses di NetSuite

Mengonfigurasi akses NetSuite API

Untuk mengaktifkan pengumpulan log melalui SuiteQL REST API, Anda harus mengaktifkan REST Web Services, membuat data integrasi, dan membuat kredensial Token-Based Authentication (TBA) di NetSuite.

Mengaktifkan fitur NetSuite yang diperlukan

  1. Login ke akun NetSuite Anda sebagai Administrator.
  2. Buka Penyiapan > Perusahaan > Aktifkan Fitur.
  3. Klik tab SuiteCloud.
  4. Di bagian SuiteScript, centang kotak berikut:
    • Client SuiteScript
    • Server SuiteScript
  5. Di bagian SuiteTalk (Web Services), centang kotak berikut:
    • REST Web Services
  6. Di bagian Manage Authentication, centang kotak Token-Based Authentication.
  7. Klik I Agree di halaman SuiteCloud Terms of Service.
  8. Klik Simpan.

Membuat data integrasi

  1. Di NetSuite, buka Setup > Integration > Manage Integrations > New.
  2. Di kolom Name, masukkan Google SecOps Integration.
  3. Di kolom Description, masukkan Integration for Google Security Operations log collection.
  4. Di kolom State, biarkan opsi default Enabled dipilih.
  5. Klik tab Authentication.
  6. Di bagian Autentikasi Berbasis Token, centang kotak Autentikasi Berbasis Token.
  7. Jangan centang kotak TBA: Authorization Flow.
  8. Centang kotak TBA: ISSUETOKEN ENDPOINT.
  9. Klik Simpan.
  10. Di halaman konfirmasi, salin dan simpan kunci pengguna/Client ID dan Consumer Secret/rahasia klien.

Membuat peran khusus untuk akses API

  1. Buka Setup > Users/Roles > Manage Roles > New.
  2. Di kolom Name, masukkan Google SecOps API Role.
  3. Di kolom ID, masukkan google_secops_api_role.
  4. Klik subtab Izin.
  5. Klik subtab Penyiapan.
  6. Tambahkan izin berikut dengan tingkat akses Penuh:
    • Login menggunakan Token Akses
    • Token Akses Pengguna
    • Layanan Web REST
  7. Klik subtab Laporan.
  8. Tambahkan izin berikut dengan tingkat akses Lihat:
    • Workbook SuiteAnalytics
  9. Klik subtab Daftar.
  10. Tambahkan izin berikut dengan tingkat akses Lihat:
    • Jejak Audit Login
  11. Klik Simpan.

Menetapkan peran kepada pengguna

  1. Buka Lists > Employees > Employees.
  2. Pilih akun pengguna yang akan digunakan untuk integrasi (atau buat pengguna integrasi khusus baru).
  3. Klik Edit.
  4. Klik tab Akses.
  5. Di bagian Roles, klik Add.
  6. Pilih Google SecOps API Role dari menu dropdown.
  7. Klik Simpan.

Membuat token akses

  1. Login ke NetSuite sebagai pengguna yang diberi Peran Google SecOps API.
  2. Buka Penyiapan > Pengguna/Peran > Token Akses > Baru.
  3. Di halaman Access Token:
    • Di menu drop-down Application Name, pilih Google SecOps Integration.
    • Di menu dropdown User, pilih akun pengguna yang diberi Peran Google SecOps API.
    • Di menu dropdown Role, pilih Google SecOps API Role.
    • Kolom Nama Token akan otomatis terisi. Anda dapat mengubahnya jika diinginkan.
  4. Klik Simpan.
  5. Di halaman konfirmasi, salin dan simpan ID token dan Token Secret.

Menemukan ID Akun NetSuite Anda

  1. Di NetSuite, buka Setup > Perusahaan > Informasi Perusahaan.
  2. Temukan kolom ID Akun (ditampilkan di bawah setelan Zona Waktu).
  3. Salin dan simpan ID Akun Anda (misalnya, 1234567 atau 1234567_SB1 untuk akun sandbox).

Memverifikasi izin

Untuk memverifikasi bahwa akun memiliki izin yang diperlukan:

  1. Login ke NetSuite dengan pengguna yang diberi Peran Google SecOps API.
  2. Buka Setup > Users/Roles > View Login Audit Trail.
  3. Jika Anda dapat melihat halaman Jejak Audit Login, berarti akun tersebut memiliki izin yang diperlukan.
  4. Jika Anda tidak dapat melihat opsi ini, hubungi administrator NetSuite Anda untuk memberikan izin Login Audit Trail di subtab Lists.

Menguji akses API

  • Uji kredensial Anda sebelum melanjutkan integrasi:

    # Replace with your actual values
    ACCOUNT_ID="your-account-id"
    
    # Replace underscores with hyphens in the account ID for the URL
    # For example: 1234567_SB1 becomes 1234567-sb1
    URL_ACCOUNT_ID=$(echo "$ACCOUNT_ID" | tr '_' '-' | tr '[:upper:]' '[:lower:]')
    
    CONSUMER_KEY="your-consumer-key"
    CONSUMER_SECRET="your-consumer-secret"
    TOKEN_ID="your-token-id"
    TOKEN_SECRET="your-token-secret"
    
    # Test SuiteQL API access using OAuth 1.0 TBA
    # Note: This example requires the oauth1 CLI tool or Postman.
    # Use the following endpoint and headers to verify connectivity:
    # POST https://${URL_ACCOUNT_ID}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=5
    # Headers:
    #   Authorization: OAuth (with consumer key, token, HMAC-SHA256 signature)
    #   Content-Type: application/json
    #   Prefer: transient
    # Body: {"q": "SELECT id, date, status, user, emailaddress FROM LoginAudit ORDER BY date DESC"}
    

Respons yang berhasil akan menampilkan objek JSON yang berisi catatan LoginAudit.

Buat bucket Google Cloud Storage

  1. Buka Konsol Google Cloud.
  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, netsuite-audit-logs)
    Location type Pilih sesuai kebutuhan Anda (Region, Dual-region, Multi-region)
    Location Pilih lokasi (misalnya, us-central1)
    Kelas penyimpanan Standard (direkomendasikan untuk log yang sering diakses)
    Kontrol akses Seragam (direkomendasikan)
    Alat perlindungan Opsional: Aktifkan pembuatan versi objek atau kebijakan retensi
  6. Klik Create.

Buat akun layanan untuk fungsi Cloud Run

Fungsi Cloud Run memerlukan akun layanan dengan izin untuk menulis ke bucket GCS 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 netsuite-audit-collector-sa
    • Deskripsi akun layanan: Masukkan Service account for Cloud Run function to collect NetSuite audit 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 GCS dan mengelola file status
  • Cloud Run Invoker: Izinkan Pub/Sub memanggil fungsi
  • Cloud Functions Invoker: Mengizinkan pemanggilan fungsi

Memberikan izin IAM pada bucket GCS

  1. Buka Cloud Storage > Buckets.
  2. Klik nama bucket Anda (netsuite-audit-logs).
  3. Buka tab Izin.
  4. Klik Grant access.
  5. Berikan detail konfigurasi berikut:
    • Add principals: Masukkan email akun layanan (netsuite-audit-collector-sa@PROJECT_ID.iam.gserviceaccount.com)
    • Menetapkan peran: Pilih Storage Object Admin
  6. Klik Simpan.

Membuat topik Pub/Sub

  1. Di Konsol GCP, buka Pub/Sub > Topics.
  2. Klik Create topic.
  3. Berikan detail konfigurasi berikut:
    • ID Topik: Masukkan netsuite-audit-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 NetSuite SuiteQL REST API dan menuliskannya ke GCS.

  1. Di Konsol GCP, buka Cloud Run.
  2. Klik Create service.
  3. Pilih Function (gunakan editor inline untuk membuat fungsi).
  4. Di bagian Konfigurasi, berikan detail konfigurasi berikut:

    Setelan Nilai
    Nama layanan netsuite-audit-collector
    Region Pilih region yang cocok dengan bucket GCS Anda (misalnya, us-central1)
    Runtime Pilih Python 3.12 atau yang lebih baru
  5. Di bagian Pemicu (opsional):

    1. Klik + Tambahkan pemicu.
    2. Pilih Cloud Pub/Sub.
    3. Di Select a Cloud Pub/Sub topic, pilih netsuite-audit-trigger.
    4. Klik Simpan.
  6. Di bagian Authentication:

    1. Pilih Wajibkan autentikasi.
    2. Periksa Identity and Access Management (IAM).
  7. Scroll ke bawah dan luaskan Containers, Networking, Security.

  8. Buka tab Security:

    • Akun layanan: Pilih netsuite-audit-collector-sa
  9. Buka tab Containers:

    1. Klik Variables & Secrets.
    2. Klik + Tambahkan variabel untuk setiap variabel lingkungan:
    Nama Variabel Nilai Contoh Deskripsi
    GCS_BUCKET netsuite-audit-logs Nama bucket GCS
    GCS_PREFIX netsuite-audit Awalan untuk file log
    STATE_KEY netsuite-audit/state.json Jalur file status
    NS_ACCOUNT_ID 1234567 ID Akun NetSuite
    NS_CONSUMER_KEY your-consumer-key Kunci Konsumen dari catatan integrasi
    NS_CONSUMER_SECRET your-consumer-secret Rahasia Konsumen dari data integrasi
    NS_TOKEN_ID your-token-id ID token dari token akses
    NS_TOKEN_SECRET your-token-secret Secret Token dari token akses
    MAX_RECORDS 10000 Jumlah data maks per proses
    PAGE_SIZE 1000 Kumpulan data per halaman SuiteQL
    LOOKBACK_HOURS 24 Periode lihat balik awal
  10. Di bagian Variables & Secrets, scroll ke bawah ke Requests:

    • Request timeout: Masukkan 600 detik (10 menit)
  11. Buka tab Setelan:

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

    • Minimum number of instances: Masukkan 0
    • Maximum number of instances: Masukkan 100
  13. Klik Create.

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

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

Menambahkan kode fungsi

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

    • main.py:
    import functions_framework
    from google.cloud import storage
    import json
    import os
    import urllib3
    import hashlib
    import hmac
    import base64
    import time
    import secrets
    import urllib.parse
    from datetime import datetime, timezone, timedelta
    
    # Initialize HTTP client with timeouts
    http = urllib3.PoolManager(
      timeout=urllib3.Timeout(connect=10.0, read=60.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', 'netsuite-audit')
    STATE_KEY = os.environ.get('STATE_KEY', 'netsuite-audit/state.json')
    NS_ACCOUNT_ID = os.environ.get('NS_ACCOUNT_ID')
    NS_CONSUMER_KEY = os.environ.get('NS_CONSUMER_KEY')
    NS_CONSUMER_SECRET = os.environ.get('NS_CONSUMER_SECRET')
    NS_TOKEN_ID = os.environ.get('NS_TOKEN_ID')
    NS_TOKEN_SECRET = os.environ.get('NS_TOKEN_SECRET')
    MAX_RECORDS = int(os.environ.get('MAX_RECORDS', '10000'))
    PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '1000'))
    LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24'))
    
    def get_netsuite_base_url(account_id):
      """Build the NetSuite REST API base URL from the account ID."""
      url_account = account_id.replace('_', '-').lower()
      return f"https://{url_account}.suitetalk.api.netsuite.com"
    
    def generate_oauth_header(method, url, account_id, consumer_key,
      consumer_secret, token_id, token_secret):
      """Generate OAuth 1.0 Authorization header using HMAC-SHA256."""
      timestamp = str(int(time.time()))
      nonce = secrets.token_hex(16)
    
      # Parse URL to separate base URL and query parameters
      parsed = urllib.parse.urlparse(url)
      base_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
      query_params = urllib.parse.parse_qs(parsed.query, keep_blank_values=True)
    
      # Build parameter string (OAuth params + query params)
      params = {
        'oauth_consumer_key': consumer_key,
        'oauth_token': token_id,
        'oauth_nonce': nonce,
        'oauth_timestamp': timestamp,
        'oauth_signature_method': 'HMAC-SHA256',
        'oauth_version': '1.0',
      }
      for key, values in query_params.items():
        params[key] = values[0]
    
      # Sort parameters and create parameter string
      sorted_params = sorted(params.items())
      param_string = '&'.join(
        f"{urllib.parse.quote(k, safe='')}={urllib.parse.quote(v, safe='')}"
        for k, v in sorted_params
      )
    
      # Create signature base string
      signature_base = (
        f"{method.upper()}&"
        f"{urllib.parse.quote(base_url, safe='')}&"
        f"{urllib.parse.quote(param_string, safe='')}"
      )
    
      # Create signing key
      signing_key = (
        f"{urllib.parse.quote(consumer_secret, safe='')}&"
        f"{urllib.parse.quote(token_secret, safe='')}"
      )
    
      # Generate HMAC-SHA256 signature
      signature = base64.b64encode(
        hmac.new(
          signing_key.encode('utf-8'),
          signature_base.encode('utf-8'),
          hashlib.sha256
        ).digest()
      ).decode('utf-8')
    
      # Build realm from account ID
      realm = account_id.replace('-', '_').upper()
    
      # Build Authorization header
      auth_header = (
        f'OAuth realm="{realm}",'
        f'oauth_consumer_key="{consumer_key}",'
        f'oauth_token="{token_id}",'
        f'oauth_nonce="{nonce}",'
        f'oauth_timestamp="{timestamp}",'
        f'oauth_signature_method="HMAC-SHA256",'
        f'oauth_version="1.0",'
        f'oauth_signature="{urllib.parse.quote(signature, safe="")}"'
      )
      return auth_header
    
    @functions_framework.cloud_event
    def main(cloud_event):
      """
      Cloud Run function triggered by Pub/Sub to fetch NetSuite
      LoginAudit logs via SuiteQL and write to GCS.
      """
      if not all([GCS_BUCKET, NS_ACCOUNT_ID, NS_CONSUMER_KEY,
        NS_CONSUMER_SECRET, NS_TOKEN_ID, NS_TOKEN_SECRET]):
        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_time'):
          try:
            last_val = state['last_event_time']
            if last_val.endswith('Z'):
              last_val = last_val[:-1] + '+00:00'
            last_time = datetime.fromisoformat(last_val)
            last_time = last_time - timedelta(minutes=2)
          except Exception as e:
            print(f"Warning: Could not parse last_event_time: {e}")
            last_time = now - timedelta(hours=LOOKBACK_HOURS)
        else:
          last_time = now - timedelta(hours=LOOKBACK_HOURS)
    
        print(f"Fetching logs from {last_time.isoformat()} to {now.isoformat()}")
    
        records, newest_time = fetch_login_audit(last_time, now)
    
        if not records:
          print("No new log records found.")
          save_state(bucket, now.isoformat())
          return
    
        timestamp = now.strftime('%Y%m%d_%H%M%S')
        object_key = f"{GCS_PREFIX}/netsuite_audit_{timestamp}.ndjson"
        blob = bucket.blob(object_key)
    
        ndjson = '\n'.join(
          [json.dumps(r, ensure_ascii=False, default=str) for r 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}")
    
        if newest_time:
          save_state(bucket, newest_time)
        else:
          save_state(bucket, now.isoformat())
    
        print(f"Successfully processed {len(records)} records")
    
      except Exception as e:
        print(f'Error processing logs: {str(e)}')
        raise
    
    def fetch_login_audit(start_time, end_time):
      """
      Fetch LoginAudit records from NetSuite SuiteQL API with pagination.
    
      Returns:
        Tuple of (records list, newest_event_time ISO string)
      """
      base_url = get_netsuite_base_url(NS_ACCOUNT_ID)
      suiteql_endpoint = f"{base_url}/services/rest/query/v1/suiteql"
    
      start_str = start_time.strftime('%Y-%m-%d %H:%M:%S')
      end_str = end_time.strftime('%Y-%m-%d %H:%M:%S')
    
      query = (
        "SELECT id, date, status, user, role, emailaddress, "
        "ipaddress, useragent, detail, requesturi, "
        "oauthaccesstokenname, oauthappname "
        "FROM LoginAudit "
        f"WHERE date BETWEEN TO_DATE('{start_str}', 'YYYY-MM-DD HH24:MI:SS') "
        f"AND TO_DATE('{end_str}', 'YYYY-MM-DD HH24:MI:SS') "
        "ORDER BY date ASC"
      )
    
      records = []
      newest_time = None
      offset = 0
      backoff = 1.0
    
      while True:
        if len(records) >= MAX_RECORDS:
          print(f"Reached max_records limit ({MAX_RECORDS})")
          break
    
        remaining = min(PAGE_SIZE, MAX_RECORDS - len(records))
        url = f"{suiteql_endpoint}?limit={remaining}&offset={offset}"
    
        auth_header = generate_oauth_header(
          'POST', url, NS_ACCOUNT_ID, NS_CONSUMER_KEY,
          NS_CONSUMER_SECRET, NS_TOKEN_ID, NS_TOKEN_SECRET
        )
    
        headers = {
          'Authorization': auth_header,
          'Content-Type': 'application/json',
          'Prefer': 'transient',
          'User-Agent': 'GoogleSecOps-NetSuiteCollector/1.0',
        }
    
        body = json.dumps({'q': query}).encode('utf-8')
    
        try:
          response = http.request('POST', url, headers=headers, body=body)
    
          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, 60.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 records, newest_time
    
          data = json.loads(response.data.decode('utf-8'))
    
          page_results = data.get('items', [])
    
          if not page_results:
            print("No more results (empty page)")
            break
    
          page_num = (offset // PAGE_SIZE) + 1
          print(f"Page {page_num}: Retrieved {len(page_results)} records")
          records.extend(page_results)
    
          for event in page_results:
            try:
              event_time = event.get('date')
              if event_time:
                if newest_time is None or event_time > newest_time:
                  newest_time = event_time
            except Exception as e:
              print(f"Warning: Could not parse event time: {e}")
    
          has_more = data.get('hasMore', False)
          if not has_more:
            print("Reached last page (hasMore=false)")
            break
    
          offset += len(page_results)
    
        except Exception as e:
          print(f"Error fetching logs: {e}")
          return records, newest_time
    
      print(f"Retrieved {len(records)} total records")
      return records, newest_time
    
    def load_state(bucket):
      """Load state from GCS."""
      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_time_iso):
      """Save the last event timestamp to GCS state file."""
      try:
        state = {
          'last_event_time': last_event_time_iso,
          '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_time={last_event_time_iso}")
      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
    
  3. Klik Deploy untuk menyimpan dan men-deploy fungsi.

  4. 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.

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

    Setelan Nilai
    Nama netsuite-audit-collector-hourly
    Region Pilih region yang sama dengan fungsi Cloud Run
    Frekuensi 0 * * * * (setiap jam, tepat pada jamnya)
    Zona Waktu Pilih zona waktu (UTC direkomendasikan)
    Jenis target Pub/Sub
    Topik Pilih netsuite-audit-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, batch processing
Harian 0 0 * * * Pengumpulan data historis

Menguji integrasi

  1. Di konsol Cloud Scheduler, temukan tugas Anda (netsuite-audit-collector-hourly).
  2. Klik Force run untuk memicu tugas secara manual.
  3. Tunggu beberapa detik.
  4. Buka Cloud Run > Services.
  5. Klik netsuite-audit-collector.
  6. Klik tab Logs.
  7. 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 records
    Wrote X records to gs://netsuite-audit-logs/netsuite-audit/netsuite_audit_YYYYMMDD_HHMMSS.ndjson
    Successfully processed X records
    
  8. Buka Cloud Storage > Buckets.

  9. Klik netsuite-audit-logs.

  10. Buka folder netsuite-audit/.

  11. Pastikan file .ndjson baru dibuat dengan stempel waktu saat ini.

Jika Anda melihat error di log:

  • HTTP 401: Pastikan kredensial OAuth (NS_CONSUMER_KEY, NS_CONSUMER_SECRET, NS_TOKEN_ID, NS_TOKEN_SECRET) dan variabel lingkungan NS_ACCOUNT_ID sudah benar
  • HTTP 403: Verifikasi bahwa peran NetSuite memiliki izin yang diperlukan (REST Web Services, Login Audit Trail, SuiteAnalytics Workbook)
  • HTTP 429: Pembatasan kapasitas — fungsi akan otomatis mencoba ulang dengan backoff eksponensial
  • Variabel lingkungan tidak ada: Pastikan semua variabel yang diperlukan ditetapkan dalam konfigurasi fungsi Cloud Run

Mengambil akun layanan Google SecOps

  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, NetSuite Audit Logs).
  5. Pilih Google Cloud Storage V2 sebagai Source type.
  6. Pilih Oracle NetSuite - NetSuite Applications Suite sebagai Log type.
  7. Klik Get Service Account. Email akun layanan yang unik akan ditampilkan. Contoh:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. Salin alamat email ini untuk digunakan di langkah berikutnya.

  9. Klik Berikutnya.

  10. Tentukan nilai untuk parameter input berikut:

    • URL bucket penyimpanan: Masukkan URI bucket GCS dengan jalur awalan:

      gs://netsuite-audit-logs/netsuite-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.
      • Delete transferred files and empty directories: Menghapus file dan direktori kosong setelah transfer berhasil.
    • Maximum File Age: Sertakan file yang diubah dalam jumlah 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 berita baru Anda di layar Finalize, lalu klik Submit.

Memberikan izin IAM ke akun layanan Google SecOps

Akun layanan Google SecOps memerlukan peran Storage Object Viewer di bucket GCS Anda.

  1. Buka Cloud Storage > Buckets.
  2. Klik netsuite-audit-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 Storage Object Viewer
  6. Klik Simpan.

Batas kapasitas API NetSuite

NetSuite API memiliki batas konkurensi dan pembatasan kapasitas berikut:

Jenis Batas Nilai Default Catatan
Permintaan serentak 15 permintaan Berlaku untuk gabungan permintaan SOAP dan REST API per akun
Hasil kueri SuiteQL Maksimum 100.000 baris Jumlah maksimum baris yang ditampilkan per kueri SuiteQL
Objek per permintaan Maksimum 1.000 objek Jumlah maksimum objek yang ditampilkan per permintaan API

Fungsi Cloud Run otomatis menangani pembatasan kapasitas dengan backoff eksponensial. Jika Anda mengalami error pembatasan kapasitas yang terus-menerus (HTTP 429), hubungi dukungan NetSuite untuk meningkatkan batas API atau menyesuaikan frekuensi Cloud Scheduler.

Tabel pemetaan UDM

Kolom Log Pemetaan UDM Logika
pengguna extensions.auth.type Jenis mekanisme autentikasi yang digunakan
tanggal metadata.event_timestamp Stempel waktu saat peristiwa terjadi
event_type metadata.event_type Jenis peristiwa (misalnya, USER_LOGIN, NETWORK_CONNECTION)
security_result security_result Detail tentang hasil keamanan acara
oauthaccesstokenname security_result.about.resource.attribute.labels Label key-value yang mendeskripsikan atribut resource yang terlibat dalam hasil keamanan
secchallenge security_result.about.resource.attribute.labels
status security_result.summary Deskripsi ringkasan hasil keamanan
oauthappname target.application Nama aplikasi target
useragent target.application
ipaddress target.ip Alamat IP target
requesturi target.url URL resource target
emailaddress target.user.email_addresses Alamat email yang terkait dengan pengguna target
peran target.user.role_name Nama peran yang ditetapkan kepada pengguna target
pengguna target.user.userid ID unik untuk pengguna target

Log Perubahan

Melihat Log Perubahan untuk parser ini

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