Recolha registos do Symantec WSS

Compatível com:

Este documento explica como carregar registos do Symantec Web Security Service (WSS) para o Google Security Operations através do Amazon S3. O analisador tenta primeiro analisar a mensagem de registo como JSON. Se falhar, usa uma série de padrões grok cada vez mais específicos para extrair campos do texto não processado, mapeando, em última análise, os dados extraídos para o modelo de dados unificado (UDM).

Antes de começar

Certifique-se de que cumpre os seguintes pré-requisitos:

  • Uma instância do Google SecOps.
  • Acesso privilegiado ao Symantec Web Security Service.
  • Acesso privilegiado à AWS (S3, Identity and Access Management [IAM], Lambda e EventBridge).

Recolha os pré-requisitos do Symantec WSS (IDs, chaves da API, IDs da organização, tokens)

  1. Inicie sessão no portal do serviço de segurança Web da Symantec como administrador.
  2. Aceda a Conta > Credenciais da API.
  3. Clique em Adicionar.
  4. Indique os seguintes detalhes de configuração:
    • Nome da API: introduza um nome descritivo (por exemplo, Google SecOps Integration).
    • Descrição: introduza uma descrição para as credenciais da API.
  5. Clique em Guardar e copie as credenciais da API geradas de forma segura.
  6. Registe o URL do portal do WSS e o ponto final da API de sincronização.
  7. Copie e guarde numa localização segura os seguintes detalhes:
    • WSS_API_USERNAME.
    • WSS_API_PASSWORD.
    • WSS_SYNC_URL.

Configure o contentor do AWS S3 e o IAM para o Google SecOps

  1. Crie um contentor do Amazon S3 seguindo este guia do utilizador: Criar um contentor
  2. Guarde o nome e a região do contentor para referência futura (por exemplo, symantec-wss-logs).
  3. Crie um utilizador seguindo este guia do utilizador: criar um utilizador do IAM.
  4. Selecione o utilizador criado.
  5. Selecione o separador Credenciais de segurança.
  6. Clique em Criar chave de acesso na secção Chaves de acesso.
  7. Selecione Serviço de terceiros como Exemplo de utilização.
  8. Clicar em Seguinte.
  9. Opcional: adicione uma etiqueta de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Transferir ficheiro CSV para guardar a chave de acesso e a chave de acesso secreta para referência futura.
  12. Clique em Concluído.
  13. Selecione o separador Autorizações.
  14. Clique em Adicionar autorizações na secção Políticas de autorizações.
  15. Selecione Adicionar autorizações.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise a política AmazonS3FullAccess.
  18. Selecione a política.
  19. Clicar em Seguinte.
  20. Clique em Adicionar autorizações.

