Coletar registros do Akamai Cloud Monitor
Este documento explica como ingerir registros do Akamai Cloud Monitor (balanceador de carga, modelador de tráfego, ADC) no Google Security Operations usando o AWS S3. A Akamai envia eventos JSON para seu endpoint HTTPS. Um receptor API Gateway + Lambda grava os eventos no S3 (JSONL, gz). O analisador transforma os registros JSON em UDM. Ele extrai campos do payload JSON, realiza conversões de tipo de dados, renomeia campos para corresponder ao esquema da UDM e processa lógica específica para campos personalizados e construção de URL. Ele também incorpora tratamento de erros e lógica condicional com base na presença de campos.
Antes de começar
Verifique se você tem os pré-requisitos a seguir:
- Instância do Google SecOps
- Acesso privilegiado ao Akamai Control Center e ao Property Manager.
- Acesso privilegiado à AWS (S3, IAM, Lambda, API Gateway)
Configurar o bucket do AWS S3 e o IAM para o Google SecOps
- Crie um bucket do Amazon S3 seguindo este guia do usuário: Como criar um bucket
- Salve o Nome e a Região do bucket para referência futura (por exemplo,
akamai-cloud-monitor
). - Crie um usuário seguindo este guia: Como criar um usuário do IAM.
- Selecione o usuário criado.
- Selecione a guia Credenciais de segurança.
- Clique em Criar chave de acesso na seção Chaves de acesso.
- Selecione Serviço de terceiros como o Caso de uso.
- Clique em Próxima.
- Opcional: adicione uma tag de descrição.
- Clique em Criar chave de acesso.
- Clique em Fazer o download do arquivo CSV para salvar a chave de acesso e a chave de acesso secreta para uso posterior.
- Clique em Concluído.
- Selecione a guia Permissões.
- Clique em Adicionar permissões na seção Políticas de permissões.
- Selecione Adicionar permissões.
- Selecione Anexar políticas diretamente.
- Pesquise e selecione a política AmazonS3FullAccess.
- Clique em Próxima.
- Clique em Adicionar permissões
Configurar a política e o papel do IAM para uploads do S3 (Lambda)
- No console da AWS, acesse IAM > Políticas > Criar política > JSON e cole a política abaixo.
Política JSON (substitua
akamai-cloud-monitor
pelo nome do seu bucket do S3):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutAkamaiObjects", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::akamai-cloud-monitor/*" } ] }
Clique em Próxima > Criar política.
Acesse IAM > Funções > Criar função > Serviço da AWS > Lambda.
Anexe a política JSON.
Nomeie a função como
WriteAkamaiCMToS3Role
e clique em Criar função.
Criar a função Lambda
Configuração | Valor |
---|---|
Nome | akamai_cloud_monitor_to_s3 |
Ambiente de execução | Python 3.13 |
Arquitetura | x86_64 |
Função de execução | WriteAkamaiCMToS3Role |
Depois que a função for criada, abra a guia Código, exclua o stub e insira o seguinte código (
akamai_cloud_monitor_to_s3.py
):#!/usr/bin/env python3 # Lambda: Receive Akamai Cloud Monitor POST, write JSONL (gz) to S3 import os, json, gzip, io, uuid, base64, datetime as dt import boto3 S3_BUCKET = os.environ["S3_BUCKET_NAME"] S3_PREFIX = os.environ.get("S3_PREFIX", "akamai/cloud-monitor/json/").strip("/") + "/" INGEST_TOKEN = os.environ.get("INGEST_TOKEN") # optional shared secret in URL query (?token=...) s3 = boto3.client("s3") def _write_jsonl_gz(objs: list) -> str: key = f"{dt.datetime.utcnow():%Y/%m/%d}/akamai-cloud-monitor-{uuid.uuid4()}.json.gz" buf = io.BytesIO() with gzip.GzipFile(fileobj=buf, mode="w") as gz: for o in objs: gz.write((json.dumps(o, separators=(",", ":")) + "n").encode()) buf.seek(0) s3.upload_fileobj( buf, S3_BUCKET, f"{S3_PREFIX}{key}", ExtraArgs={ "ContentType": "application/json", "ContentEncoding": "gzip", }, ) return f"s3://{S3_BUCKET}/{S3_PREFIX}{key}" def _parse_records_from_event(event) -> list: # HTTP API (Lambda proxy) event: body is a JSON string body = event.get("body") or "" if event.get("isBase64Encoded"): body = base64.b64decode(body).decode("utf-8", "replace") try: data = json.loads(body) except Exception: # accept line-delimited JSON as pass-through try: return [json.loads(line) for line in body.splitlines() if line.strip()] except Exception: return [] if isinstance(data, list): return data if isinstance(data, dict): return [data] return [] def lambda_handler(event, context=None): # Optional shared-secret verification via query parameter (?token=...) if INGEST_TOKEN: qs = event.get("queryStringParameters") or {} token = qs.get("token") if token != INGEST_TOKEN: return {"statusCode": 403, "body": "forbidden"} records = _parse_records_from_event(event) if not records: return {"statusCode": 204, "body": "no content"} key = _write_jsonl_gz(records) return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"ok": True, "s3_key": key, "count": len(records)}), }
Acesse Configuração > Variáveis de ambiente > Editar.
Clique em Adicionar nova variável de ambiente e defina os seguintes valores:
Variáveis de ambiente
Chave Exemplo S3_BUCKET_NAME
akamai-cloud-monitor
S3_PREFIX
akamai/cloud-monitor/json/
INGEST_TOKEN
random-shared-secret
Acesse Configuração > Configuração geral.
Clique em Editar e defina Tempo limite como 5 minutos (300 segundos).
Clique em Salvar.
Criar o Amazon API Gateway (endpoint HTTPS para Akamai)
- No console da AWS, acesse API Gateway > Criar API.
- Selecione API HTTP > Build.
- Informe os seguintes detalhes de configuração:
- Integrações: escolha Lambda e selecione
akamai_cloud_monitor_to_s3
. - Rotas: adicione ANY
/{proxy+}
ou crie uma rota específica (por exemplo, POST/akamai/cloud-monitor
). - Etapas: crie ou use $default.
- Integrações: escolha Lambda e selecione
- Implante a API e copie o URL de invocação (por exemplo,
https://abc123.execute-api.<region>.amazonaws.com
).
Configurar o Akamai Cloud Monitor para enviar registros
- No Akamai Control Center, abra sua Propriedade no Property Manager.
- Clique em Adicionar regra > escolha Gerenciamento na nuvem.
- Adicione Instrumentação do Cloud Monitoring e selecione os Conjuntos de dados necessários.
- Adicione Entrega de dados do Cloud Monitoring.
- Nome do host de entrega: insira o URL de invocação do API Gateway (por exemplo,
abc123.execute-api.<region>.amazonaws.com
). - Caminho do URL de exibição: sua rota mais um token de consulta opcional, por exemplo:
/akamai/cloud-monitor?token=<INGEST_TOKEN>
.
- Nome do host de entrega: insira o URL de invocação do API Gateway (por exemplo,
- Salve e ative a versão da propriedade.
Configurar um feed no Google SecOps para ingerir o Akamai Cloud Monitor (JSON do S3)
- Acesse Configurações do SIEM > Feeds.
- Clique em Adicionar novo feed.
- No campo Nome do feed, insira um nome para o feed (por exemplo,
Akamai Cloud Monitor — S3
). - Selecione Amazon S3 V2 como o Tipo de origem.
- Selecione Akamai Cloud Monitor como o Tipo de registro.
- Clique em Próxima.
- Especifique valores para os seguintes parâmetros de entrada:
- URI do S3:
s3://akamai-cloud-monitor/akamai/cloud-monitor/json/
- Opções de exclusão de origem: se você quer excluir arquivos e/ou diretórios após a transferência.
- Idade máxima do arquivo: inclui arquivos modificados no último número de dias. O padrão é de 180 dias.
- ID da chave de acesso: uma chave de acesso alfanumérica de 20 caracteres (por exemplo, AKIAIOSFODNN7EXAMPLE).
- Chave de acesso secreta: uma chave de acesso secreta alfanumérica de 40 caracteres (por exemplo, wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY).
- Namespace do recurso:
akamai.cloud_monitor
- Rótulos de ingestão: são adicionados a todos os eventos desse feed (por exemplo,
source=akamai_cloud_monitor
,format=json
).
- URI do S3:
- Clique em Próxima.
- Revise a nova configuração do feed na tela Finalizar e clique em Enviar.
Tabela de mapeamento do UDM
Campo de registro | Mapeamento do UDM | Lógica |
---|---|---|
accLang |
network.http.user_agent |
Mapeado diretamente se não for "-" ou uma string vazia. |
city |
principal.location.city |
Mapeado diretamente se não for "-" ou uma string vazia. |
cliIP |
principal.ip |
Mapeado diretamente se não for uma string vazia. |
country |
principal.location.country_or_region |
Mapeado diretamente se não for "-" ou uma string vazia. |
cp |
additional.fields |
Mapeado como um par de chave-valor com a chave "cp". |
customField |
about.ip , about.labels , src.ip |
Analisados como pares de chave-valor. Tratamento especial para "eIp" e "pIp" para mapear para src.ip e about.ip , respectivamente. Outras chaves são mapeadas como rótulos em about . |
errorCode |
security_result.summary , security_result.severity |
Se presente, define security_result.severity como "ERROR" e mapeia o valor para security_result.summary . |
geo.city |
principal.location.city |
Mapeado diretamente se city for "-" ou uma string vazia. |
geo.country |
principal.location.country_or_region |
Mapeado diretamente se country for "-" ou uma string vazia. |
geo.lat |
principal.location.region_latitude |
Mapeado diretamente, convertido em ponto flutuante. |
geo.long |
principal.location.region_longitude |
Mapeado diretamente, convertido em ponto flutuante. |
geo.region |
principal.location.state |
Mapeado diretamente. |
id |
metadata.product_log_id |
Mapeado diretamente se não for uma string vazia. |
message.cliIP |
principal.ip |
Mapeado diretamente se cliIP for uma string vazia. |
message.fwdHost |
principal.hostname |
Mapeado diretamente. |
message.reqHost |
target.hostname , target.url |
Usado para criar target.url e extrair target.hostname . |
message.reqLen |
network.sent_bytes |
Mapeado diretamente, convertido em número inteiro sem sinal se totalBytes estiver vazio ou for "-". |
message.reqMethod |
network.http.method |
Mapeado diretamente se reqMethod for uma string vazia. |
message.reqPath |
target.url |
Adicionado a target.url . |
message.reqPort |
target.port |
Mapeado diretamente, convertido em número inteiro se reqPort for uma string vazia. |
message.respLen |
network.received_bytes |
Mapeado diretamente, convertido para número inteiro sem sinal. |
message.sslVer |
network.tls.version |
Mapeado diretamente. |
message.status |
network.http.response_code |
Mapeado diretamente, convertido em número inteiro se statusCode estiver vazio ou for "-". |
message.UA |
network.http.user_agent |
Mapeado diretamente se UA for "-" ou uma string vazia. |
network.asnum |
additional.fields |
Mapeado como um par de chave-valor com a chave "asnum". |
network.edgeIP |
intermediary.ip |
Mapeado diretamente. |
network.network |
additional.fields |
Mapeado como um par de chave-valor com a chave "network". |
network.networkType |
additional.fields |
Mapeado como um par de chave-valor com a chave "networkType". |
proto |
network.application_protocol |
Usado para determinar network.application_protocol . |
queryStr |
target.url |
Adicionado a target.url se não for "-" ou uma string vazia. |
referer |
network.http.referral_url , about.hostname |
Mapeado diretamente se não for "-". O nome do host extraído é mapeado para about.hostname . |
reqHost |
target.hostname , target.url |
Usado para criar target.url e extrair target.hostname . |
reqId |
metadata.product_log_id , network.session_id |
Mapeado diretamente se id for uma string vazia. Também mapeado para network.session_id . |
reqMethod |
network.http.method |
Mapeado diretamente se não for uma string vazia. |
reqPath |
target.url |
Adicionado a target.url se não for "-". |
reqPort |
target.port |
Mapeado diretamente, convertido em número inteiro. |
reqTimeSec |
metadata.event_timestamp , timestamp |
Usado para definir o carimbo de data/hora do evento. |
start |
metadata.event_timestamp , timestamp |
Usado para definir o carimbo de data/hora do evento se reqTimeSec for uma string vazia. |
statusCode |
network.http.response_code |
Mapeado diretamente, convertido em número inteiro se não for "-" ou uma string vazia. |
tlsVersion |
network.tls.version |
Mapeado diretamente. |
totalBytes |
network.sent_bytes |
Mapeado diretamente, convertido em número inteiro sem sinal se não estiver vazio ou "-". |
type |
metadata.product_event_type |
Mapeado diretamente. |
UA |
network.http.user_agent |
Mapeado diretamente se não for "-" ou uma string vazia. |
version |
metadata.product_version |
Mapeado diretamente. |
xForwardedFor |
principal.ip |
Mapeado diretamente se não for "-" ou uma string vazia. |
(Lógica do analisador) | metadata.vendor_name |
Defina como "Akamai". |
(Lógica do analisador) | metadata.product_name |
Defina como "DataStream". |
(Lógica do analisador) | metadata.event_type |
Defina como "NETWORK_HTTP". |
(Lógica do analisador) | metadata.product_version |
Defina como "2" se version for uma string vazia. |
(Lógica do analisador) | metadata.log_type |
Defina como "AKAMAI_CLOUD_MONITOR". |
(Lógica do analisador) | network.application_protocol |
Determinado por proto ou message.proto . Defina como "HTTPS" se um dos dois contiver "HTTPS" (sem diferenciação de maiúsculas e minúsculas) ou "HTTP" caso contrário. |
(Lógica do analisador) | security_result.severity |
Defina como "INFORMATIONAL" se errorCode for "-" ou uma string vazia. |
(Lógica do analisador) | target.url |
Construído com base em protocol , reqHost (ou message.reqHost ), reqPath (ou message.reqPath ) e queryStr . |
Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.