Collecter les journaux Akamai Cloud Monitor
Ce document explique comment ingérer les journaux Akamai Cloud Monitor (Load Balancer, Traffic Shaper, ADC) dans Google Security Operations à l'aide d'AWS S3. Akamai envoie des événements JSON à votre point de terminaison HTTPS. Un récepteur API Gateway + Lambda écrit les événements dans S3 (JSONL, gz). L'analyseur transforme les journaux JSON en UDM. Il extrait les champs de la charge utile JSON, effectue des conversions de type de données, renomme les champs pour qu'ils correspondent au schéma UDM et gère la logique spécifique pour les champs personnalisés et la construction d'URL. Il intègre également la gestion des exceptions et la logique conditionnelle en fonction de la présence des champs.
Avant de commencer
Assurez-vous de remplir les conditions suivantes :
- Instance Google SecOps
- Accès privilégié à Akamai Control Center et Property Manager
- Accès privilégié à AWS*(S3, IAM, Lambda, API Gateway)
Configurer un bucket AWS S3 et IAM pour Google SecOps
- Créez un bucket Amazon S3 en suivant ce guide de l'utilisateur : Créer un bucket.
- Enregistrez le nom et la région du bucket pour référence ultérieure (par exemple,
akamai-cloud-monitor
). - Créez un utilisateur en suivant ce guide : Créer un utilisateur IAM.
- Sélectionnez l'utilisateur créé.
- Sélectionnez l'onglet Informations d'identification de sécurité.
- Cliquez sur Créer une clé d'accès dans la section Clés d'accès.
- Sélectionnez Service tiers comme Cas d'utilisation.
- Cliquez sur Suivant.
- Facultatif : ajoutez un tag de description.
- Cliquez sur Créer une clé d'accès.
- Cliquez sur Télécharger le fichier CSV pour enregistrer la clé d'accès et la clé d'accès secrète pour une utilisation ultérieure.
- Cliquez sur OK.
- Sélectionnez l'onglet Autorisations.
- Cliquez sur Ajouter des autorisations dans la section Règles d'autorisation.
- Sélectionnez Ajouter des autorisations.
- Sélectionnez Joindre directement des règles.
- Recherchez et sélectionnez la règle AmazonS3FullAccess.
- Cliquez sur Suivant.
- Cliquez sur Ajouter des autorisations.
Configurer la stratégie et le rôle IAM pour les importations S3 (Lambda)
- Dans la console AWS, accédez à IAM > Stratégies > Créer une stratégie > JSON, puis collez la stratégie ci-dessous.
Règle JSON (remplacez
akamai-cloud-monitor
par le nom de votre bucket S3) :{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutAkamaiObjects", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::akamai-cloud-monitor/*" } ] }
Cliquez sur Suivant > Créer une règle.
Accédez à IAM > Rôles > Créer un rôle > Service AWS > Lambda.
Associez la stratégie JSON.
Nommez le rôle
WriteAkamaiCMToS3Role
, puis cliquez sur Créer un rôle.
Créer la fonction Lambda
Paramètre | Valeur |
---|---|
Nom | akamai_cloud_monitor_to_s3 |
Durée d'exécution | Python 3.13 |
Architecture | x86_64 |
Rôle d'exécution | WriteAkamaiCMToS3Role |
Une fois la fonction créée, ouvrez l'onglet Code, supprimez le stub et saisissez le code suivant (
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)}), }
Accédez à Configuration > Variables d'environnement > Modifier.
Cliquez sur Ajouter une variable d'environnement, puis définissez les valeurs suivantes :
Variables d'environnement
Clé Exemple S3_BUCKET_NAME
akamai-cloud-monitor
S3_PREFIX
akamai/cloud-monitor/json/
INGEST_TOKEN
random-shared-secret
Accédez à Configuration > Configuration générale.
Cliquez sur Modifier et définissez Délai avant expiration sur 5 minutes (300 secondes).
Cliquez sur Enregistrer.
Créer une passerelle Amazon API (point de terminaison HTTPS pour Akamai)
- Dans la console AWS, accédez à API Gateway > Créer une API.
- Sélectionnez API HTTP > Créer.
- Fournissez les informations de configuration suivantes :
- Integrations (Intégrations) : sélectionnez Lambda, puis
akamai_cloud_monitor_to_s3
. - Routes : ajoutez ANY
/{proxy+}
ou créez une route spécifique (par exemple, POST/akamai/cloud-monitor
). - Étapes : créez ou utilisez $default.
- Integrations (Intégrations) : sélectionnez Lambda, puis
- Déployez l'API et copiez l'URL d'appel (par exemple,
https://abc123.execute-api.<region>.amazonaws.com
).
Configurer Akamai Cloud Monitor pour envoyer des journaux
- Dans Akamai Control Center, ouvrez votre propriété dans Property Manager.
- Cliquez sur Ajouter une règle> choisissez Gestion du cloud.
- Ajoutez Instrumentation Cloud Monitoring et sélectionnez les ensembles de données requis.
- Ajoutez Cloud Monitor Data Delivery.
- Nom d'hôte de la diffusion : saisissez l'URL d'appel de la passerelle API (par exemple,
abc123.execute-api.<region>.amazonaws.com
). - Chemin d'URL de diffusion : votre route plus un jeton de requête facultatif, par exemple
/akamai/cloud-monitor?token=<INGEST_TOKEN>
.
- Nom d'hôte de la diffusion : saisissez l'URL d'appel de la passerelle API (par exemple,
- Enregistrez et activez la version de la propriété.
Configurer un flux dans Google SecOps pour ingérer Akamai Cloud Monitor (S3 JSON)
- Accédez à Paramètres SIEM> Flux.
- Cliquez sur Add New Feed (Ajouter un flux).
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple,
Akamai Cloud Monitor — S3
). - Sélectionnez Amazon S3 V2 comme type de source.
- Sélectionnez Akamai Cloud Monitor comme type de journal.
- Cliquez sur Suivant.
- Spécifiez les valeurs des paramètres d'entrée suivants :
- URI S3 :
s3://akamai-cloud-monitor/akamai/cloud-monitor/json/
- Options de suppression de la source : indiquez si vous souhaitez supprimer les fichiers et/ou les répertoires après le transfert.
- Âge maximal du fichier : inclut les fichiers modifiés au cours des derniers jours. La valeur par défaut est de 180 jours.
- ID de clé d'accès : clé d'accès au compte alphanumérique de 20 caractères (par exemple, AKIAIOSFODNN7EXAMPLE).
- Clé d'accès secrète : clé d'accès secrète alphanumérique de 40 caractères (par exemple, wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY).
- Espace de noms de l'asset :
akamai.cloud_monitor
- Étiquettes d'ingestion : des étiquettes sont ajoutées à tous les événements de ce flux (par exemple,
source=akamai_cloud_monitor
,format=json
).
- URI S3 :
- Cliquez sur Suivant.
- Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.
Table de mappage UDM
Champ de journal | Mappage UDM | Logique |
---|---|---|
accLang |
network.http.user_agent |
Mappé directement s'il n'est pas "-" ou une chaîne vide. |
city |
principal.location.city |
Mappé directement s'il n'est pas "-" ou une chaîne vide. |
cliIP |
principal.ip |
Mappé directement s'il ne s'agit pas d'une chaîne vide. |
country |
principal.location.country_or_region |
Mappé directement s'il n'est pas "-" ou une chaîne vide. |
cp |
additional.fields |
Mappé sous forme de paire clé/valeur avec la clé "cp". |
customField |
about.ip , about.labels , src.ip |
Analysé sous forme de paires clé-valeur. Gestion spéciale pour "eIp" et "pIp" à mapper respectivement sur src.ip et about.ip . Les autres clés sont mappées en tant que libellés dans about . |
errorCode |
security_result.summary , security_result.severity |
Si ce paramètre est présent, il définit security_result.severity sur "ERROR" et mappe la valeur sur security_result.summary . |
geo.city |
principal.location.city |
Mappé directement si city est "-" ou une chaîne vide. |
geo.country |
principal.location.country_or_region |
Mappé directement si country est "-" ou une chaîne vide. |
geo.lat |
principal.location.region_latitude |
Mappé directement, converti en float. |
geo.long |
principal.location.region_longitude |
Mappé directement, converti en float. |
geo.region |
principal.location.state |
Mappé directement. |
id |
metadata.product_log_id |
Mappé directement s'il ne s'agit pas d'une chaîne vide. |
message.cliIP |
principal.ip |
Mappé directement si cliIP est une chaîne vide. |
message.fwdHost |
principal.hostname |
Mappé directement. |
message.reqHost |
target.hostname , target.url |
Permet de construire target.url et d'extraire target.hostname . |
message.reqLen |
network.sent_bytes |
Mappé directement, converti en entier non signé si totalBytes est vide ou "-". |
message.reqMethod |
network.http.method |
Mappé directement si reqMethod est une chaîne vide. |
message.reqPath |
target.url |
Ajouté à target.url . |
message.reqPort |
target.port |
Mappé directement, converti en entier si reqPort est une chaîne vide. |
message.respLen |
network.received_bytes |
Mappé directement, converti en entier non signé. |
message.sslVer |
network.tls.version |
Mappé directement. |
message.status |
network.http.response_code |
Mappé directement, converti en entier si statusCode est vide ou "-". |
message.UA |
network.http.user_agent |
Mappé directement si UA est "-" ou une chaîne vide. |
network.asnum |
additional.fields |
Mappé sous forme de paire clé-valeur avec la clé "asnum". |
network.edgeIP |
intermediary.ip |
Mappé directement. |
network.network |
additional.fields |
Mappé sous forme de paire clé-valeur avec la clé "network". |
network.networkType |
additional.fields |
Mappé sous forme de paire clé-valeur avec la clé "networkType". |
proto |
network.application_protocol |
Utilisé pour déterminer network.application_protocol . |
queryStr |
target.url |
Ajouté à target.url si la valeur n'est pas "-" ou une chaîne vide. |
referer |
network.http.referral_url , about.hostname |
Mappé directement si la valeur n'est pas "-". Le nom d'hôte extrait est mappé sur about.hostname . |
reqHost |
target.hostname , target.url |
Permet de construire target.url et d'extraire target.hostname . |
reqId |
metadata.product_log_id , network.session_id |
Mappé directement si id est une chaîne vide. Également mappé sur network.session_id . |
reqMethod |
network.http.method |
Mappé directement s'il ne s'agit pas d'une chaîne vide. |
reqPath |
target.url |
Ajouté à target.url si la valeur n'est pas "-". |
reqPort |
target.port |
Mappé directement, converti en entier. |
reqTimeSec |
metadata.event_timestamp , timestamp |
Permet de définir le code temporel de l'événement. |
start |
metadata.event_timestamp , timestamp |
Permet de définir l'horodatage de l'événement si reqTimeSec est une chaîne vide. |
statusCode |
network.http.response_code |
Mappé directement, converti en entier si la valeur n'est pas "-" ou une chaîne vide. |
tlsVersion |
network.tls.version |
Mappé directement. |
totalBytes |
network.sent_bytes |
Mappé directement, converti en entier non signé s'il n'est pas vide ou "-". |
type |
metadata.product_event_type |
Mappé directement. |
UA |
network.http.user_agent |
Mappé directement s'il n'est pas "-" ou une chaîne vide. |
version |
metadata.product_version |
Mappé directement. |
xForwardedFor |
principal.ip |
Mappé directement s'il n'est pas "-" ou une chaîne vide. |
(Logique de l'analyseur) | metadata.vendor_name |
Défini sur "Akamai". |
(Logique de l'analyseur) | metadata.product_name |
Définissez-le sur "DataStream". |
(Logique de l'analyseur) | metadata.event_type |
Définissez-le sur "NETWORK_HTTP". |
(Logique de l'analyseur) | metadata.product_version |
Définissez sur "2" si version est une chaîne vide. |
(Logique de l'analyseur) | metadata.log_type |
Défini sur "AKAMAI_CLOUD_MONITOR". |
(Logique de l'analyseur) | network.application_protocol |
Déterminé à partir de proto ou message.proto . Définissez sur "HTTPS" si l'une des valeurs contient "HTTPS" (sans tenir compte de la casse), ou sur "HTTP" dans le cas contraire. |
(Logique de l'analyseur) | security_result.severity |
Définissez sur "INFORMATIONAL" si errorCode est "-" ou une chaîne vide. |
(Logique de l'analyseur) | target.url |
Construit à partir de protocol , reqHost (ou message.reqHost ), reqPath (ou message.reqPath ) et queryStr . |
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.