Collecter les journaux Google App Engine

Compatible avec :

Ce document explique comment ingérer des journaux Google App Engine dans Google Security Operations à l'aide de Google Cloud Storage V2.

Google App Engine est une plate-forme sans serveur entièrement gérée qui permet de créer et de déployer des applications Web et des API. App Engine génère automatiquement des journaux de requêtes pour les requêtes HTTP et des journaux d'application à partir de votre code. Ces journaux sont envoyés à Cloud Logging et peuvent être exportés vers Cloud Storage pour être ingérés dans Google Security Operations.

Avant de commencer

Assurez-vous de remplir les conditions préalables suivantes :

  • Une instance Google SecOps
  • Un projet GCP avec l'API Cloud Storage activée
  • Autorisations pour créer et gérer des buckets GCS
  • Autorisations permettant de gérer les stratégies IAM sur les buckets GCS
  • Autorisations permettant de créer des récepteurs Cloud Logging (roles/logging.configWriter)
  • Une application App Engine active (environnement standard ou flexible)

Créer un bucket Google Cloud Storage

  1. Accédez à Google Cloud Console.
  2. Sélectionnez votre projet ou créez-en un.
  3. Dans le menu de navigation, accédez à Cloud Storage> Buckets.
  4. Cliquez sur Créer un bucket.
  5. Fournissez les informations de configuration suivantes :

    Paramètre Valeur
    Nommer votre bucket Saisissez un nom unique (par exemple, appengine-logs-export).
    Type d'emplacement Choisissez en fonction de vos besoins (région, birégion ou multirégion).
    Emplacement Sélectionnez l'emplacement (par exemple, us-central1).
    Classe de stockage Standard (recommandé pour les journaux auxquels vous accédez fréquemment)
    Access control (Contrôle des accès) Uniforme (recommandé)
    Outils de protection Facultatif : Activer la gestion des versions des objets ou la règle de conservation
  6. Cliquez sur Créer.

Configurer Cloud Logging pour exporter les journaux App Engine vers GCS

Cloud Logging utilise des récepteurs de journaux pour acheminer les entrées de journal vers des destinations compatibles, y compris les buckets Cloud Storage. L'identité du rédacteur du récepteur nécessite le rôle Créateur des objets de l'espace de stockage (roles/storage.objectCreator) sur le bucket de destination.

