Recopila registros de CloudM

Versión del analizador: 6.0

Se admite en los siguientes sistemas operativos:

En este documento, se explica cómo transferir registros de CloudM a Google Security Operations con Google Cloud Storage V2.

CloudM es una plataforma de SaaS para Google Workspace y Microsoft 365 que proporciona automatización de flujos de trabajo para la incorporación y desvinculación de usuarios, la copia de seguridad de datos, el archivado y la migración. CloudM Automate genera un registro de auditoría completo de todas las acciones realizadas en tu dominio, incluidos los eventos de administración de usuarios, los pasos del flujo de trabajo de baja, los cambios de configuración y las operaciones relacionadas con la seguridad. Se conservan los datos del registro de auditoría del último año.

Antes de comenzar

Asegúrate de cumplir con los siguientes requisitos previos:

  • Una instancia de Google SecOps
  • Un Google Cloud proyecto con la API de Cloud Storage habilitada
  • Permisos para crear y administrar buckets de Cloud Storage
  • Permisos para administrar políticas de Identity and Access Management (IAM) en buckets de Cloud Storage
  • Permisos para crear servicios de Cloud Run, temas de Pub/Sub y trabajos de Cloud Scheduler
  • Acceso de administrador a tu instancia de CloudM Automate con el permiso Edit Global Settings
  • La URL de tu instancia de CloudM Automate (por ejemplo, yourcompany.cloudm.io)
  • ID de tu dominio de CloudM

Recopila las credenciales de CloudM Automate

Crea un rol personalizado para acceder a los registros de la API

  1. Accede a tu instancia de CloudM Automate.
  2. Ve a Configuración > Roles.
  3. Haz clic en Agregar rol para crear un rol nuevo.
  4. En el campo Nombre del rol, ingresa un nombre descriptivo (por ejemplo, Google SecOps Log Reader).
  5. En la lista de permisos, habilita el siguiente permiso:

    • View Logs: Otorga la capacidad de ver todos los registros de la aplicación.
  6. Guarda el rol.

Crea una cuenta de servicio y asigna el rol

  1. En CloudM Automate, ve a Settings > Roles.
  2. Crea o identifica una cuenta de servicio que se usará para acceder a la API.
  3. Asigna el rol de lector de registros de Google SecOps a la cuenta de servicio.
  4. Asegúrate de que el rol se asigne con el alcance global para que la cuenta de servicio pueda acceder a los registros en todo el dominio.

Obtén el token de acceso de la cuenta de servicio

  1. Genera un token de acceso para la cuenta de servicio.
  2. El token de acceso se usa como token de portador en el encabezado Authorization cuando se realizan solicitudes a la API de CloudM Logs.
  3. Registra los valores siguientes:

    • URL de la instancia de Automate: Es la URL de tu instancia de CloudM Automate (por ejemplo, yourcompany.cloudm.io).
    • ID de dominio: Es el identificador de tu dominio de CloudM.
    • Token de acceso de la cuenta de servicio: Es el token de portador para la autenticación de la API.

Verifica los permisos

Para verificar que la cuenta tenga los permisos necesarios, sigue estos pasos:

  1. Accede a CloudM Automate.
  2. Ve a Configuración > Roles.
  3. Verifica que la cuenta de servicio tenga asignado el permiso Ver registros con alcance global.
  4. Si no ves esta opción, comunícate con tu administrador para que te otorgue los permisos Edit Global Settings y View Logs.

Prueba el acceso a la API

  • Prueba tus credenciales antes de continuar con la integración:

    # Replace with your actual credentials
    CLOUDM_INSTANCE="yourcompany.cloudm.io"
    DOMAIN_ID="your-domain-id"
    ACCESS_TOKEN="your-access-token"
    
    # Test API access
    curl -v -H "Authorization: Bearer ${ACCESS_TOKEN}" \
      "https://${CLOUDM_INSTANCE}/_ah/api/events/v1/${DOMAIN_ID}?from=$(date -u +%Y-%m-%d)&to=$(date -u +%Y-%m-%d)"
    

    Una respuesta correcta devuelve un array JSON de eventos de registro de auditoría.

