Coletar registros do Oracle NetSuite - NetSuite Applications Suite

Compatível com:

Este documento explica como ingerir registros do Oracle NetSuite - NetSuite Applications Suite no Google Security Operations usando o Google Cloud Storage V2.

O Oracle NetSuite é uma plataforma de planejamento de recursos empresariais (ERP) baseada na nuvem que oferece recursos abrangentes de gestão de negócios, incluindo gestão financeira, gestão de relacionamento com o cliente (CRM), e-commerce e gerenciamento de inventário. O NetSuite gera registros de auditoria extensos, incluindo trilhas de login de usuários, notas do sistema que rastreiam alterações de registros e registros de acesso à API que podem ser recuperados pela API REST do SuiteQL para fins de monitoramento de segurança e conformidade.

Antes de começar

Verifique se você atende os seguintes pré-requisitos:

  • Uma instância do Google SecOps
  • Um projeto do GCP com as APIs Cloud Storage, Cloud Run, Pub/Sub e API Cloud Scheduler ativadas
  • Permissões para criar e gerenciar buckets do GCS
  • Permissões para gerenciar políticas do IAM em buckets do GCS
  • Permissões para criar serviços do Cloud Run, tópicos do Pub/Sub e jobs do Cloud Scheduler
  • Uma conta do NetSuite com acesso de administrador
  • Serviços da Web REST do NetSuite ativados
  • Permissões para criar registros de integração e tokens de acesso no NetSuite

Configurar o acesso à API NetSuite

Para ativar a coleta de registros pela API REST do SuiteQL, é necessário ativar os serviços da Web REST, criar um registro de integração e gerar credenciais de autenticação baseada em token (TBA, na sigla em inglês) no NetSuite.

Ativar os recursos necessários do NetSuite

  1. Faça login na sua conta do NetSuite como administrador.
  2. Acesse Setup > Company > Enable Features.
  3. Clique na guia SuiteCloud.
  4. Na seção SuiteScript, marque as seguintes caixas:
    • SuiteScript do cliente
    • Server SuiteScript
  5. Na seção SuiteTalk (Web Services), marque a seguinte caixa:
    • Serviços REST da Web
  6. Na seção Manage Authentication, marque a caixa Token-Based Authentication.
  7. Clique em Concordo na página "Termos de Serviço do SuiteCloud".
  8. Clique em Salvar.

Criar um registro de integração

  1. No NetSuite, acesse Configuração > Integração > Gerenciar integrações > Novo.
  2. No campo Nome, use Google SecOps Integration.
  3. No campo Descrição, use Integration for Google Security Operations log collection.
  4. No campo Estado, deixe a opção padrão Ativado selecionada.
  5. Clique na guia Authentication.
  6. Na seção Autenticação baseada em token, marque a caixa Autenticação baseada em token.
  7. Deixe a caixa TBA: Authorization Flow desmarcada.
  8. Marque a caixa TBA: ISSUETOKEN ENDPOINT.
  9. Clique em Salvar.
  10. Na página de confirmação, copie e salve o token do cliente/ID do cliente e o segredo do consumidor/chave secreta do cliente.

Criar um papel personalizado para acesso à API

  1. Acesse Setup > Users/Roles > Manage Roles > New.
  2. No campo Nome, use Google SecOps API Role.
  3. No campo ID, insira google_secops_api_role.
  4. Clique na subguia Permissões.
  5. Clique na subguia Configuração.
  6. Adicione as seguintes permissões com o nível de acesso Completo:
    • Fazer login usando tokens de acesso
    • Tokens de acesso do usuário
    • Serviços REST da Web
  7. Clique na subguia Relatórios.
  8. Adicione as seguintes permissões com o nível de acesso Visualizar:
    • SuiteAnalytics Workbook
  9. Clique na subguia Listas.
  10. Adicione as seguintes permissões com o nível de acesso Visualizar:
    • Trilha de auditoria de login
  11. Clique em Salvar.

Atribuir o papel a um usuário

  1. Acesse Lists > Employees > Employees.
  2. Selecione a conta de usuário que será usada para a integração (ou crie um novo usuário de integração dedicado).
  3. Clique em Editar.
  4. Clique na guia Acesso.
  5. Na seção Papéis, clique em Adicionar.
  6. Selecione Papel da API Google SecOps na lista suspensa.
  7. Clique em Salvar.

Criar um token de acesso

  1. Faça login no NetSuite como o usuário atribuído à função de API do Google SecOps.
  2. Acesse Configuração > Usuários/Funções > Tokens de acesso > Novo.
  3. Na página Token de acesso:
    • Na lista suspensa Nome do aplicativo, selecione Integração do Google SecOps.
    • Na lista suspensa Usuário, selecione a conta de usuário que recebeu o papel da API Google SecOps.
    • Na lista suspensa Papel, selecione Papel da API Google SecOps.
    • O campo Nome do token é preenchido automaticamente. Você pode modificá-lo, se preferir.
  4. Clique em Salvar.
  5. Na página de confirmação, copie e salve o ID de token e a chave secreta do token.