Configure a política e a função de IAM para carregamentos do S3

  1. Na consola da AWS, aceda a IAM > Políticas.
  2. Clique em Criar política > separador JSON.
  3. Copie e cole a seguinte política.
  4. JSON da política (substitua symantec-wss-logs se tiver introduzido um nome de contentor diferente):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::symantec-wss-logs/*"
        },
        {
          "Sid": "AllowGetStateObject",
          "Effect": "Allow",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::symantec-wss-logs/symantec/wss/state.json"
        }
      ]
    }
    
  5. Clique em Seguinte > Criar política.

  6. Aceda a IAM > Funções > Criar função > Serviço AWS > Lambda.

  7. Anexe a política criada recentemente.

  8. Dê o nome SymantecWssToS3Role à função e clique em Criar função.

Crie a função Lambda

  1. Na consola da AWS, aceda a Lambda > Functions > Create function.
  2. Clique em Criar do zero.
  3. Faculte os seguintes detalhes de configuração:

    Definição Valor
    Nome symantec_wss_to_s3
    Runtime Python 3.13
    Arquitetura x86_64
    Função de execução SymantecWssToS3Role
  4. Depois de criar a função, abra o separador Código, elimine o stub e cole o seguinte código (symantec_wss_to_s3.py).

    #!/usr/bin/env python3
    # Lambda: Pull Symantec WSS logs and store raw payloads to S3
    # - Time window via millisecond timestamps for WSS Sync API.
    # - Preserves vendor-native format (CSV/JSON/ZIP).
    # - Retries with exponential backoff; unique S3 keys to avoid overwrites.
    
    import os, json, time, uuid
    from urllib.request import Request, urlopen
    from urllib.error import URLError, HTTPError
    
    import boto3
    
    S3_BUCKET   = os.environ["S3_BUCKET"]
    S3_PREFIX   = os.environ.get("S3_PREFIX", "symantec/wss/")
    STATE_KEY   = os.environ.get("STATE_KEY", "symantec/wss/state.json")
    WINDOW_SEC  = int(os.environ.get("WINDOW_SECONDS", "3600"))  # default 1h
    HTTP_TIMEOUT= int(os.environ.get("HTTP_TIMEOUT", "60"))
    WSS_SYNC_URL = os.environ.get("WSS_SYNC_URL", "https://portal.threatpulse.com/reportpod/logs/sync")
    API_USERNAME = os.environ["WSS_API_USERNAME"]
    API_PASSWORD = os.environ["WSS_API_PASSWORD"]
    TOKEN_PARAM  = os.environ.get("WSS_TOKEN_PARAM", "none")
    MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "3"))
    USER_AGENT  = os.environ.get("USER_AGENT", "symantec-wss-to-s3/1.0")
    
    s3 = boto3.client("s3")
    
    def _load_state():
        try:
            obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
            return json.loads(obj["Body"].read())
        except Exception:
            return {}
    
    def _save_state(st):
        s3.put_object(
            Bucket=S3_BUCKET,
            Key=STATE_KEY,
            Body=json.dumps(st, separators=(",", ":")).encode("utf-8"),
            ContentType="application/json",
        )
    
    def _ms_timestamp(ts: float) -> int:
        """Convert Unix timestamp to milliseconds for WSS API"""
        return int(ts * 1000)
    
    def _fetch_wss_logs(start_ms: int, end_ms: int) -> tuple[bytes, str, str]:
        # WSS Sync API parameters
        params = f"startDate={start_ms}&endDate={end_ms}&token={TOKEN_PARAM}"
        url = f"{WSS_SYNC_URL}?{params}"
    
        attempt = 0
        while True:
            req = Request(url, method="GET")
            req.add_header("User-Agent", USER_AGENT)
            req.add_header("X-APIUsername", API_USERNAME)
            req.add_header("X-APIPassword", API_PASSWORD)
    
            try:
                with urlopen(req, timeout=HTTP_TIMEOUT) as r:
                    blob = r.read()
                    content_type = r.headers.get("Content-Type", "application/octet-stream")
                    content_encoding = r.headers.get("Content-Encoding", "")
                    return blob, content_type, content_encoding
            except (HTTPError, URLError) as e:
                attempt += 1
                print(f"HTTP error on attempt {attempt}: {e}")
                if attempt > MAX_RETRIES:
                    raise
                # exponential backoff with jitter
                time.sleep(min(60, 2 ** attempt) + (time.time() % 1))
    
    def _determine_extension(content_type: str, content_encoding: str) -> str:
        """Determine file extension based on content type and encoding"""
        if "zip" in content_type.lower():
            return ".zip"
        if "gzip" in content_type.lower() or content_encoding.lower() == "gzip":
            return ".gz"
        if "json" in content_type.lower():
            return ".json"
        if "csv" in content_type.lower():
            return ".csv"
        return ".bin"
    
    def _put_wss_data(blob: bytes, content_type: str, content_encoding: str, from_ts: float, to_ts: float) -> str:
        # Create unique S3 key for WSS data
        ts_path = time.strftime("%Y/%m/%d", time.gmtime(to_ts))
        uniq = f"{int(time.time()*1e6)}_{uuid.uuid4().hex[:8]}"
        ext = _determine_extension(content_type, content_encoding)
        key = f"{S3_PREFIX}{ts_path}/symantec_wss_{int(from_ts)}_{int(to_ts)}_{uniq}{ext}"
    
        s3.put_object(
            Bucket=S3_BUCKET, 
            Key=key, 
            Body=blob, 
            ContentType=content_type,
            Metadata={
                'source': 'symantec-wss',
                'from_timestamp': str(int(from_ts)),
                'to_timestamp': str(int(to_ts)),
                'content_encoding': content_encoding
            }
        )
        return key
    
    def lambda_handler(event=None, context=None):
        st = _load_state()
        now = time.time()
        from_ts = float(st.get("last_to_ts") or (now - WINDOW_SEC))
        to_ts = now
    
        # Convert to milliseconds for WSS API
        start_ms = _ms_timestamp(from_ts)
        end_ms = _ms_timestamp(to_ts)
    
        print(f"Fetching Symantec WSS logs from {start_ms} to {end_ms}")
    
        blob, content_type, content_encoding = _fetch_wss_logs(start_ms, end_ms)
    
        print(f"Retrieved {len(blob)} bytes with content-type: {content_type}")
        if content_encoding:
            print(f"Content encoding: {content_encoding}")
    
        key = _put_wss_data(blob, content_type, content_encoding, from_ts, to_ts)
    
        st["last_to_ts"] = to_ts
        st["last_successful_run"] = now
        _save_state(st)
    
        return {
            "statusCode": 200,
            "body": {
                "success": True, 
                "s3_key": key, 
                "content_type": content_type,
                "content_encoding": content_encoding,
                "from_timestamp": from_ts,
                "to_timestamp": to_ts,
                "bytes_retrieved": len(blob)
            }
        }
    
    if __name__ == "__main__":
        print(lambda_handler())
    
  5. Aceda a Configuração > Variáveis de ambiente.

  6. Clique em Editar > Adicionar nova variável de ambiente.

  7. Introduza as variáveis de ambiente fornecidas na tabela seguinte, substituindo pelos valores de exemplo com os seus valores.

    Variáveis de ambiente

    Chave Valor de exemplo
    S3_BUCKET symantec-wss-logs
    S3_PREFIX symantec/wss/
    STATE_KEY symantec/wss/state.json
    WINDOW_SECONDS 3600
    HTTP_TIMEOUT 60
    MAX_RETRIES 3
    USER_AGENT symantec-wss-to-s3/1.0
    WSS_SYNC_URL https://portal.threatpulse.com/reportpod/logs/sync
    WSS_API_USERNAME your-api-username (do passo 2)
    WSS_API_PASSWORD your-api-password (do passo 2)
    WSS_TOKEN_PARAM none
  8. Depois de criar a função, permaneça na respetiva página (ou abra Lambda > Functions > a sua função).

  9. Selecione o separador Configuração.

  10. No painel Configuração geral, clique em Editar.

  11. Altere Tempo limite para 5 minutos (300 segundos) e clique em Guardar.

Crie um horário do EventBridge

  1. Aceda a Amazon EventBridge > Scheduler > Create schedule.
  2. Indique os seguintes detalhes de configuração:
    • Agenda recorrente: Taxa (1 hour).
    • Destino: a sua função Lambda symantec_wss_to_s3.
    • Nome: symantec-wss-1h.
  3. Clique em Criar programação.

(Opcional) Crie um utilizador e chaves da IAM só de leitura para o Google SecOps

  1. Na consola da AWS, aceda a IAM > Utilizadores.
  2. Clique em Adicionar utilizadores.
  3. Indique os seguintes detalhes de configuração:
    • Utilizador: introduza secops-reader.
    • Tipo de acesso: selecione Chave de acesso – Acesso programático.
  4. Clique em Criar utilizador.
  5. Anexe a política de leitura mínima (personalizada): Users > secops-reader > Permissions > Add permissions > Attach policies directly > Create policy.
  6. JSON:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::symantec-wss-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::symantec-wss-logs"
        }
      ]
    }
    
  7. Nome = secops-reader-policy.

  8. Clique em Criar política > procure/selecione > Seguinte > Adicionar autorizações.

  9. Crie uma chave de acesso para secops-reader: Credenciais de segurança > Chaves de acesso.

  10. Clique em Criar chave de acesso.

  11. Transfira o CSV. (Vai colar estes valores no feed).

Configure um feed no Google SecOps para carregar registos do Symantec WSS

  1. Aceda a Definições do SIEM > Feeds.
  2. Clique em + Adicionar novo feed.
  3. No campo Nome do feed, introduza um nome para o feed (por exemplo, Symantec WSS logs).
  4. Selecione Amazon S3 V2 como o Tipo de origem.
  5. Selecione Symantec WSS como o Tipo de registo.
  6. Clicar em Seguinte.
  7. Especifique valores para os seguintes parâmetros de entrada:
    • URI do S3: s3://symantec-wss-logs/symantec/wss/
    • Opções de eliminação de origens: selecione a opção de eliminação de acordo com a sua preferência.
    • Idade máxima do ficheiro: inclua ficheiros modificados no último número de dias. A predefinição é 180 dias.
    • ID da chave de acesso: chave de acesso do utilizador com acesso ao contentor do S3.
    • Chave de acesso secreta: chave secreta do utilizador com acesso ao contentor do S3.
    • Espaço de nomes do recurso: o espaço de nomes do recurso.
    • Etiquetas de carregamento: a etiqueta aplicada aos eventos deste feed.
  8. Clicar em Seguinte.
  9. Reveja a nova configuração do feed no ecrã Finalizar e, de seguida, clique em Enviar.

Tabela de mapeamento do UDM

Campo de registo Mapeamento do UDM Lógica
category_id read_only_udm.metadata.product_event_type Se category_id for 1, read_only_udm.metadata.product_event_type é definido como Security. Se category_id for 5, read_only_udm.metadata.product_event_type é definido como Policy
collector_device_ip read_only_udm.principal.ip, read_only_udm.principal.asset.ip Valor do campo collector_device_ip
connection.bytes_download read_only_udm.network.received_bytes Valor do campo connection.bytes_download convertido em número inteiro
connection.bytes_upload read_only_udm.network.sent_bytes Valor do campo connection.bytes_upload convertido em número inteiro
connection.dst_ip read_only_udm.target.ip Valor do campo connection.dst_ip
connection.dst_location.country read_only_udm.target.location.country_or_region Valor do campo connection.dst_location.country
connection.dst_name read_only_udm.target.hostname Valor do campo connection.dst_name
connection.dst_port read_only_udm.target.port Valor do campo connection.dst_port convertido em número inteiro
connection.http_status read_only_udm.network.http.response_code Valor do campo connection.http_status convertido em número inteiro
connection.http_user_agent read_only_udm.network.http.user_agent Valor do campo connection.http_user_agent
connection.src_ip read_only_udm.principal.ip, read_only_udm.src.ip Valor do campo connection.src_ip. Se src_ip ou collector_device_ip não estiver vazio, é mapeado para read_only_udm.src.ip
connection.tls.version read_only_udm.network.tls.version_protocol Valor do campo connection.tls.version
connection.url.host read_only_udm.target.hostname Valor do campo connection.url.host
connection.url.method read_only_udm.network.http.method Valor do campo connection.url.method
connection.url.path read_only_udm.target.url Valor do campo connection.url.path
connection.url.text read_only_udm.target.url Valor do campo connection.url.text
cs_connection_negotiated_cipher read_only_udm.network.tls.cipher Valor do campo cs_connection_negotiated_cipher
cs_icap_status read_only_udm.security_result.description Valor do campo cs_icap_status
device_id read_only_udm.target.resource.id, read_only_udm.target.resource.product_object_id Valor do campo device_id
device_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo device_ip
device_time read_only_udm.metadata.collected_timestamp, read_only_udm.metadata.event_timestamp Valor do campo device_time convertido em string. Se when estiver vazio, é mapeado para read_only_udm.metadata.event_timestamp
hostname read_only_udm.principal.hostname, read_only_udm.principal.asset.hostname Valor do campo do nome do anfitrião
log_time read_only_udm.metadata.event_timestamp O valor do campo log_time foi convertido em indicação de tempo. Se when e device_time estiverem vazios, são mapeados para read_only_udm.metadata.event_timestamp
msg_desc read_only_udm.metadata.description Valor do campo msg_desc
os_details read_only_udm.target.asset.platform_software.platform, read_only_udm.target.asset.platform_software.platform_version Valor do campo os_details. Se os_details não estiver vazio, é analisado para extrair os_name e os_ver. Se os_name contiver Windows, read_only_udm.target.asset.platform_software.platform é definido como WINDOWS. os_ver está mapeado para read_only_udm.target.asset.platform_software.platform_version
product_data.cs(Referer) read_only_udm.network.http.referral_url Valor do campo product_data.cs(Referer)
product_data.r-supplier-country read_only_udm.principal.location.country_or_region Valor do campo product_data.r-supplier-country
product_data.s-supplier-ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo product_data.s-supplier-ip
product_data.x-bluecoat-application-name read_only_udm.target.application Valor do campo product_data.x-bluecoat-application-name
product_data.x-bluecoat-transaction-uuid read_only_udm.metadata.product_log_id Valor do campo product_data.x-bluecoat-transaction-uuid
product_data.x-client-agent-sw read_only_udm.observer.platform_version Valor do campo product_data.x-client-agent-sw
product_data.x-client-agent-type read_only_udm.observer.application Valor do campo product_data.x-client-agent-type
product_data.x-client-device-id read_only_udm.target.resource.type, read_only_udm.target.resource.id, read_only_udm.target.resource.product_object_id Se não estiver vazio, read_only_udm.target.resource.type é definido como DEVICE. O valor do campo product_data.x-client-device-id é mapeado para read_only_udm.target.resource.id e read_only_udm.target.resource.product_object_id
product_data.x-client-device-name read_only_udm.src.hostname, read_only_udm.src.asset.hostname Valor do campo product_data.x-client-device-name
product_data.x-cs-client-ip-country read_only_udm.target.location.country_or_region Valor do campo product_data.x-cs-client-ip-country
product_data.x-cs-connection-negotiated-cipher read_only_udm.network.tls.cipher Valor do campo product_data.x-cs-connection-negotiated-cipher
product_data.x-cs-connection-negotiated-ssl-version read_only_udm.network.tls.version_protocol Valor do campo product_data.x-cs-connection-negotiated-ssl-version
product_data.x-exception-id read_only_udm.security_result.summary Valor do campo product_data.x-exception-id
product_data.x-rs-certificate-hostname read_only_udm.network.tls.client.server_name Valor do campo product_data.x-rs-certificate-hostname
product_data.x-rs-certificate-hostname-categories read_only_udm.security_result.category_details Valor do campo product_data.x-rs-certificate-hostname-categories
product_data.x-rs-certificate-observed-errors read_only_udm.network.tls.server.certificate.issuer Valor do campo product_data.x-rs-certificate-observed-errors
product_data.x-rs-certificate-validate-status read_only_udm.network.tls.server.certificate.subject Valor do campo product_data.x-rs-certificate-validate-status
product_name read_only_udm.metadata.product_name Valor do campo product_name
product_ver read_only_udm.metadata.product_version Valor do campo product_ver
proxy_connection.src_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo proxy_connection.src_ip
received_bytes read_only_udm.network.received_bytes Valor do campo received_bytes convertido em número inteiro
ref_uid read_only_udm.metadata.product_log_id Valor do campo ref_uid
s_action read_only_udm.metadata.description Valor do campo s_action
sent_bytes read_only_udm.network.sent_bytes Valor do campo sent_bytes convertido em número inteiro
severity_id read_only_udm.security_result.severity Se severity_id for 1 ou 2, read_only_udm.security_result.severity é definido como LOW. Se severity_id for 3 ou 4, read_only_udm.security_result.severity é definido como MEDIUM. Se severity_id for 5 ou 6, read_only_udm.security_result.severity é definido como HIGH
supplier_country read_only_udm.principal.location.country_or_region Valor do campo supplier_country
target_ip read_only_udm.target.ip, read_only_udm.target.asset.ip Valor do campo target_ip
user.full_name read_only_udm.principal.user.user_display_name Valor do campo user.full_name
user.name read_only_udm.principal.user.user_display_name Valor do campo user.name
user_name read_only_udm.principal.user.user_display_name Valor do campo user_name
uuid read_only_udm.metadata.product_log_id Valor do campo uuid
quando read_only_udm.metadata.event_timestamp Valor de quando o campo foi convertido em data/hora
read_only_udm.metadata.event_type Definido como NETWORK_UNCATEGORIZED se o nome de anfitrião estiver vazio e connection.dst_ip não estiver vazio. Definido como SCAN_NETWORK se o nome de anfitrião não estiver vazio. Definido como NETWORK_CONNECTION se has_principal e has_target forem true. Definido como STATUS_UPDATE se has_principal for true e has_target for false. Definido como GENERIC_EVENT se has_principal e has_target forem false
read_only_udm.metadata.log_type Definir sempre como SYMANTEC_WSS
read_only_udm.metadata.vendor_name Definir sempre como SYMANTEC
read_only_udm.security_result.action Definido como ALLOW se product_data.sc-filter_result for OBSERVED ou PROXIED. Definido como BLOCK se product_data.sc-filter_result for DENIED
read_only_udm.security_result.action_details Valor do campo product_data.sc-filter_result
read_only_udm.target.resource.type Definido como DEVICE se product_data.x-client-device-id não estiver vazio

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