Permisos de API necesarios

  • La cuenta de servicio requiere el siguiente permiso:

    Permiso Nivel de acceso Objetivo
    Ver registros Global Recupera todos los eventos de registro de auditoría de CloudM Automate

Crea un Google Cloud bucket de Storage

  1. Ve a la consola de Google Cloud.
  2. Selecciona tu proyecto o crea uno nuevo.
  3. En el menú de navegación, ve a Cloud Storage > Buckets.
  4. Haz clic en Crear bucket.
  5. Proporciona los siguientes detalles de configuración:

    Parámetro de configuración Valor
    Asigna un nombre a tu bucket Ingresa un nombre global único (por ejemplo, cloudm-audit-logs).
    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
  6. Haz clic en Crear.

Crea una cuenta de servicio para la función de Cloud Run

La función de Cloud Run necesita una cuenta de servicio con permisos para escribir en el bucket de Cloud Storage y ser invocada por Pub/Sub.

Crear cuenta de servicio

  1. En la Google Cloud consola, ve a IAM y administración > Cuentas de servicio.
  2. Haz clic en Crear cuenta de servicio.
  3. Proporciona los siguientes detalles de configuración:
    • Nombre de la cuenta de servicio: Ingresa cloudm-audit-collector-sa.
    • Descripción de la cuenta de servicio: Ingresa Service account for Cloud Run function to collect CloudM audit logs.
  4. Haz clic en Crear y continuar.
  5. En la sección Otorga a esta cuenta de servicio acceso al proyecto, agrega los siguientes roles:

    1. Haz clic en Selecciona un rol.
    2. Busca y selecciona Administrador de objetos de Storage.
    3. Haz clic en + Agregar otro rol.
    4. Busca y selecciona Invocador de Cloud Run.
    5. Haz clic en + Agregar otro rol.
    6. Busca y selecciona Invocador de Cloud Functions.
  6. Haz clic en Continuar.

  7. Haz clic en Listo.

    Estos roles son necesarios para las siguientes acciones:

    • Administrador de objetos de Storage: Escribe registros en el bucket de Cloud Storage y administra archivos de estado
    • Invocador de Cloud Run: Permite que Pub/Sub invoque la función
    • Invocador de Cloud Functions: Permite la invocación de funciones

Otorga permisos de IAM en el bucket de Cloud Storage

Otorga permisos de escritura a la cuenta de servicio en el bucket de Cloud Storage:

  1. Ve a Cloud Storage > Buckets.
  2. Haz clic en el nombre de tu bucket (cloudm-audit-logs).
  3. Ve a la pestaña Permisos.
  4. Haz clic en Otorgar acceso.
  5. Proporciona los siguientes detalles de configuración:

    • Agregar entidades: Ingresa el correo electrónico de la cuenta de servicio (cloudm-audit-collector-sa@PROJECT_ID.iam.gserviceaccount.com).
    • Asignar roles: Selecciona Administrador de objetos de almacenamiento.
  6. Haz clic en Guardar.

Crea un tema de Pub/Sub

Crea un tema de Pub/Sub en el que Cloud Scheduler publicará y al que se suscribirá la función de Cloud Run.

  1. En la Google Cloud consola, ve a Pub/Sub > Temas.
  2. Haz clic en Crear un tema.
  3. Proporciona los siguientes detalles de configuración:

    • ID del tema: Ingresa cloudm-audit-trigger.
    • Deja el resto de la configuración con sus valores predeterminados.
  4. Haz clic en Crear.

Crea una función de Cloud Run para recopilar registros

