Recolha registos do Symantec WSS
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)
- Inicie sessão no portal do serviço de segurança Web da Symantec como administrador.
- Aceda a Conta > Credenciais da API.
- Clique em Adicionar.
- 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.
- Nome da API: introduza um nome descritivo (por exemplo,
- Clique em Guardar e copie as credenciais da API geradas de forma segura.
- Registe o URL do portal do WSS e o ponto final da API de sincronização.
- 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
- Crie um contentor do Amazon S3 seguindo este guia do utilizador: Criar um contentor
- Guarde o nome e a região do contentor para referência futura (por exemplo,
symantec-wss-logs
). - Crie um utilizador seguindo este guia do utilizador: criar um utilizador do IAM.
- Selecione o utilizador criado.
- Selecione o separador Credenciais de segurança.
- Clique em Criar chave de acesso na secção Chaves de acesso.
- Selecione Serviço de terceiros como Exemplo de utilização.
- Clicar em Seguinte.
- Opcional: adicione uma etiqueta de descrição.
- Clique em Criar chave de acesso.
- Clique em Transferir ficheiro CSV para guardar a chave de acesso e a chave de acesso secreta para referência futura.
- Clique em Concluído.
- Selecione o separador Autorizações.
- Clique em Adicionar autorizações na secção Políticas de autorizações.
- Selecione Adicionar autorizações.
- Selecione Anexar políticas diretamente.
- Pesquise a política AmazonS3FullAccess.
- Selecione a política.
- Clicar em Seguinte.
- Clique em Adicionar autorizações.
Configure a política e a função de IAM para carregamentos do S3
- Na consola da AWS, aceda a IAM > Políticas.
- Clique em Criar política > separador JSON.
- Copie e cole a seguinte política.
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" } ] }
Clique em Seguinte > Criar política.
Aceda a IAM > Funções > Criar função > Serviço AWS > Lambda.
Anexe a política criada recentemente.
Dê o nome
SymantecWssToS3Role
à função e clique em Criar função.
Crie a função Lambda
- Na consola da AWS, aceda a Lambda > Functions > Create function.
- Clique em Criar do zero.
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
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())
Aceda a Configuração > Variáveis de ambiente.
Clique em Editar > Adicionar nova variável de ambiente.
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
Depois de criar a função, permaneça na respetiva página (ou abra Lambda > Functions > a sua função).
Selecione o separador Configuração.
No painel Configuração geral, clique em Editar.
Altere Tempo limite para 5 minutos (300 segundos) e clique em Guardar.
Crie um horário do EventBridge
- Aceda a Amazon EventBridge > Scheduler > Create schedule.
- 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
.
- Agenda recorrente: Taxa (
- Clique em Criar programação.
(Opcional) Crie um utilizador e chaves da IAM só de leitura para o Google SecOps
- Na consola da AWS, aceda a IAM > Utilizadores.
- Clique em Adicionar utilizadores.
- Indique os seguintes detalhes de configuração:
- Utilizador: introduza
secops-reader
. - Tipo de acesso: selecione Chave de acesso – Acesso programático.
- Utilizador: introduza
- Clique em Criar utilizador.
- Anexe a política de leitura mínima (personalizada): Users > secops-reader > Permissions > Add permissions > Attach policies directly > Create policy.
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" } ] }
Nome =
secops-reader-policy
.Clique em Criar política > procure/selecione > Seguinte > Adicionar autorizações.
Crie uma chave de acesso para
secops-reader
: Credenciais de segurança > Chaves de acesso.Clique em Criar chave de acesso.
Transfira o CSV. (Vai colar estes valores no feed).
Configure um feed no Google SecOps para carregar registos do Symantec WSS
- Aceda a Definições do SIEM > Feeds.
- Clique em + Adicionar novo feed.
- No campo Nome do feed, introduza um nome para o feed (por exemplo,
Symantec WSS logs
). - Selecione Amazon S3 V2 como o Tipo de origem.
- Selecione Symantec WSS como o Tipo de registo.
- Clicar em Seguinte.
- 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.
- URI do S3:
- Clicar em Seguinte.
- 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.