Encontrar o ID da conta do NetSuite

  1. No NetSuite, acesse Setup > Company > Company Information.
  2. Localize o campo ID da conta (exibido abaixo da configuração Fuso horário).
  3. Copie e salve o ID da conta (por exemplo, 1234567 ou 1234567_SB1 para contas de sandbox).

Verifique as permissões

Para verificar se a conta tem as permissões necessárias:

  1. Faça login no NetSuite com o usuário que recebeu a função de API do Google SecOps.
  2. Acesse Configuração > Usuários/Funções > Ver trilha de auditoria de login.
  3. Se você conseguir ver a página "Trilha de auditoria de login", a conta tem as permissões necessárias.
  4. Se essa opção não aparecer, entre em contato com o administrador do NetSuite para conceder a permissão Trilha de auditoria de login na subguia Listas.

Testar o acesso à API

  • Teste suas credenciais antes de prosseguir com a integração:

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

Uma resposta bem-sucedida retorna um objeto JSON que contém registros LoginAudit.

Criar um bucket do Google Cloud Storage

  1. Acesse o Console do Google Cloud.
  2. Selecione seu projeto ou crie um novo.
  3. No menu de navegação, acesse Cloud Storage > Buckets.
  4. Clique em Criar bucket.
  5. Informe os seguintes detalhes de configuração:

    Configuração Valor
    Nomeie seu bucket Insira um nome globalmente exclusivo (por exemplo, netsuite-audit-logs)
    Tipo de local Escolha de acordo com suas necessidades (região, birregional, multirregional)
    Local Selecione o local (por exemplo, us-central1).
    Classe de armazenamento Padrão (recomendado para registros acessados com frequência)
    Controle de acesso Uniforme (recomendado)
    Ferramentas de proteção Opcional: ativar o controle de versões de objetos ou a política de retenção
  6. Clique em Criar.

Criar uma conta de serviço para a função do Cloud Run

A função do Cloud Run precisa de uma conta de serviço com permissões para gravar no bucket do GCS e ser invocada pelo Pub/Sub.

Criar conta de serviço

  1. No Console do GCP, acesse IAM e administrador > Contas de serviço.
  2. Clique em Criar conta de serviço.
  3. Informe os seguintes detalhes de configuração:
    • Nome da conta de serviço: insira netsuite-audit-collector-sa.
    • Descrição da conta de serviço: insira Service account for Cloud Run function to collect NetSuite audit logs.
  4. Clique em Criar e continuar.
  5. Na seção Conceder acesso a essa conta de serviço ao projeto, adicione os seguintes papéis:
    1. Clique em Selecionar papel.
    2. Pesquise e selecione Administrador de objetos do Storage.
    3. Clique em + Adicionar outro papel.
    4. Pesquise e selecione Invocador do Cloud Run.
    5. Clique em + Adicionar outro papel.
    6. Pesquise e selecione Invocador do Cloud Functions.
  6. Clique em Continuar.
  7. Clique em Concluído.

Esses papéis são necessários para:

  • Administrador de objetos do Storage: gravar registros no bucket do GCS e gerenciar arquivos de estado
  • Invocador do Cloud Run: permite que o Pub/Sub invoque a função.
  • Invocador do Cloud Functions: permite a invocação de funções.

Conceder permissões do IAM no bucket do GCS

  1. Acesse Cloud Storage > Buckets.
  2. Clique no nome do bucket (netsuite-audit-logs).
  3. Acesse a guia Permissões.
  4. Clique em Conceder acesso.
  5. Informe os seguintes detalhes de configuração:
    • Adicionar principais: insira o e-mail da conta de serviço (netsuite-audit-collector-sa@PROJECT_ID.iam.gserviceaccount.com)
    • Atribuir papéis: selecione Administrador de objetos do Storage.
  6. Clique em Salvar.

Criar tópico Pub/Sub

  1. No Console do GCP, acesse Pub/Sub > Tópicos.
  2. Selecione Criar tópico.
  3. Informe os seguintes detalhes de configuração:
    • ID do tópico: insira netsuite-audit-trigger.
    • Não mude as outras configurações.
  4. Clique em Criar.

Criar uma função do Cloud Run para coletar registros

