Recopila registros de Google App Engine
En este documento, se explica cómo transferir registros de Google App Engine a Google Security Operations con Google Cloud Storage V2.
Google App Engine es una plataforma sin servidores completamente administrada para compilar e implementar aplicaciones web y APIs. App Engine genera automáticamente registros de solicitudes para las solicitudes HTTP y registros de aplicaciones a partir de tu código. Estos registros se envían a Cloud Logging y se pueden exportar a Cloud Storage para su transferencia a Google Security Operations.
Antes de comenzar
Asegúrate de cumplir con los siguientes requisitos previos:
- Una instancia de Google SecOps
- Un proyecto de GCP con la API de Cloud Storage habilitada
- Permisos para crear y administrar buckets de GCS
- Permisos para administrar políticas de IAM en buckets de GCS
- Permisos para crear receptores de Cloud Logging (roles/logging.configWriter)
- Una aplicación de App Engine activa (entorno estándar o flexible)
Crea un bucket de Google Cloud Storage
- Ve a Google Cloud Console.
- Selecciona tu proyecto o crea uno nuevo.
- En el menú de navegación, ve a Cloud Storage > Buckets.
- Haz clic en Crear bucket.
Proporciona los siguientes detalles de configuración:
Configuración Valor Asigna un nombre a tu bucket Ingresa un nombre global único (por ejemplo, appengine-logs-export).Tipo de ubicación Elige según tus necesidades (región, birregional, multirregional) Ubicación Selecciona la ubicación (por ejemplo, us-central1).Clase de almacenamiento Estándar (recomendado para los registros a los que se accede con frecuencia) Control de acceso Uniforme (recomendado) Herramientas de protección Opcional: Habilita el control de versiones de objetos o la política de retención Haz clic en Crear.
Configura Cloud Logging para exportar registros de App Engine a GCS
Cloud Logging usa receptores de registros para enrutar las entradas de registro a destinos compatibles, incluidos los buckets de Cloud Storage. La identidad de escritor del receptor requiere el rol de Creador de objetos de Storage (roles/storage.objectCreator) en el bucket de destino.
Crea un receptor de Cloud Logging
- En la consola de Google Cloud, ve a Logging > Enrutador de registros.
- Haz clic en Crear un receptor.
- Proporciona los siguientes detalles de configuración:
- Nombre del receptor: Ingresa un nombre descriptivo (por ejemplo,
appengine-to-gcs). - Descripción del receptor: Es una descripción opcional.
- Nombre del receptor: Ingresa un nombre descriptivo (por ejemplo,
- Haz clic en Siguiente.
- En la sección Selecciona el servicio de receptor, haz lo siguiente:
- Servicio de receptor: Selecciona Bucket de Cloud Storage.
- Selecciona un bucket de Cloud Storage: Selecciona
appengine-logs-exporten el menú desplegable.
- Haz clic en Siguiente.
En la sección Elige registros para incluirlos en el receptor, ingresa una consulta de filtro para seleccionar los registros de App Engine. El tipo de recurso debe ser exactamente "gae_app".
Para todos los registros de App Engine (registros de solicitudes y de aplicaciones):
resource.type="gae_app"Solo para los registros de solicitudes de App Engine:
resource.type="gae_app" logName="projects/PROJECT_ID/logs/appengine.googleapis.com/request_log"Para los registros de aplicaciones de App Engine (stdout/stderr):
resource.type="gae_app" (logName="projects/PROJECT_ID/logs/stdout" OR logName="projects/PROJECT_ID/logs/stderr")Reemplaza
PROJECT_IDpor el ID del proyecto de GCP.Haz clic en Siguiente.
Revisa la configuración y haz clic en Crear receptor.
Otorga permisos a la identidad de escritor del receptor
Después de crear el receptor, debes otorgarle a la identidad de escritor del receptor el rol de Creador de objetos de Storage en el bucket de destino. La identidad de escritor de la cuenta de servicio se ve de la siguiente manera: serviceAccount:service-123456789012@gcp-sa-logging.iam.gserviceaccount.com
- En la página Enrutador de registros, busca el receptor que creaste recientemente.
- Haz clic en el ícono de menú (tres puntos verticales) junto al nombre del receptor.
- Selecciona Ver detalles del receptor.
- Copia la Identidad del escritor (correo electrónico de la cuenta de servicio).
- Ve a Cloud Storage > Buckets.
- Haz clic en el nombre del bucket (
appengine-logs-export). - Ve a la pestaña Permisos.
- Haz clic en Otorgar acceso.
- Proporciona los siguientes detalles de configuración:
- Agregar principales: Pega la identidad del escritor del receptor (correo electrónico de la cuenta de servicio).
- Asignar roles: Selecciona Creador de objetos de Storage.
- Haz clic en Guardar.
Recupera la cuenta de servicio de Google SecOps
Las Operaciones de seguridad de Google usan una cuenta de servicio única para leer datos de tu bucket de GCS. Debes otorgar acceso a tu bucket a esta cuenta de servicio.
Configura un feed en Google SecOps para transferir registros de App Engine
- Ve a Configuración de SIEM > Feeds.
- Haz clic en Agregar feed nuevo.
- Haz clic en Configura un feed único.
- En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo,
App Engine Logs). - Selecciona Google Cloud Storage V2 como el Tipo de fuente.
Selecciona GCP_APP_ENGINE como el Tipo de registro.
Haz clic en Obtener cuenta de servicio.
Se mostrará un correo electrónico único de la cuenta de servicio, por ejemplo:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comCopia esta dirección de correo electrónico. la usarás en el próximo paso.
Haz clic en Siguiente.
Especifica valores para los siguientes parámetros de entrada:
URL del bucket de almacenamiento: Ingresa el URI del bucket de GCS con la ruta de acceso del prefijo:
gs://appengine-logs-export/Cloud Logging organiza los archivos de registro exportados en jerarquías de directorios por tipo de registro y fecha. El tipo de registro puede ser un nombre compuesto, como appengine.googleapis.com/request_log. Los archivos se fragmentan y se nombran con períodos (por ejemplo, 08:00:00_08:59:59_S0.json).
Opción de borrado de la fuente: Selecciona la opción de borrado según tu preferencia:
- Nunca: Nunca borra ningún archivo después de las transferencias (se recomienda para las pruebas).
- Borrar archivos transferidos: Borra los archivos después de la transferencia exitosa.
- Borrar los archivos transferidos y los directorios vacíos: Borra los archivos y los directorios vacíos después de la transferencia exitosa.
Antigüedad máxima del archivo: Incluye los archivos modificados en la cantidad de días especificada. El valor predeterminado es de 180 días.
Espacio de nombres del recurso: Es el espacio de nombres del recurso.
Etiquetas de transmisión: Es la etiqueta que se aplicará a los eventos de este feed.
Haz clic en Siguiente.
Revisa la nueva configuración del feed en la pantalla Finalizar y, luego, haz clic en Enviar.
Otorga permisos de IAM a la cuenta de servicio de Google SecOps
La cuenta de servicio de Google SecOps necesita el rol de visualizador de objetos de almacenamiento en tu bucket de GCS.
- Ve a Cloud Storage > Buckets.
- Haz clic en el nombre del bucket (
appengine-logs-export). - Ve a la pestaña Permisos.
- Haz clic en Otorgar acceso.
- Proporciona los siguientes detalles de configuración:
- Agregar principales: Pega el correo electrónico de la cuenta de servicio de Google SecOps.
- Asignar roles: Selecciona Visualizador de objetos de Storage.
Haz clic en Guardar.
Información sobre la estructura de los registros de App Engine
App Engine envía automáticamente los registros de solicitud y los registros de la app a Cloud Logging. App Engine emite registros de forma automática para las solicitudes enviadas a tu app, por lo que no es necesario escribir registros de solicitudes. En esta sección, se explica cómo escribir registros de apps.
Los registros de solicitudes de App Engine tienen entradas de registro que contienen campos protoPayload que incluyen objetos de tipo RequestLog con @type "type.googleapis.com/google.appengine.logging.v1.RequestLog". El tipo de recurso es "gae_app".
De forma predeterminada, la carga útil del registro es una cadena de texto almacenada en el campo textPayload de la entrada de registro. Las cadenas aparecerán como mensajes en el Explorador de registros y se asociarán con el servicio y la versión de App Engine que las emitió.
Para escribir registros estructurados, debes escribir registros en el formato de una sola línea de JSON serializado. Cuando proporcionas un registro estructurado como un diccionario JSON, algunos campos especiales se quitan de jsonPayload y se escriben en el campo correspondiente en la LogEntry generada. Por ejemplo, si tu JSON incluye una propiedad de gravedad, se quita de jsonPayload y aparece como la gravedad de la entrada de registro.
Limitaciones conocidas
Cuando enrutas registros del receptor de registros a Cloud Storage, el destino de Cloud Storage solo contiene registros de solicitud. App Engine escribe registros de aplicaciones en diferentes carpetas.
Las entradas de registro se guardan en buckets de Cloud Storage en lotes por hora. Es posible que se necesiten entre 2 y 3 horas para que aparezcan las primeras entradas.
En el entorno flexible de App Engine, el registro funciona automáticamente. Sin embargo, los registros se recopilan en un formato diferente. Los registros no se agruparán por solicitudes, y los de stdout y stderr se recopilarán por separado.
Tabla de asignación de UDM
| Campo de registro | Asignación de UDM | Lógica |
|---|---|---|
| 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 | Se combinan con las etiquetas de clave-valor creadas a partir de cada campo. |
| metadatos | metadatos | Se cambió el nombre de los metadatos |
| receiveTimestamp | metadata.collected_timestamp | Se analizó con el filtro de fecha con RFC3339 |
| metadata.event_type | Se establece en "USER_LOGIN" si tiene principal, tiene destino y tiene usuario principal; en "NETWORK_CONNECTION" si tiene principal y tiene destino; en "USER_UNCATEGORIZED" si no tiene principal y tiene destino; en "STATUS_UPDATE" si tiene principal; en "USER_UNCATEGORIZED" si tiene usuario principal; de lo contrario, en "GENERIC_EVENT". | |
| metadata.extensions.auth.type | Se establece en "AUTHTYPE_UNSPECIFIED" si tiene principal, tiene destino o tiene usuario principal. | |
| insertId | metadata.product_log_id | Valor copiado directamente |
| httpRequest.requestMethod,protoPayload.method | network.http.method | Valor de httpRequest.requestMethod si no está vacío; de lo contrario, protoPayload.method |
| httpRequest.userAgent | network.http.parsed_user_agent | Se convirtió a parseduseragent |
| httpRequest.status | network.http.response_code | Se convierte en una cadena y, luego, en un número entero. |
| httpRequest.userAgent | network.http.user_agent | Valor copiado directamente |
| httpRequest.responseSize | network.received_bytes | Se convirtió en uinteger |
| httpRequest.requestSize | network.sent_bytes | Se convirtió en uinteger |
| entidad | entidad | Se cambió el nombre de principal si no está vacío |
| protoPayload.host | principal.asset.hostname | Valor copiado directamente |
| httpRequest.serverIp, protoPayload.ip | principal.asset.ip | Se combinó con server_ip de httpRequest.serverIp o protoPayload.ip |
| protoPayload.host | principal.hostname | Valor copiado directamente |
| httpRequest.serverIp, protoPayload.ip | principal.ip | Se combinó con server_ip de httpRequest.serverIp o protoPayload.ip |
| protoPayload.appId | principal.resource.attribute.labels | Se combinó con appId_label, que contiene la clave "appId" y el valor del campo. |
| requestUser | principal.user.email_addresses | Se combina con requestUser si coincide con el patrón de correo electrónico. |
| security_result | security_result | Se combinó de security_result |
| resource.labels.forwarding_rule_name | security_result.rule_labels | Se combinó con rule_label que contiene la clave "forwarding_rule_name" y el valor del campo. |
| gravedad, | security_result.severity | Se establece en gravedad si coincide con (?i)ERROR|CRITICAL, en INFORMATIONAL si coincide con (?i)INFO, en MEDIUM si coincide con (?i)WARN, en LOW si coincide con (?i)DEBUG y, de lo contrario, en UNKNOWN_SEVERITY. |
| jsonPayload.statusDetails | security_result.summary | Valor copiado directamente |
| objetivo | objetivo | Se cambió el nombre del destino si no está vacío. |
| resource.labels.backend_service_name | target.application | Valor copiado directamente |
| httpRequest.remoteIp, jsonPayload.remoteIp | target.asset.ip | Se combina con remote_ip extraído de httpRequest.remoteIp o jsonPayload.remoteIp |
| resource.labels.project_id | target.cloud.project.name | Valor copiado directamente |
| httpRequest.remoteIp, jsonPayload.remoteIp | target.ip | Se combina con remote_ip extraído de httpRequest.remoteIp o jsonPayload.remoteIp |
| resource.labels.zone | target.resource.attribute.cloud.availability_zone | Valor copiado directamente |
| resource.labels.target_proxy_name, resource.labels.url_map_name | target.resource.attribute.labels | Se combinan con las etiquetas de cada fuente. |
| resource.type | target.resource.type | Valor copiado directamente |
| httpRequest.requestUrl | target.url | Valor copiado directamente |
| metadata.product_name | Se establece en "GCP_APP_ENGINE". | |
| metadata.vendor_name | Se estableció en "GCP". |
¿Necesitas más ayuda? Obtén respuestas de miembros de la comunidad y profesionales de Google SecOps.