Collecter les journaux Symantec WSS

Compatible avec :

Ce document explique comment ingérer des journaux Symantec Web Security Service (WSS) dans Google Security Operations à l'aide d'Amazon S3. L'analyseur tente d'abord d'analyser le message de journal en tant que JSON. Si cela échoue, il utilise une série de modèles grok de plus en plus spécifiques pour extraire les champs du texte brut, en mappant finalement les données extraites au modèle de données unifié (UDM).

Avant de commencer

Assurez-vous de remplir les conditions suivantes :

  • Une instance Google SecOps.
  • Accès privilégié à Symantec Web Security Service.
  • Accès privilégié à AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).

Collecter les conditions préalables de Symantec WSS (ID, clés API, ID d'organisation, jetons)

  1. Connectez-vous au portail Symantec Web Security Service en tant qu'administrateur.
  2. Accédez à Compte > Identifiants de l'API.
  3. Cliquez sur Ajouter.
  4. Fournissez les informations de configuration suivantes :
    • Nom de l'API : saisissez un nom descriptif (par exemple, Google SecOps Integration).
    • Description : saisissez une description pour les identifiants de l'API.
  5. Cliquez sur Enregistrer et copiez les identifiants API générés de manière sécurisée.
  6. Notez l'URL du portail WSS et le point de terminaison de l'API de synchronisation.
  7. Copiez et enregistrez les informations suivantes dans un emplacement sécurisé :
    • WSS_API_USERNAME.
    • WSS_API_PASSWORD.
    • WSS_SYNC_URL.

Configurer un bucket AWS S3 et IAM pour Google SecOps

  1. Créez un bucket Amazon S3 en suivant ce guide de l'utilisateur : Créer un bucket.
  2. Enregistrez le Nom et la Région du bucket pour référence ultérieure (par exemple, symantec-wss-logs).
  3. Créez un utilisateur en suivant ce guide de l'utilisateur : Créer un utilisateur IAM.
  4. Sélectionnez l'utilisateur créé.
  5. Sélectionnez l'onglet Informations d'identification de sécurité.
  6. Cliquez sur Créer une clé d'accès dans la section Clés d'accès.
  7. Sélectionnez Service tiers comme Cas d'utilisation.
  8. Cliquez sur Suivant.
  9. Facultatif : Ajoutez un tag de description.
  10. Cliquez sur Créer une clé d'accès.
  11. Cliquez sur Télécharger le fichier CSV pour enregistrer la clé d'accès et la clé d'accès secrète pour référence ultérieure.
  12. Cliquez sur OK.
  13. Sélectionnez l'onglet Autorisations.
  14. Cliquez sur Ajouter des autorisations dans la section Règles relatives aux autorisations.
  15. Sélectionnez Ajouter des autorisations.
  16. Sélectionnez Joindre directement des règles.
  17. Recherchez la règle AmazonS3FullAccess.
  18. Sélectionnez la règle.
  19. Cliquez sur Suivant.
  20. Cliquez sur Ajouter des autorisations.

Configurer la stratégie et le rôle IAM pour les importations S3

  1. Dans la console AWS, accédez à IAM > Stratégies.
  2. Cliquez sur Créer une règle > onglet JSON.
  3. Copiez et collez le règlement suivant.
  4. JSON de la règle (remplacez symantec-wss-logs si vous avez saisi un autre nom de bucket) :

    {
      "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. Cliquez sur Suivant > Créer une règle.

  6. Accédez à IAM > Rôles > Créer un rôle > Service AWS > Lambda.

  7. Associez la règle que vous venez de créer.

  8. Nommez le rôle SymantecWssToS3Role, puis cliquez sur Créer un rôle.

Créer la fonction Lambda

  1. Dans la console AWS, accédez à Lambda > Fonctions > Créer une fonction.
  2. Cliquez sur Créer à partir de zéro.
  3. Fournissez les informations de configuration suivantes :

    Paramètre Valeur
    Nom symantec_wss_to_s3
    Durée d'exécution Python 3.13
    Architecture x86_64
    Rôle d'exécution SymantecWssToS3Role
  4. Une fois la fonction créée, ouvrez l'onglet Code, supprimez le stub et collez le code suivant (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. Accédez à Configuration > Variables d'environnement.

  6. Cliquez sur Modifier > Ajouter une variable d'environnement.

  7. Saisissez les variables d'environnement fournies dans le tableau suivant, en remplaçant les exemples de valeurs par les vôtres.

    Variables d'environnement

    Clé Exemple de valeur
    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 (à partir de l'étape 2)
    WSS_API_PASSWORD your-api-password (à partir de l'étape 2)
    WSS_TOKEN_PARAM none
  8. Une fois la fonction créée, restez sur sa page (ou ouvrez Lambda > Fonctions > votre-fonction).

  9. Accédez à l'onglet Configuration.

  10. Dans le panneau Configuration générale, cliquez sur Modifier.

  11. Définissez le délai avant expiration sur 5 minutes (300 secondes), puis cliquez sur Enregistrer.

Créer une programmation EventBridge

  1. Accédez à Amazon EventBridge> Scheduler> Create schedule.
  2. Fournissez les informations de configuration suivantes :
    • Planning récurrent : Tarif (1 hour).
    • Cible : votre fonction Lambda symantec_wss_to_s3.
    • Nom : symantec-wss-1h.
  3. Cliquez sur Créer la programmation.

(Facultatif) Créez un utilisateur et des clés IAM en lecture seule pour Google SecOps

  1. Dans la console AWS, accédez à IAM > Utilisateurs.
  2. Cliquez sur Add users (Ajouter des utilisateurs).
  3. Fournissez les informations de configuration suivantes :
    • Utilisateur : saisissez secops-reader.
    • Type d'accès : sélectionnez Clé d'accès – Accès programmatique.
  4. Cliquez sur Créer un utilisateur.
  5. Associez une stratégie de lecture minimale (personnalisée) : Utilisateurs > secops-reader > Autorisations > Ajouter des autorisations > Associer des stratégies directement > Créer une stratégie.
  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. Nom = secops-reader-policy.

  8. Cliquez sur Créer une règle> recherchez/sélectionnez > Suivant> Ajouter des autorisations.

  9. Créez une clé d'accès pour secops-reader : Identifiants de sécurité > Clés d'accès.

  10. Cliquez sur Créer une clé d'accès.

  11. Téléchargez le fichier CSV. (Vous collerez ces valeurs dans le flux.)

Configurer un flux dans Google SecOps pour ingérer les journaux Symantec WSS

  1. Accédez à Paramètres SIEM> Flux.
  2. Cliquez sur + Ajouter un flux.
  3. Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, Symantec WSS logs).
  4. Sélectionnez Amazon S3 V2 comme type de source.
  5. Sélectionnez Symantec WSS comme Type de journal.
  6. Cliquez sur Suivant.
  7. Spécifiez les valeurs des paramètres d'entrée suivants :
    • URI S3 : s3://symantec-wss-logs/symantec/wss/
    • Options de suppression de la source : sélectionnez l'option de suppression de votre choix.
    • Âge maximal des fichiers : incluez 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 utilisateur ayant accès au bucket S3.
    • Clé d'accès secrète : clé secrète de l'utilisateur ayant accès au bucket S3.
    • Espace de noms de l'élément : espace de noms de l'élément.
    • Libellés d'ingestion : libellé appliqué aux événements de ce flux.
  8. Cliquez sur Suivant.
  9. Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.

Table de mappage UDM

Champ du journal Mappage UDM Logique
category_id read_only_udm.metadata.product_event_type Si category_id est défini sur 1, read_only_udm.metadata.product_event_type est défini sur Security. Si category_id est défini sur 5, read_only_udm.metadata.product_event_type est défini sur Policy.
collector_device_ip read_only_udm.principal.ip, read_only_udm.principal.asset.ip Valeur du champ "collector_device_ip"
connection.bytes_download read_only_udm.network.received_bytes Valeur du champ "connection.bytes_download" convertie en entier
connection.bytes_upload read_only_udm.network.sent_bytes Valeur du champ connection.bytes_upload convertie en entier
connection.dst_ip read_only_udm.target.ip Valeur du champ "connection.dst_ip"
connection.dst_location.country read_only_udm.target.location.country_or_region Valeur du champ connection.dst_location.country
connection.dst_name read_only_udm.target.hostname Valeur du champ "connection.dst_name"
connection.dst_port read_only_udm.target.port Valeur du champ connection.dst_port convertie en entier
connection.http_status read_only_udm.network.http.response_code Valeur du champ connection.http_status convertie en entier
connection.http_user_agent read_only_udm.network.http.user_agent Valeur du champ connection.http_user_agent
connection.src_ip read_only_udm.principal.ip, read_only_udm.src.ip Valeur du champ "connection.src_ip". Si src_ip ou collector_device_ip n'est pas vide, il est mappé sur read_only_udm.src.ip.
connection.tls.version read_only_udm.network.tls.version_protocol Valeur du champ "connection.tls.version"
connection.url.host read_only_udm.target.hostname Valeur du champ connection.url.host
connection.url.method read_only_udm.network.http.method Valeur du champ connection.url.method
connection.url.path read_only_udm.target.url Valeur du champ connection.url.path
connection.url.text read_only_udm.target.url Valeur du champ "connection.url.text"
cs_connection_negotiated_cipher read_only_udm.network.tls.cipher Valeur du champ cs_connection_negotiated_cipher
cs_icap_status read_only_udm.security_result.description Valeur du champ cs_icap_status
device_id read_only_udm.target.resource.id, read_only_udm.target.resource.product_object_id Valeur du champ device_id
device_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valeur du champ "device_ip"
device_time read_only_udm.metadata.collected_timestamp, read_only_udm.metadata.event_timestamp Valeur du champ device_time convertie en chaîne. Si "when" est vide, il est mappé sur read_only_udm.metadata.event_timestamp.
nom d'hôte read_only_udm.principal.hostname, read_only_udm.principal.asset.hostname Valeur du champ "Nom d'hôte"
log_time read_only_udm.metadata.event_timestamp La valeur du champ "log_time" a été convertie en code temporel. Si les champs "when" et "device_time" sont vides, ils sont mappés sur read_only_udm.metadata.event_timestamp.
msg_desc read_only_udm.metadata.description Valeur du champ msg_desc
os_details read_only_udm.target.asset.platform_software.platform, read_only_udm.target.asset.platform_software.platform_version Valeur du champ "os_details". Si os_details n'est pas vide, il est analysé pour extraire os_name et os_ver. Si os_name contient Windows, read_only_udm.target.asset.platform_software.platform est défini sur WINDOWS. os_ver est mappé sur read_only_udm.target.asset.platform_software.platform_version
product_data.cs(Referer) read_only_udm.network.http.referral_url Valeur du champ product_data.cs(Referer)
product_data.r-supplier-country read_only_udm.principal.location.country_or_region Valeur du champ product_data.r-supplier-country
product_data.s-supplier-ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valeur du champ product_data.s-supplier-ip
product_data.x-bluecoat-application-name read_only_udm.target.application Valeur du champ product_data.x-bluecoat-application-name
product_data.x-bluecoat-transaction-uuid read_only_udm.metadata.product_log_id Valeur du champ product_data.x-bluecoat-transaction-uuid
product_data.x-client-agent-sw read_only_udm.observer.platform_version Valeur du champ product_data.x-client-agent-sw
product_data.x-client-agent-type read_only_udm.observer.application Valeur du champ 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 Si elle n'est pas vide, read_only_udm.target.resource.type est défini sur DEVICE. La valeur du champ product_data.x-client-device-id est mappée sur read_only_udm.target.resource.id et 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 Valeur du champ product_data.x-client-device-name
product_data.x-cs-client-ip-country read_only_udm.target.location.country_or_region Valeur du champ product_data.x-cs-client-ip-country
product_data.x-cs-connection-negotiated-cipher read_only_udm.network.tls.cipher Valeur du champ product_data.x-cs-connection-negotiated-cipher
product_data.x-cs-connection-negotiated-ssl-version read_only_udm.network.tls.version_protocol Valeur du champ product_data.x-cs-connection-negotiated-ssl-version
product_data.x-exception-id read_only_udm.security_result.summary Valeur du champ product_data.x-exception-id
product_data.x-rs-certificate-hostname read_only_udm.network.tls.client.server_name Valeur du champ product_data.x-rs-certificate-hostname
product_data.x-rs-certificate-hostname-categories read_only_udm.security_result.category_details Valeur du champ product_data.x-rs-certificate-hostname-categories
product_data.x-rs-certificate-observed-errors read_only_udm.network.tls.server.certificate.issuer Valeur du champ product_data.x-rs-certificate-observed-errors
product_data.x-rs-certificate-validate-status read_only_udm.network.tls.server.certificate.subject Valeur du champ product_data.x-rs-certificate-validate-status
product_name read_only_udm.metadata.product_name Valeur du champ product_name
product_ver read_only_udm.metadata.product_version Valeur du champ product_ver
proxy_connection.src_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valeur du champ "proxy_connection.src_ip"
received_bytes read_only_udm.network.received_bytes Valeur du champ "received_bytes" convertie en entier
ref_uid read_only_udm.metadata.product_log_id Valeur du champ ref_uid
s_action read_only_udm.metadata.description Valeur du champ s_action
sent_bytes read_only_udm.network.sent_bytes Valeur du champ "sent_bytes" convertie en entier
severity_id read_only_udm.security_result.severity Si severity_id est défini sur 1 ou 2, read_only_udm.security_result.severity est défini sur LOW. Si severity_id est défini sur 3 ou 4, read_only_udm.security_result.severity est défini sur MEDIUM. Si severity_id est défini sur 5 ou 6, read_only_udm.security_result.severity est défini sur HIGH.
supplier_country read_only_udm.principal.location.country_or_region Valeur du champ "supplier_country"
target_ip read_only_udm.target.ip, read_only_udm.target.asset.ip Valeur du champ "target_ip"
user.full_name read_only_udm.principal.user.user_display_name Valeur du champ user.full_name
nom.utilisateur read_only_udm.principal.user.user_display_name Valeur du champ "user.name"
user_name read_only_udm.principal.user.user_display_name Valeur du champ user_name
uuid read_only_udm.metadata.product_log_id Valeur du champ "uuid"
date read_only_udm.metadata.event_timestamp Valeur du champ "when" convertie en code temporel
read_only_udm.metadata.event_type Défini sur NETWORK_UNCATEGORIZED si le nom d'hôte est vide et que connection.dst_ip n'est pas vide. Défini sur SCAN_NETWORK si le nom d'hôte n'est pas vide. Définie sur NETWORK_CONNECTION si has_principal et has_target sont true. Définie sur STATUS_UPDATE si has_principal est true et has_target est false. Définie sur GENERIC_EVENT si has_principal et has_target sont false
read_only_udm.metadata.log_type Toujours défini sur SYMANTEC_WSS
read_only_udm.metadata.vendor_name Toujours défini sur SYMANTEC
read_only_udm.security_result.action Définissez sur ALLOW si product_data.sc-filter_result est OBSERVED ou PROXIED. Définie sur BLOCK si product_data.sc-filter_result est DENIED
read_only_udm.security_result.action_details Valeur du champ product_data.sc-filter_result
read_only_udm.target.resource.type Définissez sur DEVICE si product_data.x-client-device-id n'est pas vide.

Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.