La función de Cloud Run se activará con mensajes de Pub/Sub de Cloud Scheduler para recuperar registros de la API de CloudM Automate Logs y escribirlos en Cloud Storage.

  1. En la Google Cloud consola, ve a Cloud Run.
  2. Haz clic en Crear servicio.
  3. Selecciona Función (usa un editor intercalado para crear una función).
  4. En la sección Configurar, proporciona los siguientes detalles de configuración:

    Parámetro de configuración Valor
    Nombre del servicio cloudm-audit-collector
    Región Selecciona la región que coincida con tu bucket de Cloud Storage (por ejemplo, us-central1).
    Tiempo de ejecución Selecciona Python 3.12 o una versión posterior.
  5. En la sección Activador (opcional), haz lo siguiente:

    1. Haz clic en + Agregar activador.
    2. Selecciona Cloud Pub/Sub.
    3. En Selecciona un tema de Cloud Pub/Sub, elige cloudm-audit-trigger.
    4. Haz clic en Guardar.
  6. En la sección Autenticación, haz lo siguiente:

    1. Selecciona Necesita autenticación.
    2. Verifica Identity and Access Management (IAM).
  7. Desplázate hacia abajo y expande Contenedores, redes y seguridad.

  8. Ve a la pestaña Seguridad:

    • Cuenta de servicio: Selecciona cloudm-audit-collector-sa.
  9. Ve a la pestaña Contenedores:

    1. Haz clic en Variables y secretos.
    2. Haz clic en + Agregar variable para cada variable de entorno:

      Nombre de la variable Valor de ejemplo Descripción
      GCS_BUCKET cloudm-audit-logs Nombre del bucket de Cloud Storage
      GCS_PREFIX cloudm-audit Prefijo para los archivos de registro
      STATE_KEY cloudm-audit/state.json Ruta de acceso al archivo de estado
      CLOUDM_INSTANCE_URL yourcompany.cloudm.io URL de la instancia de CloudM Automate
      CLOUDM_DOMAIN_ID your-domain-id Identificador de dominio de CloudM
      CLOUDM_ACCESS_TOKEN your-access-token Token del portador de la cuenta de servicio de CloudM
      LOOKBACK_HOURS 24 Período de visualización inicial
  10. En la sección Variables y Secrets, desplázate hacia abajo hasta Solicitudes:

    • Tiempo de espera de la solicitud: Ingresa 600 segundos (10 minutos).
  11. Ve a la pestaña Configuración:

    • En la sección Recursos, haz lo siguiente:

      • Memoria: Selecciona 512 MiB o más.
      • CPU: Selecciona 1.
  12. En la sección Escalamiento de revisión, haz lo siguiente:

    • Cantidad mínima de instancias: Ingresa 0.
    • Cantidad máxima de instancias: Ingresa 100.
  13. Haz clic en Crear.

  14. Espera a que se cree el servicio (de 1 a 2 minutos).

  15. Después de crear el servicio, se abrirá automáticamente el editor de código intercalado.

