Mengumpulkan log audit ServiceNow
Dokumen ini menjelaskan cara menyerap log audit ServiceNow ke Google Security Operations menggunakan beberapa metode.
Opsi A: AWS S3 dengan Lambda
Metode ini menggunakan AWS Lambda untuk secara berkala mengkueri ServiceNow REST API untuk mendapatkan log audit dan menyimpannya di bucket S3. Kemudian, Google Security Operations mengumpulkan log dari bucket S3.
Sebelum memulai
- Instance Google SecOps
- Akses istimewa ke tenant atau API ServiceNow
- Akses istimewa ke AWS (S3, IAM, Lambda, EventBridge)
Mengumpulkan prasyarat ServiceNow (ID, kunci API, ID org, token)
- Login ke Konsol Admin ServiceNow.
- Buka Keamanan Sistem > Pengguna dan Grup > Pengguna.
- Buat pengguna baru atau pilih pengguna yang sudah ada dengan izin yang sesuai untuk mengakses log audit.
- Salin dan simpan detail berikut di lokasi yang aman:
- Username
- Password
- URL Instance (misalnya, https://instance.service-now.com)
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,
servicenow-audit-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 CSV file untuk menyimpan Access Key dan Secret Access Key untuk digunakan nanti.
- Klik Done.
- Pilih tab Izin.
- Klik Tambahkan izin di bagian Kebijakan izin .
- Pilih Tambahkan izin.
- Pilih Lampirkan kebijakan secara langsung.
- Telusuri dan pilih kebijakan AmazonS3FullAccess.
- Klik Berikutnya.
- Klik Add permissions.
Mengonfigurasi kebijakan dan peran IAM untuk upload S3
- Di konsol AWS, buka IAM > Policies > Create policy > JSON tab.
Salin dan tempel kebijakan di bawah.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::servicenow-audit-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::servicenow-audit-logs/audit-logs/state.json" } ] }- Ganti
servicenow-audit-logsjika Anda memasukkan nama bucket yang berbeda.
- Ganti
Klik Berikutnya > Buat kebijakan.
Buka IAM > Roles > Create role > AWS service > Lambda.
Lampirkan kebijakan yang baru dibuat.
Beri nama peran
servicenow-audit-lambda-role, lalu klik Buat peran.
Buat fungsi Lambda
- Di AWS Console, buka Lambda > Functions > Create function.
- Klik Buat dari awal.
Berikan detail konfigurasi berikut:
Setelan Nilai Nama servicenow-audit-collectorRuntime Python 3.13 Arsitektur x86_64 Peran eksekusi servicenow-audit-lambda-roleSetelah fungsi dibuat, buka tab Code, hapus stub, lalu masukkan kode berikut (
servicenow-audit-collector.py):import urllib3 import json import os import datetime import boto3 import base64 def lambda_handler(event, context): # ServiceNow API details base_url = os.environ['API_BASE_URL'] # e.g., https://instance.service-now.com username = os.environ['API_USERNAME'] password = os.environ['API_PASSWORD'] # S3 details s3_bucket = os.environ['S3_BUCKET'] s3_prefix = os.environ['S3_PREFIX'] # State management state_key = os.environ.get('STATE_KEY', f"{s3_prefix}/state.json") # Pagination settings page_size = int(os.environ.get('PAGE_SIZE', '1000')) max_pages = int(os.environ.get('MAX_PAGES', '1000')) # Initialize S3 client s3 = boto3.client('s3') # Get last run timestamp from state file last_run_timestamp = get_last_run_timestamp(s3, s3_bucket, state_key) # Current timestamp for this run current_timestamp = datetime.datetime.now().isoformat() # Query ServiceNow API for audit logs with pagination audit_logs = get_audit_logs(base_url, username, password, last_run_timestamp, page_size, max_pages) if audit_logs: # Write logs to S3 in NDJSON format (newline-delimited JSON) timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S') s3_key = f"{s3_prefix}/servicenow-audit-{timestamp}.ndjson" # Format as NDJSON: one JSON object per line body = "\n".join(json.dumps(log) for log in audit_logs) + "\n" s3.put_object( Bucket=s3_bucket, Key=s3_key, Body=body, ContentType='application/x-ndjson' ) # Update state file update_state_file(s3, s3_bucket, state_key, current_timestamp) return { 'statusCode': 200, 'body': json.dumps(f'Successfully exported {len(audit_logs)} audit logs to S3') } else: return { 'statusCode': 200, 'body': json.dumps('No new audit logs to export') } def get_last_run_timestamp(s3, bucket, key): try: response = s3.get_object(Bucket=bucket, Key=key) state = json.loads(response['Body'].read().decode('utf-8')) return state.get('last_run_timestamp', '1970-01-01T00:00:00') except: return '1970-01-01T00:00:00' def update_state_file(s3, bucket, key, timestamp): state = {'last_run_timestamp': timestamp} s3.put_object( Bucket=bucket, Key=key, Body=json.dumps(state), ContentType='application/json' ) def get_audit_logs(base_url, username, password, last_run_timestamp, page_size=1000, max_pages=1000): """ Query ServiceNow sys_audit table with proper pagination. Uses sys_created_on field for timestamp filtering. """ # Encode credentials auth_string = f"{username}:{password}" auth_bytes = auth_string.encode('ascii') auth_encoded = base64.b64encode(auth_bytes).decode('ascii') # Setup HTTP client http = urllib3.PoolManager() headers = { 'Authorization': f'Basic {auth_encoded}', 'Accept': 'application/json' } results = [] offset = 0 for page in range(max_pages): # Build query with pagination # Use sys_created_on (not created_on) for timestamp filtering query_params = ( f"sysparm_query=sys_created_onAFTER{last_run_timestamp}" f"&sysparm_display_value=true" f"&sysparm_limit={page_size}" f"&sysparm_offset={offset}" ) url = f"{base_url}/api/now/table/sys_audit?{query_params}" try: response = http.request('GET', url, headers=headers) if response.status == 200: data = json.loads(response.data.decode('utf-8')) chunk = data.get('result', []) results.extend(chunk) # Stop if we got fewer records than page_size (last page) if len(chunk) < page_size: break # Move to next page offset += page_size else: print(f"Error querying ServiceNow API: {response.status} - {response.data.decode('utf-8')}") break except Exception as e: print(f"Exception querying ServiceNow API: {str(e)}") break return resultsBuka Configuration > Environment variables > Edit > Add new environment variable.
Masukkan variabel lingkungan berikut, lalu ganti dengan nilai Anda.
Kunci Nilai contoh S3_BUCKETservicenow-audit-logsS3_PREFIXaudit-logs/STATE_KEYaudit-logs/state.jsonAPI_BASE_URLhttps://instance.service-now.comAPI_USERNAME<your-username>API_PASSWORD<your-password>PAGE_SIZE1000MAX_PAGES1000Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda > Functions > servicenow-audit-collector).
Pilih tab Configuration
Di panel General configuration, 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
servicenow-audit-collector. - Name:
servicenow-audit-collector-1h.
- Jadwal berulang: Tarif (
- Klik Buat jadwal.
Mengonfigurasi feed di Google SecOps untuk menyerap log Audit ServiceNow
- Buka Setelan SIEM > Feed.
- Klik + Tambahkan Feed Baru.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
ServiceNow Audit logs). - Pilih Amazon S3 V2 sebagai Jenis sumber.
- Pilih ServiceNow Audit sebagai Jenis log.
- Klik Berikutnya.
- Tentukan nilai untuk parameter input berikut:
- URI S3:
s3://servicenow-audit-logs/audit-logs/ - 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.
Opsi B: Agen Bindplane dengan syslog
Metode ini menggunakan agen Bindplane untuk mengumpulkan log Audit ServiceNow dan meneruskannya ke Google Security Operations. Karena ServiceNow tidak mendukung syslog secara native untuk log audit, kita akan menggunakan skrip untuk membuat kueri ServiceNow REST API dan meneruskan log ke agen Bindplane melalui syslog.
Sebelum memulai
Pastikan Anda memiliki prasyarat berikut:
- Instance Google SecOps
- Host Windows 2016 atau yang lebih baru atau Linux dengan
systemd - Jika beroperasi dari balik proxy, pastikan port firewall terbuka sesuai dengan persyaratan agen Bindplane
- Akses istimewa ke konsol atau appliance pengelolaan ServiceNow
Mendapatkan file autentikasi penyerapan Google SecOps
- Login ke konsol Google SecOps.
- Buka Setelan SIEM > Agen Pengumpulan.
- Download File Autentikasi Penyerapan. Simpan file dengan aman di sistem tempat BindPlane akan diinstal.
Mendapatkan ID pelanggan Google SecOps
- Login ke konsol Google SecOps.
- Buka Setelan SIEM > Profil.
- Salin dan simpan ID Pelanggan dari bagian Detail Organisasi.
Menginstal agen Bindplane
Instal agen Bindplane di sistem operasi Windows atau Linux Anda sesuai dengan petunjuk berikut.
Penginstalan Linux
- Buka terminal dengan hak istimewa root atau sudo.
Jalankan perintah berikut:
sudo sh -c "$(curl -fsSlL https://github.com/observiq/bindplane-agent/releases/latest/download/install_unix.sh)" install_unix.sh
Referensi penginstalan tambahan
- Untuk opsi penginstalan tambahan, lihat panduan penginstalan ini.
Mengonfigurasi agen BindPlane untuk menyerap Syslog dan mengirimkannya ke Google SecOps
Akses file konfigurasi:
- Cari file
config.yaml. Biasanya, file ini berada di direktori/etc/bindplane-agent/di Linux atau di direktori penginstalan di Windows. - Buka file menggunakan editor teks (misalnya,
nano,vi, atau Notepad).
- Cari file
Edit file
config.yamlsebagai berikut:receivers: udplog: # Replace the port and IP address as required listen_address: "0.0.0.0:514" exporters: chronicle/chronicle_w_labels: compression: gzip # Adjust the path to the credentials file you downloaded in Step 1 creds_file_path: '/path/to/ingestion-authentication-file.json' # Replace with your actual customer ID from Step 2 customer_id: <YOUR_CUSTOMER_ID> # Replace with the appropriate regional endpoint endpoint: <CUSTOMER_REGION_ENDPOINT> # Add optional ingestion labels for better organization log_type: 'SERVICENOW_AUDIT' raw_log_field: body ingestion_labels: service: pipelines: logs/source0__chronicle_w_labels-0: receivers: - udplog exporters: - chronicle/chronicle_w_labels
- Ganti port dan alamat IP sesuai kebutuhan di infrastruktur Anda.
- Ganti
<YOUR_CUSTOMER_ID>dengan ID Pelanggan yang sebenarnya. - Ganti
<CUSTOMER_REGION_ENDPOINT>dengan endpoint regional yang sesuai dari dokumentasi Endpoint Regional. - Ganti
/path/to/ingestion-authentication-file.jsondengan jalur tempat file autentikasi disimpan di bagian Mendapatkan file autentikasi penyerapan Google SecOps.
Mulai ulang agen Bindplane untuk menerapkan perubahan
Untuk memulai ulang agen Bindplane di Linux, jalankan perintah berikut:
sudo systemctl restart bindplane-agentUntuk memulai ulang agen Bindplane di Windows, Anda dapat menggunakan konsol Services atau memasukkan perintah berikut:
net stop BindPlaneAgent && net start BindPlaneAgent
Membuat skrip untuk meneruskan log Audit ServiceNow ke syslog
Karena ServiceNow tidak mendukung syslog secara native untuk log audit, kita akan membuat skrip yang mengkueri ServiceNow REST API dan meneruskan log ke syslog. Skrip ini dapat dijadwalkan untuk dijalankan secara berkala.
Contoh Skrip Python (Linux)
Buat file bernama
servicenow_audit_to_syslog.pydengan konten berikut:import urllib3 import json import datetime import base64 import socket import time import os # ServiceNow API details BASE_URL = 'https://instance.service-now.com' # Replace with your ServiceNow instance URL USERNAME = 'admin' # Replace with your ServiceNow username PASSWORD = 'password' # Replace with your ServiceNow password # Syslog details SYSLOG_SERVER = '127.0.0.1' # Replace with your Bindplane agent IP SYSLOG_PORT = 514 # Replace with your Bindplane agent port # State file to keep track of last run STATE_FILE = '/tmp/servicenow_audit_last_run.txt' # Pagination settings PAGE_SIZE = 1000 MAX_PAGES = 1000 def get_last_run_timestamp(): try: with open(STATE_FILE, 'r') as f: return f.read().strip() except: return '1970-01-01T00:00:00' def update_state_file(timestamp): with open(STATE_FILE, 'w') as f: f.write(timestamp) def send_to_syslog(message): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(message.encode(), (SYSLOG_SERVER, SYSLOG_PORT)) sock.close() def get_audit_logs(last_run_timestamp): """ Query ServiceNow sys_audit table with proper pagination. Uses sys_created_on field for timestamp filtering. """ # Encode credentials auth_string = f"{USERNAME}:{PASSWORD}" auth_bytes = auth_string.encode('ascii') auth_encoded = base64.b64encode(auth_bytes).decode('ascii') # Setup HTTP client http = urllib3.PoolManager() headers = { 'Authorization': f'Basic {auth_encoded}', 'Accept': 'application/json' } results = [] offset = 0 for page in range(MAX_PAGES): # Build query with pagination # Use sys_created_on (not created_on) for timestamp filtering query_params = ( f"sysparm_query=sys_created_onAFTER{last_run_timestamp}" f"&sysparm_display_value=true" f"&sysparm_limit={PAGE_SIZE}" f"&sysparm_offset={offset}" ) url = f"{BASE_URL}/api/now/table/sys_audit?{query_params}" try: response = http.request('GET', url, headers=headers) if response.status == 200: data = json.loads(response.data.decode('utf-8')) chunk = data.get('result', []) results.extend(chunk) # Stop if we got fewer records than PAGE_SIZE (last page) if len(chunk) < PAGE_SIZE: break # Move to next page offset += PAGE_SIZE else: print(f"Error querying ServiceNow API: {response.status} - {response.data.decode('utf-8')}") break except Exception as e: print(f"Exception querying ServiceNow API: {str(e)}") break return results def main(): # Get last run timestamp last_run_timestamp = get_last_run_timestamp() # Current timestamp for this run current_timestamp = datetime.datetime.now().isoformat() # Query ServiceNow API for audit logs audit_logs = get_audit_logs(last_run_timestamp) if audit_logs: # Send each log to syslog for log in audit_logs: # Format the log as JSON log_json = json.dumps(log) # Send to syslog send_to_syslog(log_json) # Sleep briefly to avoid flooding time.sleep(0.01) # Update state file update_state_file(current_timestamp) print(f"Successfully forwarded {len(audit_logs)} audit logs to syslog") else: print("No new audit logs to forward") if __name__ == "__main__": main()
Menyiapkan Eksekusi Terjadwal (Linux)
Setel agar skrip dapat dieksekusi:
chmod +x servicenow_audit_to_syslog.pyBuat cron job untuk menjalankan skrip setiap jam:
crontab -eTambahkan baris berikut:
0 * * * * /usr/bin/python3 /path/to/servicenow_audit_to_syslog.py >> /tmp/servicenow_audit_to_syslog.log 2>&1
Contoh Skrip PowerShell (Windows)
Buat file bernama
ServiceNow-Audit-To-Syslog.ps1dengan konten berikut:# ServiceNow API details $BaseUrl = 'https://instance.service-now.com' # Replace with your ServiceNow instance URL $Username = 'admin' # Replace with your ServiceNow username $Password = 'password' # Replace with your ServiceNow password # Syslog details $SyslogServer = '127.0.0.1' # Replace with your Bindplane agent IP $SyslogPort = 514 # Replace with your Bindplane agent port # State file to keep track of last run $StateFile = "$env:TEMP\ServiceNowAuditLastRun.txt" # Pagination settings $PageSize = 1000 $MaxPages = 1000 function Get-LastRunTimestamp { try { if (Test-Path $StateFile) { return Get-Content $StateFile } else { return '1970-01-01T00:00:00' } } catch { return '1970-01-01T00:00:00' } } function Update-StateFile { param ( [string]$Timestamp ) Set-Content -Path $StateFile -Value $Timestamp } function Send-ToSyslog { param ( [string]$Message ) $UdpClient = New-Object System.Net.Sockets.UdpClient $UdpClient.Connect($SyslogServer, $SyslogPort) $Encoding = [System.Text.Encoding]::ASCII $Bytes = $Encoding.GetBytes($Message) $UdpClient.Send($Bytes, $Bytes.Length) $UdpClient.Close() } function Get-AuditLogs { param ( [string]$LastRunTimestamp ) # Create auth header $Auth = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("${Username}:${Password}")) $Headers = @{ Authorization = "Basic ${Auth}" Accept = 'application/json' } $Results = @() $Offset = 0 for ($page = 0; $page -lt $MaxPages; $page++) { # Build query with pagination # Use sys_created_on (not created_on) for timestamp filtering $QueryParams = "sysparm_query=sys_created_onAFTER${LastRunTimestamp}&sysparm_display_value=true&sysparm_limit=${PageSize}&sysparm_offset=${Offset}" $Url = "${BaseUrl}/api/now/table/sys_audit?${QueryParams}" try { $Response = Invoke-RestMethod -Uri $Url -Headers $Headers -Method Get $Chunk = $Response.result $Results += $Chunk # Stop if we got fewer records than PageSize (last page) if ($Chunk.Count -lt $PageSize) { break } # Move to next page $Offset += $PageSize } catch { Write-Error "Error querying ServiceNow API: $_" break } } return $Results } # Main execution $LastRunTimestamp = Get-LastRunTimestamp $CurrentTimestamp = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss') $AuditLogs = Get-AuditLogs -LastRunTimestamp $LastRunTimestamp if ($AuditLogs -and $AuditLogs.Count -gt 0) { # Send each log to syslog foreach ($Log in $AuditLogs) { # Format the log as JSON $LogJson = $Log | ConvertTo-Json -Compress # Send to syslog Send-ToSyslog -Message $LogJson # Sleep briefly to avoid flooding Start-Sleep -Milliseconds 10 } # Update state file Update-StateFile -Timestamp $CurrentTimestamp Write-Output "Successfully forwarded $($AuditLogs.Count) audit logs to syslog" } else { Write-Output "No new audit logs to forward" }
Menyiapkan Eksekusi Terjadwal (Windows)
- Buka Task Scheduler.
- Klik Create Task.
- Berikan konfigurasi berikut:
- Name: ServiceNowAuditToSyslog
- Opsi keamanan: Jalankan apakah pengguna login atau tidak
- Buka tab Pemicu.
- Klik New dan tetapkan agar berjalan setiap jam.
- Buka tab Tindakan.
- Klik Baru dan tetapkan:
- Tindakan: Mulai program
- Program/script: powershell.exe
- Argumen: -ExecutionPolicy Bypass -File "C:\path\to\ServiceNow-Audit-To-Syslog.ps1"
- Klik OK untuk menyimpan tugas.
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.