Créer un récepteur Cloud Logging

  1. Dans la console Google Cloud, accédez à Journalisation > Routeur de journaux.
  2. Cliquez sur Créer un récepteur.
  3. Fournissez les informations de configuration suivantes :
    • Nom du récepteur : saisissez un nom descriptif (par exemple, appengine-to-gcs).
    • Description du récepteur : description facultative.
  4. Cliquez sur Suivant.
  5. Dans la section Sélectionner le service de récepteur :
    • Service de récepteur : sélectionnez Bucket Cloud Storage.
    • Sélectionner un bucket Cloud Storage : sélectionnez appengine-logs-export dans le menu déroulant.
  6. Cliquez sur Suivant.
  7. Dans la section Sélectionner les journaux à inclure dans le récepteur, saisissez une requête de filtre pour sélectionner les journaux App Engine. Le type de ressource doit correspondre exactement à "gae_app".

    Pour tous les journaux App Engine (journaux de requêtes et d'application) :

    resource.type="gae_app"
    

    Pour les journaux de requête App Engine uniquement :

    resource.type="gae_app"
    logName="projects/PROJECT_ID/logs/appengine.googleapis.com/request_log"
    

    Pour les journaux d'application App Engine (stdout/stderr) :

    resource.type="gae_app"
    (logName="projects/PROJECT_ID/logs/stdout" OR logName="projects/PROJECT_ID/logs/stderr")
    

    Remplacez PROJECT_ID par l'ID de votre projet GCP.

  8. Cliquez sur Suivant.

  9. Vérifiez la configuration, puis cliquez sur Créer un récepteur.

Accorder des autorisations à l'identité du rédacteur du récepteur

Après avoir créé le récepteur, vous devez attribuer le rôle "Créateur des objets de l'espace de stockage" à l'identité du rédacteur du récepteur dans le bucket de destination. L'identité du rédacteur pour le compte de service ressemble à ceci : serviceAccount:service-123456789012@gcp-sa-logging.iam.gserviceaccount.com

  1. Sur la page Routeur de journaux, localisez le récepteur que vous venez de créer.
  2. Cliquez sur l'icône de menu (trois points verticaux) à côté du nom du connecteur.
  3. Sélectionnez Afficher les informations sur le récepteur.
  4. Copiez l'identité du rédacteur (adresse e-mail du compte de service).
  5. Accédez à Cloud Storage > Buckets.
  6. Cliquez sur le nom du bucket (appengine-logs-export).
  7. Accédez à l'onglet Autorisations.
  8. Cliquez sur Accorder l'accès.
  9. Fournissez les informations de configuration suivantes :
    • Ajouter des comptes principaux : collez l'identité du rédacteur du récepteur (adresse e-mail du compte de service).
    • Attribuer des rôles : sélectionnez Créateur d'objets Storage.
  10. Cliquez sur Enregistrer.

Récupérer le compte de service Google SecOps

Google SecOps utilise un compte de service unique pour lire les données de votre bucket GCS. Vous devez accorder à ce compte de service l'accès à votre bucket.

Configurer un flux dans Google SecOps pour ingérer les journaux App Engine

  1. Accédez à Paramètres SIEM> Flux.
  2. Cliquez sur Add New Feed (Ajouter un flux).
  3. Cliquez sur Configurer un flux unique.
  4. Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, App Engine Logs).
  5. Sélectionnez Google Cloud Storage V2 comme Type de source.
  6. Sélectionnez GCP_APP_ENGINE comme type de journal.

  7. Cliquez sur Obtenir un compte de service.

  8. Une adresse e-mail unique pour le compte de service s'affiche, par exemple :

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  9. Copiez cette adresse e-mail. Vous en aurez besoin lors de la tâche suivante.

  10. Cliquez sur Suivant.

  11. Spécifiez les valeurs des paramètres d'entrée suivants :

    • URL du bucket Storage : saisissez l'URI du bucket GCS avec le préfixe du chemin d'accès :

      gs://appengine-logs-export/
      

      Cloud Logging organise les fichiers journaux exportés dans des hiérarchies de répertoires classées par type de journal et par date. Le type de journal peut être un nom composé, tel que appengine.googleapis.com/request_log. Les fichiers sont partitionnés et nommés avec des périodes (par exemple, 08:00:00_08:59:59_S0.json).

    • Option de suppression de la source : sélectionnez l'option de suppression de votre choix :

      • Jamais : ne supprime jamais aucun fichier après les transferts (recommandé pour les tests).
      • Supprimer les fichiers transférés : supprime les fichiers après un transfert réussi.
      • Supprimer les fichiers transférés et les répertoires vides : supprime les fichiers et les répertoires vides après un transfert réussi.
    • Âge maximal des fichiers : incluez les fichiers modifiés au cours des derniers jours. La valeur par défaut est de 180 jours.

    • Espace de noms de l'élément : espace de noms de l'élément.

    • Libellés d'ingestion : libellé à appliquer aux événements de ce flux.

  12. Cliquez sur Suivant.

  13. Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.

Accorder des autorisations IAM au compte de service Google SecOps

Le compte de service Google SecOps a besoin du rôle Lecteur des objets Storage sur votre bucket GCS.

  1. Accédez à Cloud Storage > Buckets.
  2. Cliquez sur le nom du bucket (appengine-logs-export).
  3. Accédez à l'onglet Autorisations.
  4. Cliquez sur Accorder l'accès.
  5. Fournissez les informations de configuration suivantes :
    • Ajouter des comptes principaux : collez l'adresse e-mail du compte de service Google SecOps.
    • Attribuez des rôles : sélectionnez Lecteur des objets de l'espace de stockage.
  6. Cliquez sur Enregistrer.

Comprendre la structure des journaux App Engine

App Engine envoie automatiquement les journaux de requête et les journaux d'application à Cloud Logging. App Engine émet automatiquement des journaux pour les requêtes envoyées à votre application. Il n'est donc pas nécessaire d'écrire des journaux de requêtes. Cette section explique comment écrire des journaux d'application.

Les journaux de requêtes App Engine contiennent des entrées de journal comportant des champs protoPayload qui incluent des objets de type RequestLog avec @type "type.googleapis.com/google.appengine.logging.v1.RequestLog". Le type de ressource est "gae_app".

Par défaut, la charge utile du journal est une chaîne de texte stockée dans le champ textPayload de l'entrée de journal. Les chaînes apparaissent sous forme de messages dans l'explorateur de journaux. Elles sont associées au service App Engine et à la version qui les a émises.

Pour écrire des journaux structurés, vous écrivez des journaux sous la forme d'une seule ligne de données JSON sérialisées. Lorsque vous fournissez un journal structuré sous forme de dictionnaire JSON, certains champs spéciaux sont supprimés de jsonPayload et écrits dans le champ correspondant de l'entrée LogEntry générée. Par exemple, si vos données JSON incluent une propriété "severity", celle-ci est supprimée de "jsonPayload" et apparaît en tant que propriété "severity" de l'entrée de journal.

Limitations connues

Lorsque vous acheminez des journaux du récepteur de journaux vers Cloud Storage, la destination Cloud Storage ne contient que des journaux de requêtes. App Engine écrit les journaux d'application dans différents dossiers.