A função do Cloud Run será acionada por mensagens do Pub/Sub do Cloud Scheduler para buscar registros da API REST SuiteQL do NetSuite e gravá-los no GCS.

  1. No Console do GCP, acesse o Cloud Run.
  2. Clique em Criar serviço.
  3. Selecione Função (use um editor in-line para criar uma função).
  4. Na seção Configurar, forneça os seguintes detalhes de configuração:

    Configuração Valor
    Nome do serviço netsuite-audit-collector
    Região Selecione a região correspondente ao bucket do GCS (por exemplo, us-central1).
    Ambiente de execução Selecione Python 3.12 ou mais recente.
  5. Na seção Acionador (opcional):

    1. Clique em + Adicionar gatilho.
    2. Selecione Cloud Pub/Sub.
    3. Em Selecionar um tópico do Cloud Pub/Sub, escolha netsuite-audit-trigger.
    4. Clique em Salvar.
  6. Na seção Autenticação:

    1. Selecione Exigir autenticação.
    2. Verifique o Identity and Access Management (IAM).
  7. Role para baixo e abra Contêineres, rede, segurança.

  8. Acesse a guia Segurança:

    • Conta de serviço: selecione netsuite-audit-collector-sa.
  9. Acesse a guia Contêineres:

    1. Clique em Variáveis e secrets.
    2. Clique em + Adicionar variável para cada variável de ambiente:
    Nome da variável Valor de exemplo Descrição
    GCS_BUCKET netsuite-audit-logs Nome do bucket do GCS
    GCS_PREFIX netsuite-audit Prefixo para arquivos de registro
    STATE_KEY netsuite-audit/state.json Caminho do arquivo de estado
    NS_ACCOUNT_ID 1234567 ID da conta do NetSuite
    NS_CONSUMER_KEY your-consumer-key Token do cliente do registro de integração
    NS_CONSUMER_SECRET your-consumer-secret Secret do cliente do registro de integração
    NS_TOKEN_ID your-token-id ID de token do token de acesso
    NS_TOKEN_SECRET your-token-secret Secret do token de acesso
    MAX_RECORDS 10000 Máximo de registros por execução
    PAGE_SIZE 1000 Registros por página do SuiteQL
    LOOKBACK_HOURS 24 Período de lookback inicial
  10. Na seção Variáveis e secrets, role para baixo até Solicitações:

    • Tempo limite da solicitação: insira 600 segundos (10 minutos)
  11. Acesse a guia Configurações:

    • Na seção Recursos:
      • Memória: selecione 512 MiB ou mais.
      • CPU: selecione 1.
  12. Na seção Escalonamento de revisão:

    • Número mínimo de instâncias: insira 0.
    • Número máximo de instâncias: insira 100.
  13. Clique em Criar.

  14. Aguarde a criação do serviço (1 a 2 minutos).

  15. Depois que o serviço for criado, o editor de código inline será aberto automaticamente.

Adicionar código da função

  1. Insira main no campo Ponto de entrada.
  2. No editor de código inline, crie dois arquivos:

    • 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. Clique em Implantar para salvar e implantar a função.

  4. Aguarde a conclusão da implantação (2 a 3 minutos).

Criar o job do Cloud Scheduler

O Cloud Scheduler vai publicar mensagens no tópico do Pub/Sub em intervalos regulares, acionando a função do Cloud Run.

  1. No Console do GCP, acesse o Cloud Scheduler.
  2. Clique em Criar job.
  3. Informe os seguintes detalhes de configuração:

    Configuração Valor
    Nome netsuite-audit-collector-hourly
    Região Selecione a mesma região da função do Cloud Run
    Frequência 0 * * * * (de hora em hora)
    Fuso horário Selecione o fuso horário (UTC recomendado)
    Tipo de destino Pub/Sub
    Tópico Selecionar netsuite-audit-trigger
    Corpo da mensagem {} (objeto JSON vazio)
  4. Clique em Criar.

Opções de frequência de programação

Escolha a frequência com base no volume de registros e nos requisitos de latência:

Frequência Expressão Cron Caso de uso
A cada 5 minutos */5 * * * * Alto volume e baixa latência
A cada 15 minutos */15 * * * * Volume médio
A cada hora 0 * * * * Padrão (recomendado)
A cada 6 horas 0 */6 * * * Processamento em lote de baixo volume
Diário 0 0 * * * Coleta de dados históricos

Testar a integração

  1. No console do Cloud Scheduler, encontre seu job (netsuite-audit-collector-hourly).
  2. Clique em Forçar execução para acionar o job manualmente.
  3. Aguarde alguns segundos.
  4. Acesse Cloud Run > Serviços.
  5. Clique em netsuite-audit-collector.
  6. Clique na guia Registros.
  7. Verifique se a função foi executada com sucesso. Procure por:

    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. Acesse Cloud Storage > Buckets.

  9. Clique em netsuite-audit-logs.

  10. Navegue até a pasta netsuite-audit/.

  11. Verifique se um novo arquivo .ndjson foi criado com o carimbo de data/hora atual.

