Raccogliere i log di controllo di ServiceNow

Supportato in:

Questo documento spiega come importare i log di controllo di ServiceNow in Google Security Operations utilizzando più metodi.

Opzione A: AWS S3 con Lambda

Questo metodo utilizza AWS Lambda per eseguire periodicamente query sull'API REST di ServiceNow per gli audit log e archiviarli in un bucket S3. Google Security Operations raccoglie quindi i log dal bucket S3.

Prima di iniziare

  • Un'istanza Google SecOps
  • Accesso con privilegi al tenant o all'API ServiceNow
  • Accesso privilegiato ad AWS (S3, IAM, Lambda, EventBridge)

Raccogli i prerequisiti di ServiceNow (ID, chiavi API, ID organizzazione, token)

  1. Accedi alla Console di amministrazione ServiceNow.
  2. Vai a Sicurezza del sistema > Utenti e gruppi > Utenti.
  3. Crea un nuovo utente o selezionane uno esistente con le autorizzazioni appropriate per accedere ai log di controllo.
  4. Copia e salva in una posizione sicura i seguenti dettagli:
    • Nome utente
    • Password
    • URL dell'istanza (ad es. https://instance.service-now.com)

Configura il bucket AWS S3 e IAM per Google SecOps

  1. Crea un bucket Amazon S3 seguendo questa guida utente: Creazione di un bucket
  2. Salva il nome e la regione del bucket per riferimento futuro (ad esempio, servicenow-audit-logs).
  3. Crea un utente seguendo questa guida utente: Creazione di un utente IAM.
  4. Seleziona l'utente creato.
  5. Seleziona la scheda Credenziali di sicurezza.
  6. Fai clic su Crea chiave di accesso nella sezione Chiavi di accesso.
  7. Seleziona Servizio di terze parti come Caso d'uso.
  8. Fai clic su Avanti.
  9. (Facoltativo) Aggiungi un tag di descrizione.
  10. Fai clic su Crea chiave di accesso.
  11. Fai clic su Scarica file CSV per salvare la chiave di accesso e la chiave di accesso segreta per un utilizzo successivo.
  12. Fai clic su Fine.
  13. Seleziona la scheda Autorizzazioni.
  14. Fai clic su Aggiungi autorizzazioni nella sezione Criteri per le autorizzazioni .
  15. Seleziona Aggiungi autorizzazioni.
  16. Seleziona Collega direttamente i criteri.
  17. Cerca e seleziona il criterio AmazonS3FullAccess.
  18. Fai clic su Avanti.
  19. Fai clic su Aggiungi autorizzazioni.

Configura il ruolo e il criterio IAM per i caricamenti S3

  1. Nella console AWS, vai a IAM > Policy > Crea policy > scheda JSON.
  2. Copia e incolla il criterio riportato di seguito.

    {
      "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"
        }
      ]
    }
    
    • Sostituisci servicenow-audit-logs se hai inserito un nome bucket diverso.
  3. Fai clic su Avanti > Crea policy.

  4. Vai a IAM > Ruoli > Crea ruolo > Servizio AWS > Lambda.

  5. Allega la policy appena creata.

  6. Assegna al ruolo il nome servicenow-audit-lambda-role e fai clic su Crea ruolo.