Agregar el código de función

  1. Ingresa main en el campo Punto de entrada.
  2. En el editor de código intercalado, crea dos archivos:

    • main.py:

      import functions_framework
      from google.cloud import storage
      import json
      import os
      import urllib3
      from datetime import datetime, timezone, timedelta
      
      http = urllib3.PoolManager(
        timeout=urllib3.Timeout(connect=10.0, read=60.0),
        retries=False,
      )
      
      storage_client = storage.Client()
      
      GCS_BUCKET = os.environ.get('GCS_BUCKET')
      GCS_PREFIX = os.environ.get('GCS_PREFIX', 'cloudm-audit')
      STATE_KEY = os.environ.get('STATE_KEY', 'cloudm-audit/state.json')
      CLOUDM_INSTANCE_URL = os.environ.get('CLOUDM_INSTANCE_URL', '').rstrip('/')
      CLOUDM_DOMAIN_ID = os.environ.get('CLOUDM_DOMAIN_ID')
      CLOUDM_ACCESS_TOKEN = os.environ.get('CLOUDM_ACCESS_TOKEN')
      LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24'))
      
      @functions_framework.cloud_event
      def main(cloud_event):
        if not all([GCS_BUCKET, CLOUDM_INSTANCE_URL, CLOUDM_DOMAIN_ID, CLOUDM_ACCESS_TOKEN]):
          print('Error: Missing required environment variables')
          return
      
        try:
          bucket = storage_client.bucket(GCS_BUCKET)
          state = load_state(bucket)
          now = datetime.now(timezone.utc)
      
          if isinstance(state, dict) and state.get('last_event_date'):
            try:
              last_date = state['last_event_date']
              last_time = datetime.strptime(last_date, '%Y-%m-%d').replace(tzinfo=timezone.utc)
            except Exception as e:
              print(f"Warning: Could not parse last_event_date: {e}")
              last_time = now - timedelta(hours=LOOKBACK_HOURS)
          else:
            last_time = now - timedelta(hours=LOOKBACK_HOURS)
      
          from_date = last_time.strftime('%Y-%m-%d')
          to_date = now.strftime('%Y-%m-%d')
      
          print(f"Fetching logs from {from_date} to {to_date}")
      
          records = fetch_logs(from_date, to_date)
      
          if not records:
            print("No new log records found.")
            save_state(bucket, to_date)
            return
      
          timestamp = now.strftime('%Y%m%d_%H%M%S')
          object_key = f"{GCS_PREFIX}/cloudm_audit_{timestamp}.ndjson"
          blob = bucket.blob(object_key)
      
          ndjson = '\n'.join(
            [json.dumps(record, ensure_ascii=False, default=str) for record in records]
          ) + '\n'
          blob.upload_from_string(ndjson, content_type='application/x-ndjson')
      
          print(f"Wrote {len(records)} records to gs://{GCS_BUCKET}/{object_key}")
      
          save_state(bucket, to_date)
      
          print(f"Successfully processed {len(records)} records")
      
        except Exception as e:
          print(f'Error processing logs: {str(e)}')
          raise
      
      def fetch_logs(from_date, to_date):
        instance = CLOUDM_INSTANCE_URL
        if not instance.startswith('https://'):
          instance = f"https://{instance}"
      
        endpoint = f"{instance}/_ah/api/events/v1/{CLOUDM_DOMAIN_ID}"
      
        headers = {
          'Authorization': f'Bearer {CLOUDM_ACCESS_TOKEN}',
          'Accept': 'application/json',
          'User-Agent': 'GoogleSecOps-CloudMCollector/1.0'
        }
      
        url = f"{endpoint}?from={from_date}&to={to_date}"
      
        try:
          response = http.request('GET', url, headers=headers)
      
          if response.status == 429:
            retry_after = int(response.headers.get('Retry-After', '60'))
            print(f"Rate limited (429). Retry after {retry_after}s.")
            return []
      
          if response.status != 200:
            print(f"HTTP Error: {response.status}")
            response_text = response.data.decode('utf-8')
            print(f"Response body: {response_text}")
            return []
      
          data = json.loads(response.data.decode('utf-8'))
      
          if isinstance(data, list):
            records = data
          elif isinstance(data, dict):
            records = data.get('items', data.get('events', [data]))
          else:
            records = []
      
          print(f"Retrieved {len(records)} events")
          return records
      
        except Exception as e:
          print(f"Error fetching logs: {e}")
          return []
      
      def load_state(bucket):
        try:
          blob = bucket.blob(STATE_KEY)
          if blob.exists():
            return json.loads(blob.download_as_text())
        except Exception as e:
          print(f"Warning: Could not load state: {e}")
        return {}
      
      def save_state(bucket, last_event_date):
        try:
          state = {
            'last_event_date': last_event_date,
            'last_run': datetime.now(timezone.utc).isoformat()
          }
          blob = bucket.blob(STATE_KEY)
          blob.upload_from_string(
            json.dumps(state, indent=2),
            content_type='application/json'
          )
          print(f"Saved state: last_event_date={last_event_date}")
        except Exception as e:
          print(f"Warning: Could not save state: {e}")
      
    • requirements.txt:

      functions-framework==3.*
      google-cloud-storage==2.*
      urllib3>=2.0.0
      
  3. Haz clic en Implementar para guardar y, luego, implementar la función.

  4. Espera a que se complete la implementación (de 2 a 3 minutos).

Crea un trabajo de Cloud Scheduler