Les entrées de journal acheminées sont enregistrées toutes les heures et sous forme de lots dans des buckets Cloud Storage. L'affichage des premières entrées peut prendre 2 à 3 heures.

Dans l'environnement flexible App Engine, la journalisation fonctionne automatiquement. Toutefois, les journaux sont collectés dans un format différent. Ils ne sont pas regroupés par requête. Par ailleurs, les journaux de stdout et de stderr sont collectés séparément.

Table de mappage UDM

Champ du journal Mappage UDM Logique
jsonPayload.logger, taskTypeName, jsonPayload.@type, jsonPayload.backendTargetProjectNumber, jsonPayload.cacheDecision, resource.labels.version_id, resource.labels.module_id, logName, spanId, trace, protoPayload.@type, labels.clone_id, operation.producer additional.fields Fusionné avec les libellés clé-valeur créés à partir de chaque champ
métadonnées métadonnées Renommé à partir des métadonnées
receiveTimestamp metadata.collected_timestamp Analysé à l'aide d'un filtre de date avec RFC3339
metadata.event_type Définissez la valeur sur "USER_LOGIN" si has_principal, has_target et has_principal_user sont définis sur "true", sur "NETWORK_CONNECTION" si has_principal et has_target sont définis sur "true", sur "USER_UNCATEGORIZED" si has_principal et has_target sont définis sur "false", sur "STATUS_UPDATE" si has_principal est défini sur "true", sur "USER_UNCATEGORIZED" si has_principal_user est défini sur "true", ou sur "GENERIC_EVENT" dans le cas contraire.
metadata.extensions.auth.type Définissez sur "AUTHTYPE_UNSPECIFIED" si has_principal, has_target, has_principal_user
insertId metadata.product_log_id Valeur copiée directement
httpRequest.requestMethod,protoPayload.method network.http.method Valeur de httpRequest.requestMethod si elle n'est pas vide, sinon protoPayload.method
httpRequest.userAgent network.http.parsed_user_agent Converti en parseduseragent
httpRequest.status network.http.response_code Converti en chaîne, puis en entier
httpRequest.userAgent network.http.user_agent Valeur copiée directement
httpRequest.responseSize network.received_bytes Converti en uinteger
httpRequest.requestSize network.sent_bytes Converti en uinteger
compte principal compte principal Renommé à partir du compte principal s'il n'est pas vide
protoPayload.host principal.asset.hostname Valeur copiée directement
httpRequest.serverIp, protoPayload.ip principal.asset.ip Fusionné avec server_ip à partir de httpRequest.serverIp ou protoPayload.ip
protoPayload.host principal.hostname Valeur copiée directement
httpRequest.serverIp, protoPayload.ip principal.ip Fusionné avec server_ip à partir de httpRequest.serverIp ou protoPayload.ip
protoPayload.appId principal.resource.attribute.labels Fusionné avec appId_label contenant la clé "appId" et la valeur du champ
requestUser principal.user.email_addresses Fusionné avec requestUser s'il correspond au format de l'adresse e-mail
security_result security_result Fusionné à partir de security_result
resource.labels.forwarding_rule_name security_result.rule_labels Fusionné avec rule_label contenant la clé "forwarding_rule_name" et la valeur du champ
de gravité, security_result.severity Définissez la gravité sur ERROR ou CRITICAL si elle correspond à (?i)ERROR|CRITICAL, sur INFORMATIONAL si elle correspond à (?i)INFO, sur MEDIUM si elle correspond à (?i)WARN, sur LOW si elle correspond à (?i)DEBUG, ou sur UNKNOWN_SEVERITY dans le cas contraire.
jsonPayload.statusDetails security_result.summary Valeur copiée directement
cible cible Renommé à partir de la cible si elle n'est pas vide
resource.labels.backend_service_name target.application Valeur copiée directement
httpRequest.remoteIp, jsonPayload.remoteIp target.asset.ip Fusionné avec remote_ip extrait de httpRequest.remoteIp ou jsonPayload.remoteIp
resource.labels.project_id target.cloud.project.name Valeur copiée directement
httpRequest.remoteIp, jsonPayload.remoteIp target.ip Fusionné avec remote_ip extrait de httpRequest.remoteIp ou jsonPayload.remoteIp
resource.labels.zone target.resource.attribute.cloud.availability_zone Valeur copiée directement
resource.labels.target_proxy_name, resource.labels.url_map_name target.resource.attribute.labels Fusionnées avec les libellés de chaque source
resource.type target.resource.type Valeur copiée directement
httpRequest.requestUrl target.url Valeur copiée directement
metadata.product_name Définissez-le sur "GCP_APP_ENGINE".
metadata.vendor_name Défini sur "GCP"

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