Se você encontrar erros nos registros:

  • HTTP 401: verifique se as credenciais do OAuth (NS_CONSUMER_KEY, NS_CONSUMER_SECRET, NS_TOKEN_ID, NS_TOKEN_SECRET) e as variáveis de ambiente NS_ACCOUNT_ID estão corretas.
  • HTTP 403: verifique se a função do NetSuite tem as permissões necessárias (serviços da Web REST, trilha de auditoria de login, pasta de trabalho do SuiteAnalytics).
  • HTTP 429: limitação de taxa. A função vai tentar novamente de forma automática com espera exponencial.
  • Variáveis de ambiente ausentes: verifique se todas as variáveis necessárias estão definidas na configuração da função do Cloud Run.

Recuperar a conta de serviço do Google SecOps

  1. Acesse Configurações > Gerenciamento de dados > Feeds. Se você estiver em uma plataforma somente SIEM, acesse Configurações > Feeds.
  2. Clique em Adicionar novo feed.
  3. Clique em Configurar um único feed.
  4. No campo Nome do feed, insira um nome para o feed (por exemplo, NetSuite Audit Logs).
  5. Selecione Google Cloud Storage V2 como o Tipo de origem.
  6. Selecione Oracle NetSuite - NetSuite Applications Suite como o Tipo de registro.
  7. Clique em Get Service Account. Um e-mail exclusivo da conta de serviço será exibido. Exemplo:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. Copie esse endereço de e-mail para usar na próxima etapa.

  9. Clique em Próxima.

  10. Especifique valores para os seguintes parâmetros de entrada:

    • URL do bucket de armazenamento: insira o URI do bucket do GCS com o caminho do prefixo:

      gs://netsuite-audit-logs/netsuite-audit/
      
    • Opção de exclusão da fonte: selecione a opção de exclusão de acordo com sua preferência:
      • Nunca: nunca exclui arquivos após as transferências (recomendado para testes).
      • Excluir arquivos transferidos: exclui arquivos após a transferência.
      • Excluir arquivos transferidos e diretórios vazios: exclui arquivos e diretórios vazios após a transferência.
    • Idade máxima do arquivo: inclua arquivos modificados nos últimos dias (o padrão é 180 dias).
    • Namespace do recurso: o namespace do recurso
    • Rótulos de ingestão: o rótulo que será aplicado aos eventos desse feed.
  11. Clique em Próxima.

  12. Revise a configuração do novo feed na tela Finalizar e clique em Enviar.

Conceder permissões do IAM à conta de serviço do Google SecOps

A conta de serviço do Google SecOps precisa do papel Leitor de objetos do Storage no bucket do GCS.

  1. Acesse Cloud Storage > Buckets.
  2. Clique em netsuite-audit-logs.
  3. Acesse a guia Permissões.
  4. Clique em Conceder acesso.
  5. Informe os seguintes detalhes de configuração:
    • Adicionar participantes: cole o e-mail da conta de serviço do Google SecOps.
    • Atribuir papéis: selecione Leitor de objetos do Storage.
  6. Clique em Salvar.

Limites de taxa da API NetSuite

A API NetSuite tem os seguintes limites de simultaneidade e taxa:

Tipo de limite Valor padrão Observações
Solicitações simultâneas 15 solicitações Aplica-se a solicitações combinadas de API SOAP e REST por conta
Resultados da consulta do SuiteQL Máximo de 100.000 linhas Máximo de linhas retornadas por consulta do SuiteQL
Objetos por solicitação Máximo de 1.000 objetos Número máximo de objetos retornados por solicitação de API

A função do Cloud Run lida automaticamente com a limitação de taxa com espera exponencial. Se você encontrar erros persistentes de limitação de taxa (HTTP 429), entre em contato com o suporte do NetSuite para aumentar os limites da API ou ajustar a frequência do Cloud Scheduler.

Tabela de mapeamento de UDM

Campo de registro Mapeamento de UDM Lógica
usuário extensions.auth.type Tipo de mecanismo de autenticação usado
data metadata.event_timestamp Carimbo de data/hora de quando o evento ocorreu
event_type metadata.event_type Tipo de evento (por exemplo, USER_LOGIN, NETWORK_CONNECTION)
security_result security_result Detalhes sobre o resultado de segurança do evento
oauthaccesstokenname security_result.about.resource.attribute.labels Rótulos de chave-valor que descrevem atributos do recurso envolvido no resultado de segurança
secchallenge security_result.about.resource.attribute.labels
status security_result.summary Descrição resumida do resultado de segurança
oauthappname target.application Nome do aplicativo de destino
useragent target.application
ipaddress target.ip Endereço IP do destino
requesturi target.url URL do recurso de destino
emailaddress target.user.email_addresses Endereços de e-mail associados ao usuário de destino
papel target.user.role_name Nome da função atribuída ao usuário de destino
usuário target.user.userid Identificador exclusivo do usuário de destino

Registro de alterações

Confira o registro de alterações deste analisador

Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.