Cloud Scheduler publicará mensajes en el tema de Pub/Sub a intervalos regulares, lo que activará la función de Cloud Run.

  1. En la Google Cloud consola, ve a Cloud Scheduler.
  2. Haz clic en Crear trabajo.
  3. Proporciona los siguientes detalles de configuración:

    Parámetro de configuración Valor
    Nombre cloudm-audit-collector-hourly
    Región Selecciona la misma región que la función de Cloud Run
    Frecuencia 0 * * * * (cada hora, en punto)
    Zona horaria Selecciona la zona horaria (se recomienda UTC)
    Tipo de objetivo Pub/Sub
    Tema Seleccionar cloudm-audit-trigger
    Cuerpo del mensaje {} (objeto JSON vacío)
  4. Haz clic en Crear.

Prueba la integración

  1. En la consola de Cloud Scheduler, busca tu trabajo (cloudm-audit-collector-hourly).
  2. Haz clic en Ejecutar forzosamente para activar el trabajo de forma manual.
  3. Espera unos segundos.
  4. Ve a Cloud Run > Servicios.
  5. Haz clic en cloudm-audit-collector:
  6. Haz clic en la pestaña Registros.
  7. Verifica que la función se haya ejecutado correctamente. Busca lo siguiente:

    Fetching logs from YYYY-MM-DD to YYYY-MM-DD
    Retrieved X events
    Wrote X records to gs://cloudm-audit-logs/cloudm-audit/cloudm_audit_YYYYMMDD_HHMMSS.ndjson
    Successfully processed X records
    
  8. Ve a Cloud Storage > Buckets.

  9. Haz clic en cloudm-audit-logs:

  10. Navega a la carpeta cloudm-audit/:

  11. Verifica que se haya creado un archivo .ndjson nuevo con la marca de tiempo actual.

Si ves errores en los registros, haz lo siguiente:

  • HTTP 401: Verifica que la variable de entorno CLOUDM_ACCESS_TOKEN sea correcta.
  • HTTP 403: Verifica que la cuenta de servicio tenga el permiso Ver registros con alcance global.
  • HTTP 429: Se aplica una limitación de frecuencia. La función se detendrá y se reanudará en la próxima ejecución programada.
  • Faltan variables de entorno: Verifica que todas las variables requeridas estén configuradas en la configuración de la función de Cloud Run.

Recupera la cuenta de servicio de Google SecOps

Google SecOps usa una cuenta de servicio única para leer datos de tu bucket de Cloud Storage. Debes otorgar acceso a tu bucket a esta cuenta de servicio.

Obtén el correo electrónico de la cuenta de servicio

  1. Ve a Configuración de SIEM > Feeds.
  2. Haz clic en Agregar feed nuevo.
  3. Haz clic en Configura un feed único.
  4. En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo, CloudM Audit Logs).
  5. Selecciona Google Cloud Storage V2 como el Tipo de fuente.
  6. Selecciona CloudM como el Tipo de registro.
  7. 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.com
    
  8. Copia esta dirección de correo electrónico para usarla en el siguiente paso.

  9. Haz clic en Siguiente.

  10. Especifica valores para los siguientes parámetros de entrada:

    • URL del bucket de almacenamiento: Ingresa el URI del bucket de Cloud Storage con la ruta de acceso del prefijo:

      gs://cloudm-audit-logs/cloudm-audit/
      
    • Opción de eliminación del código fuente: Selecciona la opción de eliminación 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 que se transfirieron correctamente.
      • Borrar los archivos transferidos y los directorios vacíos: Borra los archivos y los directorios vacíos después de que se transfirieron correctamente.

    • Antigüedad máxima del archivo: Incluye los archivos modificados en la cantidad de días más reciente (el valor predeterminado es 180 días).

    • Espacio de nombres del recurso: Es el espacio de nombres del recurso.

    • Etiquetas de transferencia: Es la etiqueta que se aplicará a los eventos de este feed.

  11. Haz clic en Siguiente.

  12. 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 Storage en tu bucket de Cloud Storage.

  1. Ve a Cloud Storage > Buckets.
  2. Haz clic en cloudm-audit-logs:
  3. Ve a la pestaña Permisos.
  4. Haz clic en Otorgar acceso.
  5. Proporciona los siguientes detalles de configuración:

    • Agregar entidades: Pega el correo electrónico de la cuenta de servicio de Google SecOps.
    • Asignar roles: Selecciona Visualizador de objetos de Storage.
  6. Haz clic en Guardar.