Crea la funzione Lambda

  1. Nella console AWS, vai a Lambda > Funzioni > Crea funzione.
  2. Fai clic su Autore da zero.
  3. Fornisci i seguenti dettagli di configurazione:

    Impostazione Valore
    Nome servicenow-audit-collector
    Tempo di esecuzione Python 3.13
    Architettura x86_64
    Ruolo di esecuzione servicenow-audit-lambda-role
  4. Dopo aver creato la funzione, apri la scheda Codice, elimina lo stub e inserisci il seguente codice (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 results
    
  5. Vai a Configurazione > Variabili di ambiente > Modifica > Aggiungi nuova variabile di ambiente.

  6. Inserisci le seguenti variabili di ambiente, sostituendole con i tuoi valori.

    Chiave Valore di esempio
    S3_BUCKET servicenow-audit-logs
    S3_PREFIX audit-logs/
    STATE_KEY audit-logs/state.json
    API_BASE_URL https://instance.service-now.com
    API_USERNAME <your-username>
    API_PASSWORD <your-password>
    PAGE_SIZE 1000
    MAX_PAGES 1000
  7. Dopo aver creato la funzione, rimani sulla relativa pagina (o apri Lambda > Funzioni > servicenow-audit-collector).

  8. Seleziona la scheda Configurazione.

  9. Nel riquadro Configurazione generale, fai clic su Modifica.

  10. Modifica Timeout in 5 minuti (300 secondi) e fai clic su Salva.

Creare una pianificazione EventBridge

  1. Vai a Amazon EventBridge > Scheduler > Crea pianificazione.
  2. Fornisci i seguenti dettagli di configurazione:
    • Programma ricorrente: Tariffa (1 hour).
    • Target: la tua funzione Lambda servicenow-audit-collector.
    • Nome: servicenow-audit-collector-1h
  3. Fai clic su Crea pianificazione.

Configura un feed in Google SecOps per importare i log di controllo di ServiceNow

  1. Vai a Impostazioni SIEM > Feed.
  2. Fai clic su + Aggiungi nuovo feed.
  3. Nel campo Nome feed, inserisci un nome per il feed (ad esempio, ServiceNow Audit logs).
  4. Seleziona Amazon S3 V2 come Tipo di origine.
  5. Seleziona ServiceNow Audit come Tipo di log.
  6. Fai clic su Avanti.
  7. Specifica i valori per i seguenti parametri di input:
    • URI S3: s3://servicenow-audit-logs/audit-logs/
    • Opzioni di eliminazione dell'origine: seleziona l'opzione di eliminazione in base alle tue preferenze.
    • Età massima del file: includi i file modificati nell'ultimo numero di giorni. Il valore predefinito è 180 giorni.
    • ID chiave di accesso: chiave di accesso utente con accesso al bucket S3.
    • Chiave di accesso segreta: chiave segreta dell'utente con accesso al bucket S3.
    • Spazio dei nomi dell'asset: lo spazio dei nomi dell'asset.
    • Etichette di importazione: l'etichetta applicata agli eventi di questo feed.
  8. Fai clic su Avanti.
  9. Controlla la nuova configurazione del feed nella schermata Finalizza e poi fai clic su Invia.

Opzione B: agente Bindplane con syslog

Questo metodo utilizza un agente Bindplane per raccogliere i log di controllo di ServiceNow e inoltrarli a Google Security Operations. Poiché ServiceNow non supporta in modo nativo syslog per i log di controllo, utilizzeremo uno script per eseguire query sull'API REST di ServiceNow e inoltrare i log all'agente Bindplane tramite syslog.

Prima di iniziare

Assicurati di soddisfare i seguenti prerequisiti:

  • Un'istanza Google SecOps
  • Un host Windows 2016 o versioni successive o Linux con systemd
  • Se l'agente viene eseguito dietro un proxy, assicurati che le porte del firewall siano aperte in base ai requisiti dell'agente Bindplane.
  • Accesso privilegiato alla console di gestione o all'appliance ServiceNow

Recuperare il file di autenticazione importazione di Google SecOps

  1. Accedi alla console Google SecOps.
  2. Vai a Impostazioni SIEM > Agenti di raccolta.
  3. Scarica il file di autenticazione importazione. Salva il file in modo sicuro sul sistema in cui verrà installato Bindplane.

Recuperare l'ID cliente Google SecOps

  1. Accedi alla console Google SecOps.
  2. Vai a Impostazioni SIEM > Profilo.
  3. Copia e salva l'ID cliente dalla sezione Dettagli dell'organizzazione.

Installa l'agente Bindplane

Installa l'agente Bindplane sul sistema operativo Windows o Linux seguendo le istruzioni riportate di seguito.

Installazione di Linux

  1. Apri un terminale con privilegi root o sudo.
  2. Esegui questo comando:

    sudo sh -c "$(curl -fsSlL https://github.com/observiq/bindplane-agent/releases/latest/download/install_unix.sh)" install_unix.sh
    

Risorse aggiuntive per l'installazione

Configura l'agente Bindplane per importare Syslog e inviarlo a Google SecOps

  1. Accedi al file di configurazione:

    1. Individua il file config.yaml. In genere si trova nella directory /etc/bindplane-agent/ su Linux o nella directory di installazione su Windows.
    2. Apri il file utilizzando un editor di testo (ad esempio nano, vi o Blocco note).
  2. Modifica il file config.yaml come segue:

    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
    

Riavvia l'agente Bindplane per applicare le modifiche

  • Per riavviare l'agente Bindplane in Linux, esegui questo comando:

    sudo systemctl restart bindplane-agent
    
  • Per riavviare l'agente Bindplane in Windows, puoi utilizzare la console Servizi o inserire il seguente comando:

    net stop BindPlaneAgent && net start BindPlaneAgent
    

Crea uno script per inoltrare i log di controllo di ServiceNow a syslog

Poiché ServiceNow non supporta in modo nativo syslog per gli audit log, creeremo uno script che esegue query sull'API REST di ServiceNow e inoltra i log a syslog. Questo script può essere programmato per essere eseguito periodicamente.

Esempio di script Python (Linux)

  • Crea un file denominato servicenow_audit_to_syslog.py con i seguenti contenuti:

    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()
    

Configurare l'esecuzione pianificata (Linux)

  1. Rendi eseguibile lo script:

    chmod +x servicenow_audit_to_syslog.py
    
  2. Crea un cron job per eseguire lo script ogni ora:

    crontab -e
    
  3. Aggiungi la seguente riga:

    0 * * * * /usr/bin/python3 /path/to/servicenow_audit_to_syslog.py >> /tmp/servicenow_audit_to_syslog.log 2>&1
    

Esempio di script PowerShell (Windows)

  • Crea un file denominato ServiceNow-Audit-To-Syslog.ps1 con i seguenti contenuti:

    # 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"
    }
    

Configurare l'esecuzione pianificata (Windows)

  1. Apri Utilità di pianificazione.
  2. Fai clic su Crea attività.
  3. Fornisci la seguente configurazione:
    • Nome: ServiceNowAuditToSyslog
    • Opzioni di sicurezza: esegui indipendentemente dall'accesso dell'utente
  4. Vai alla scheda Trigger.
  5. Fai clic su Nuovo e impostalo in modo che venga eseguito ogni ora.
  6. Vai alla scheda Azioni.
  7. Fai clic su Nuovo e imposta:
    • Azione: avvia un programma
    • Programma/script: powershell.exe
    • Argomenti: -ExecutionPolicy Bypass -File "C:\path\to\ServiceNow-Audit-To-Syslog.ps1"
  8. Fai clic su Ok per salvare l'attività.

Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.