Parámetros de la API de CloudM Logs

La API de CloudM Logs admite los siguientes parámetros de consulta para filtrar eventos de registro:

Parámetro Formato Descripción
byUser Dirección de correo electrónico Filtrar eventos según el usuario que realizó la acción (análogo a Usuario en la IU de CloudM)
from yyyy-MM-dd Fecha de inicio del filtro de período
a yyyy-MM-dd Fecha de finalización del filtro de período
contextType String Filtrar por tipo de contexto (por ejemplo, perfil, grupo, UO)
contextName String Filtrar por el objetivo de una acción (por ejemplo, un usuario específico que se da de baja)
operación String Filtrar por tipo de operación (por ejemplo, asignar alias, suspender usuario)
country Código de país Filtrar por código de país de la ubicación geográfica

Tabla de asignación de UDM

Campo de registro Asignación de UDM Lógica
acerca de acerca de Valor copiado directamente
Context_Name about.labels Se combinaron como pares clave-valor de about_Context_Name, about_Context_Type y labels0.
Context_Type about.labels
Login_Type about.labels
Emisor additional.fields Se combinó de additional_field0, additional_field1 y additional_field2.
SAML_code additional.fields
SAML_ACS_Url additional.fields
Operación extensions.auth.type Se establece en SSO si Operation coincide con SSORequest, y en AUTHTYPE_UNSPECIFIED si Context_Type es LoginUser.
Context_Type extensions.auth.type
Marca de tiempo metadata.event_timestamp Se extrajeron la fecha y hora y la zona horaria de la marca de tiempo, se convirtió la zona horaria en desfase, se concatenó y se analizó como marca de tiempo.
Operación metadata.event_type Se establece en USER_UNCATEGORIZED si la operación coincide con Update/Delete/SuspendUser/UnsuspendUser/Create, USER_LOGIN si la operación coincide con SSORequest/SSORequestFail o Context_Type es LoginUser, STATUS_UPDATE si IP no está vacío y, de lo contrario, GENERIC_EVENT
Context_Type metadata.event_type
User_Agent network.http.user_agent Valor copiado directamente
entidad entidad Se cambió el nombre de principal si Context_Type != LoginUser; de lo contrario, se cambió el nombre de destino.
objetivo entidad
Organization_Unit principal.administrative_domain Valor copiado directamente
IP principal.ip Valor copiado directamente
Ciudad principal.location.city Valor copiado directamente
País principal.location.country_or_region Valor copiado directamente
Ubicación geográfica principal.location.region_latitude Latitud extraída de Geolocation con grok
Ubicación geográfica principal.location.region_longitude Longitud extraída de Geolocation con grok
Región principal.location.state Valor copiado directamente
Agente principal.user.attribute.roles Se establece en role.name si Actor no es un correo electrónico y no está vacío, luego se combina.
Agente principal.user.email_addresses El valor se copia directamente si Actor coincide con la regex de correo electrónico
Mensaje principal.user.userid Nombre de usuario extraído del mensaje con grok
security_result security_result Se combinó el objeto security_result
SAML_code security_result.action Se establece en ALLOW si SAML_code coincide con Success y en BLOCK si coincide con RequestDenied.
Mensaje security_result.description Valor copiado directamente
Gravedad security_result.severity Se establece en mayúsculas si es Error/Crítico, INFORMATIONAL si es Info, MEDIUM si es Advertencia y, de lo contrario, UNKNOWN_SEVERITY
Operación security_result.summary Valor copiado directamente
objetivo objetivo Se cambió el nombre de destino si Context_Type != LoginUser; de lo contrario, se cambió el nombre de principal.
entidad objetivo
metadata.product_name metadata.product_name Se estableció en "CLOUDM".
metadata.vendor_name metadata.vendor_name Se estableció en "CLOUDM".

Registro de cambios

Consulta el registro de cambios de este analizador

¿Necesitas más ayuda? Obtén respuestas de miembros de la comunidad y profesionales de Google SecOps.