Recopila registros de BeyondTrust Endpoint Privilege Management (EPM)
En este documento, se explica cómo transferir registros de BeyondTrust Endpoint Privilege Management (EPM) a Google Security Operations con Cloud Storage. El analizador se enfoca en transformar los datos de registro JSON sin procesar de BeyondTrust Endpoint en un formato estructurado que cumpla con el UDM de Google SecOps. Primero, inicializa los valores predeterminados para varios campos y, luego, analiza la carga útil JSON, y, posteriormente, asigna campos específicos del registro sin procesar a los campos del UDM correspondientes dentro del objeto event.idm.read_only_udm.
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 Functions, temas de Pub/Sub y trabajos de Cloud Scheduler
- Acceso con privilegios al arrendatario o la API de Endpoint Privilege Management de BeyondTrust
Cree un bucket de Cloud Storage
- Ve a la consola deGoogle Cloud .
- 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:
Parámetro de configuración Valor Asigna un nombre a tu bucket Ingresa un nombre global único (por ejemplo, beyondtrust-epm-logs).Tipo de ubicación Elige según tus necesidades (regional, birregional o 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.
Recopila las credenciales de la API de BeyondTrust EPM
- Accede a la consola web de BeyondTrust Privilege Management como administrador.
- Ve a Configuración > Configuración > Configuración de la API.
- Haz clic en Create an API Account.
- Proporciona los siguientes detalles de configuración:
- Nombre: Ingresa
Google SecOps Collector. - Acceso a la API: Habilita Informes (solo lectura). El extremo
/management-api/v3/Events/FromStartDateque usa este recopilador pertenece a la categoría de permisos de Informes. Habilita Auditoría (solo lectura) solo si también deseas recopilar auditorías de actividad de la consola web.
- Nombre: Ingresa
- Copia y guarda el ID de cliente y el secreto del cliente.
Copia la URL base de la API que se muestra en la parte superior de la página Configuración de la API. Por lo general, es
https://<your-tenant>-services.pm.beyondtrustcloud.com. Lo usarás comoBPT_API_URL.
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 para que Pub/Sub la invoque.
Crea la cuenta de servicio
- En la consola de Google Cloud , ve a IAM y administración > Cuentas de servicio.
- Haz clic en Crear cuenta de servicio.
- Proporciona los siguientes detalles de configuración:
- Nombre de la cuenta de servicio: Ingresa
beyondtrust-epm-collector-sa. - Descripción de la cuenta de servicio: Ingresa
Service account for Cloud Run function to collect BeyondTrust EPM logs.
- Nombre de la cuenta de servicio: Ingresa
- Haz clic en Crear y continuar.
- En la sección Otorga acceso al proyecto a esta cuenta de servicio, agrega los siguientes roles:
- Haz clic en Selecciona un rol.
- Busca y selecciona Administrador de objetos de Storage.
- Haz clic en + Agregar otro rol.
- Busca y selecciona Invocador de Cloud Run.
- Haz clic en + Agregar otro rol.
- Busca y selecciona Invocador de Cloud Functions.
- Haz clic en Continuar.
- Haz clic en Listo.
Estos roles son necesarios para lo siguiente:
- Administrador de objetos de Storage: Escribe registros en un 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 un bucket de Cloud Storage
Otorga permisos de escritura a la cuenta de servicio en el bucket de Cloud Storage:
- Ve a Cloud Storage > Buckets.
- Haz clic en el nombre de tu bucket.
- Ve a la pestaña Permisos.
- Haz clic en Otorgar acceso.
- Proporciona los siguientes detalles de configuración:
- Agregar entidades: Ingresa el correo electrónico de la cuenta de servicio. Por ejemplo,
beyondtrust-epm-collector-sa@PROJECT_ID.iam.gserviceaccount.com - Asignar roles: Selecciona Administrador de objetos de Storage.
- Agregar entidades: Ingresa el correo electrónico de la cuenta de servicio. Por ejemplo,
- 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.
- En la Google Cloud consola, ve a Pub/Sub > Temas.
- Haz clic en Crear un tema.
- Proporciona los siguientes detalles de configuración:
- ID del tema: Ingresa
beyondtrust-epm-trigger. - Deja el resto de la configuración con sus valores predeterminados.
- ID del tema: Ingresa
- Haz clic en Crear.
Crea una función de Cloud Run para recopilar registros
La función de Cloud Run se activa con mensajes de Pub/Sub de Cloud Scheduler para recuperar registros de la API de BeyondTrust EPM y los escribe en Cloud Storage.
- En la consola de Google Cloud , ve a Cloud Run.
- Haz clic en Crear servicio.
- Selecciona Función. Usa un editor intercalado para crear una función.
En la sección Configurar, proporciona los siguientes detalles de configuración:
Parámetro de configuración Valor Nombre del servicio beyondtrust-epm-collectorRegió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. En la sección Activador (opcional), haz lo siguiente:
- Haz clic en + Agregar activador.
- Selecciona Cloud Pub/Sub.
- En Selecciona un tema de Cloud Pub/Sub, elige el tema
beyondtrust-epm-trigger. - Haz clic en Guardar.
En la sección Autenticación, haz lo siguiente:
- Selecciona Necesita autenticación.
- Verifica Identity and Access Management (IAM).
Ve a Contenedores, redes y seguridad y expándelo.
Ve a la pestaña Seguridad:
- Cuenta de servicio: Selecciona la cuenta de servicio
beyondtrust-epm-collector-sa.
- Cuenta de servicio: Selecciona la cuenta de servicio
Ve a la pestaña Contenedores:
- Haz clic en Variables y secretos.
- Haz clic en + Agregar variable para cada variable de entorno:
Nombre de la variable Valor de ejemplo GCS_BUCKETbeyondtrust-epm-logsGCS_PREFIXbeyondtrust-epm/STATE_KEYbeyondtrust-epm-state.jsonBPT_API_URLhttps://yourtenant-services.pm.beyondtrustcloud.comCLIENT_IDyour-client-idCLIENT_SECRETyour-client-secretRECORD_SIZE1000MAX_BATCHES50LOOKBACK_HOURS24RECORD_SIZE: Registros por solicitud./Events/FromStartDateacepta valores de 1 a 1,000; la función limita los valores más altos.MAX_BATCHES: Es el límite de seguridad para las solicitudes por ejecución. Si una ejecución lo alcanza, el cursor sigue avanzando hacia lo que se escribió, por lo que la siguiente ejecución continúa desde allí en lugar de repetir el trabajo.LOOKBACK_HOURS: Indica qué tan atrás llega la primera ejecución. BeyondTrust conserva los eventos pasivos (códigos 106, 107, 603 y 706) durante 30 días y todos los demás eventos durante 90 días, por lo que una ventana de visualización más larga no muestra nada anterior a ese período.LOOKBACK_HOURS: Indica qué tan atrás se debe llegar en la primera ejecución, antes de que exista cualquier cursor.
En la pestaña Variables y Secrets, ve a Solicitudes:
- Tiempo de espera de la solicitud: Ingresa
600segundos (10 minutos).
- Tiempo de espera de la solicitud: Ingresa
Ve a la pestaña Configuración en Contenedores:
- En la sección Recursos, haz lo siguiente:
- Memoria: Selecciona 512 MiB o más.
- CPU: Selecciona 1.
- Haz clic en Listo.
- En la sección Recursos, haz lo siguiente:
Desplázate hasta Entorno de ejecución:
- Selecciona Predeterminado (recomendado).
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(o ajusta según la carga esperada).
- Cantidad mínima de instancias: Ingresa
Haz clic en Crear.
Espera a que se cree el servicio (de 1 a 2 minutos).
Después de crear el servicio, se abrirá automáticamente el editor de código intercalado.
Agrega un código de función
- Ingresa main en Punto de entrada de la función.
En el editor de código intercalado, crea dos archivos:
- Primer archivo:main.py:
import hashlib import json import os import re import time import urllib3 import uuid from datetime import datetime, timedelta, timezone from urllib.parse import urlencode import functions_framework from google.cloud import storage from google.cloud.exceptions import NotFound # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=60.0), retries=False, ) # Initialize Storage client storage_client = storage.Client() TOKEN_PATH = '/oauth/connect/token' # Events/FromStartDate is the endpoint BeyondTrust documents for bulk extraction: # "A new API is exposed to extract the events in bulk." It takes only StartDate and # RecordSize. Events/search is not usable here: it requires an OperatingSystem value, # it is a single string with no documented "all" value, so it can only ever return one # operating system per call. EVENTS_PATH = '/management-api/v3/Events/FromStartDate' OAUTH_SCOPE = 'urn:management:api' # RecordSize accepts 1 to 1000. MAX_RECORD_SIZE = 1000 class FetchError(Exception): """Raised when the BeyondTrust API call fails. The cursor must never advance on a failed fetch, otherwise the events in the failed window are skipped permanently. """ def rfc3339(dt: datetime) -> str: """Render a datetime the way StartDate accepts it: milliseconds and a literal Z.""" return dt.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' def parse_iso(stamp: str) -> datetime: """Parse any ISO 8601 timestamp shape the API emits. event.ingested comes back with a +00:00 offset and up to seven fractional digits, e.g. 2026-08-03T13:29:55.1109163+00:00. fromisoformat's tolerance for long fractions varies across Python versions, so the fraction is trimmed to microseconds first. """ text = stamp.strip() if text.endswith('Z'): text = text[:-1] + '+00:00' text = re.sub(r'\.(\d{1,6})\d*', r'.\1', text, count=1) parsed = datetime.fromisoformat(text) if parsed.tzinfo is None: parsed = parsed.replace(tzinfo=timezone.utc) return parsed def canonical(stamp: str) -> str: """Re-render an API timestamp into the one form StartDate accepts. The API returns event.ingested as +00:00-offset ISO 8601 but rejects that same shape as a StartDate value with 400 "Invalid Start date format": it accepts only Z-suffixed values. Every timestamp that came from the API must pass through here before being sent back or persisted. """ return rfc3339(parse_iso(stamp)) def event_id(evt: dict) -> str: """Return the event's identity for deduplication. Falls back to a content hash when event.id is absent, so an id-less event still deduplicates instead of being re-ingested on every boundary re-read. """ explicit = str((evt.get('event') or {}).get('id') or '') if explicit: return explicit digest = hashlib.sha256( json.dumps(evt, sort_keys=True, ensure_ascii=False).encode('utf-8') ).hexdigest() return f'sha256:{digest}' def event_ingested(evt: dict) -> str: """Return the Elastic ingestion timestamp, which is what StartDate filters on.""" return str((evt.get('event') or {}).get('ingested') or '') def next_millisecond(stamp: str) -> str: """Return the canonical timestamp one millisecond later. Used only when a full batch fits inside a single ingestion millisecond. Without this the cursor cannot move and the collector stalls on that timestamp forever. """ return rfc3339(parse_iso(stamp) + timedelta(milliseconds=1)) @functions_framework.cloud_event def main(cloud_event): """Fetch BeyondTrust EPM events and write them to Cloud Storage as NDJSON. Args: cloud_event: CloudEvent object containing the Pub/Sub message. """ bucket_name = os.environ.get('GCS_BUCKET') prefix = os.environ.get('GCS_PREFIX', 'beyondtrust-epm/') state_key = os.environ.get('STATE_KEY', 'beyondtrust-epm-state.json') api_url = (os.environ.get('BPT_API_URL') or '').rstrip('/') client_id = os.environ.get('CLIENT_ID') client_secret = os.environ.get('CLIENT_SECRET') # Clamp both ends: the API rejects RecordSize outside 1 to 1000, and a # misconfigured 0 would otherwise crash-loop on a 400 every run. record_size = min(max(int(os.environ.get('RECORD_SIZE', '1000')), 1), MAX_RECORD_SIZE) max_batches = int(os.environ.get('MAX_BATCHES', '50')) lookback_hours = int(os.environ.get('LOOKBACK_HOURS', '24')) if not all([bucket_name, api_url, client_id, client_secret]): raise RuntimeError( 'Missing required environment variables: ' 'GCS_BUCKET, BPT_API_URL, CLIENT_ID, CLIENT_SECRET' ) bucket = storage_client.bucket(bucket_name) state = load_state(bucket, state_key) # StartDate is a rising cursor on the Elastic ingestion timestamp, not a closed # window. There is no EndDate on this endpoint. if state.get('last_ingested'): # canonical() also repairs state written by the previous script revision, # which persisted the API's raw +00:00 form that StartDate rejects. try: start_date = canonical(state['last_ingested']) except (ValueError, TypeError, AttributeError) as e: raise RuntimeError( f'Unparseable last_ingested in gs://{bucket_name}/{state_key}: ' f'{state["last_ingested"]!r}. Fix or delete that object; deleting ' f'restarts collection from the lookback window.' ) from e else: start_date = rfc3339(datetime.now(timezone.utc) - timedelta(hours=lookback_hours)) seen_ids = set(state.get('seen_ids', [])) print(f'Collecting events ingested from {start_date}') token = get_oauth_token(api_url, client_id, client_secret) fresh, cursor, drained = fetch_events( api_url, token, start_date, record_size, max_batches, seen_ids ) if not fresh: print('No new events. Cursor left unchanged.') return # Everything that can fail is computed before the upload: a crash between the # upload and save_state replays the batch on the next run, so the window where # side effects exist without recorded state must stay minimal. The retained ids # share the cursor millisecond, because those are exactly the ones the # inclusive StartDate will return again. retained = { event_id(e) for e in fresh if event_ingested(e) and canonical(event_ingested(e)) == cursor } if cursor == start_date: # The cursor millisecond did not advance, so ids retained by earlier runs # are still on the boundary; dropping them would re-ingest their events. retained |= seen_ids timestamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ') # The random suffix keeps concurrent executions (Pub/Sub delivers at least # once) from overwriting each other's object within the same second. filename = ( f'{prefix}beyondtrust-epm-events-{timestamp}-' f'{uuid.uuid4().hex[:8]}.ndjson' ) ndjson = '\n'.join(json.dumps(e, ensure_ascii=False) for e in fresh) + '\n' bucket.blob(filename).upload_from_string( ndjson, content_type='application/x-ndjson' ) print(f'Wrote {len(fresh)} events to gs://{bucket_name}/{filename}') save_state(bucket, state_key, { 'last_ingested': cursor, 'seen_ids': sorted(retained), 'updated_at': rfc3339(datetime.now(timezone.utc)), }) if not drained: print( f'Stopped after {max_batches} batches with more events available. ' 'The cursor advanced, so the next run continues from here.' ) def fetch_events(api_url, token, start_date, record_size, max_batches, seen_ids): """Read forward from start_date until the API returns a short batch. The response envelope has no page count and no next-page token, so a batch shorter than record_size is the only documented end-of-data signal. StartDate is inclusive and the cursor lands on the newest event's timestamp, so every batch re-returns the events sharing it. Deduplication by event id therefore has to happen as batches arrive, not only between runs. Returns: Tuple of (new events, cursor to persist, whether the stream was drained). Raises: FetchError: on any API or transport failure, so the caller cannot mistake a failed fetch for an empty result and advance the cursor. """ headers = { 'Authorization': f'Bearer {token}', 'Accept': 'application/json', } all_events = [] seen = set(seen_ids) cursor = start_date for batch in range(1, max_batches + 1): query = urlencode({'StartDate': cursor, 'RecordSize': record_size}) body = request_with_retry(f'{api_url}{EVENTS_PATH}?{query}', headers) events = body.get('events') or [] new = [e for e in events if event_id(e) not in seen] seen.update(event_id(e) for e in events if event_id(e)) all_events.extend(new) print(f'Batch {batch}: {len(events)} events, {len(new)} new') # Termination is judged on what the API returned, not on what survived # deduplication: a batch can be full and still be entirely duplicates. # The newest stamp in the batch, not events[-1]: ordering is a client-side # convention, not a documented guarantee, and a misordered tail would # regress the cursor and re-ingest events already written. stamps = [canonical(s) for s in (event_ingested(e) for e in events) if s] if len(events) < record_size: # Short batch: the stream is drained. if events and not stamps: print( 'Warning: no event in the final batch carries event.ingested; ' 'cursor left unchanged.' ) if stamps: cursor = max(max(stamps), cursor) return all_events, cursor, True if not stamps: raise FetchError( 'No event in a full batch carries event.ingested, ' 'so the cursor cannot advance' ) batch_max = max(stamps) if batch_max <= cursor: # A full batch fits inside one ingestion millisecond. Stepping past it is the only # way to make progress; holding the cursor here stalls collection forever. # Events beyond record_size at that exact millisecond are unreachable, which # needs more than 1000 events in one millisecond. print( f'Warning: a full batch shares ingestion timestamp {batch_max}; ' 'stepping past it. Events beyond RecordSize at that timestamp are skipped.' ) cursor = next_millisecond(cursor) else: cursor = batch_max return all_events, cursor, False def request_with_retry(url, headers, attempts=4): """GET with backoff on 429 and 5xx. The documented rate limit is 1000 requests per 100 seconds. """ backoff = 1.0 for attempt in range(1, attempts + 1): try: response = http.request('GET', url, headers=headers) except Exception as e: raise FetchError(f'Request to {url} failed: {e}') from e if response.status in (429, 500, 502, 503, 504) and attempt < attempts: retry_after = response.headers.get('Retry-After') try: delay = int(retry_after) if retry_after else backoff except (TypeError, ValueError): delay = backoff # Retry-After is server-controlled input: bound it so a bogus value # cannot sleep past the function timeout or crash time.sleep. delay = min(max(delay, 1.0), 60.0) print(f'HTTP {response.status}. Retrying in {delay}s...') time.sleep(delay) backoff = min(backoff * 2, 30.0) continue if response.status != 200: raise FetchError( f'Request failed: {response.status} {response.data.decode("utf-8")}' ) text = response.data.decode('utf-8') # BeyondTrust's sample folds "Owner" into "owner" before parsing, but that # workaround exists for PowerShell's case-insensitive ConvertFrom-Json. # Python parses case-sensitively, and the SecOps parser maps file.Owner.* # and file.owner to different UDM fields, so both keys must survive. try: return json.loads(text) except json.JSONDecodeError as e: raise FetchError(f'Malformed JSON response: {e}') from e raise FetchError(f'Giving up on {url} after {attempts} attempts') def get_oauth_token(api_url, client_id, client_secret): """Get an access token using the OAuth client credentials flow. The token is valid for one hour. """ body = urlencode({ 'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret, 'scope': OAUTH_SCOPE, }) response = http.request( 'POST', f'{api_url}{TOKEN_PATH}', body=body, headers={'Content-Type': 'application/x-www-form-urlencoded'}, ) if response.status != 200: raise FetchError( f'Token request failed: {response.status} ' f'{response.data.decode("utf-8")}' ) return json.loads(response.data.decode('utf-8'))['access_token'] def load_state(bucket, key): """Read the collector state from Cloud Storage. Only a missing object is a cold start. Any other error is raised: swallowing it would reset collection to the lookback window and re-ingest that period. """ blob = bucket.blob(key) try: return json.loads(blob.download_as_text()) except NotFound: print('No state file found. Starting from the lookback window.') return {} def save_state(bucket, key, state): """Write the collector state to Cloud Storage. Failures are raised, not logged. A run that cannot record its cursor must fail, otherwise the next run repeats the same window. """ bucket.blob(key).upload_from_string( json.dumps(state, indent=2), content_type='application/json' ) print(f"Saved state: last_ingested={state.get('last_ingested')}")- Segundo archivo: requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0Haz clic en Implementar para guardar y, luego, implementar la función.
Espera a que se complete la implementación (de 2 a 3 minutos).
Crea un trabajo de Cloud Scheduler
Cloud Scheduler publica mensajes en el tema de Pub/Sub a intervalos regulares, lo que activa la función de Cloud Run.
- En la consola Google Cloud , ve a Cloud Scheduler.
- Haz clic en Crear trabajo.
Proporciona los siguientes detalles de configuración:
Parámetro de configuración Valor Nombre beyondtrust-epm-collector-hourlyRegión Selecciona la misma región que la Cloud Run Function. Frecuencia 0 * * * *(cada hora en punto)Zona horaria Selecciona la zona horaria (se recomienda UTC) Tipo de objetivo Pub/Sub Tema Selecciona el tema beyondtrust-epm-trigger.Cuerpo del mensaje {}(objeto JSON vacío)Haz clic en Crear.
Opciones de frecuencia de programación
Elige la frecuencia según los requisitos de volumen y latencia de los registros:
| Frecuencia | Expresión cron | Caso de uso |
|---|---|---|
| Cada 5 minutos | */5 * * * * |
Alto volumen y baja latencia |
| Cada 15 minutos | */15 * * * * |
Volumen medio |
| Cada 1 hora | 0 * * * * |
Estándar (recomendado) |
| Cada 6 horas | 0 */6 * * * |
Procesamiento por lotes de bajo volumen |
| Diario | 0 0 * * * |
Recopilación de datos históricos |
Prueba el trabajo del programador
- En la consola de Cloud Scheduler, busca tu trabajo.
- Haz clic en Forzar ejecución para activar el DAG de forma manual.
- Espera unos segundos y ve a Cloud Run > Servicios > beyondtrust-epm-collector > Registros.
- Verifica que la función se haya ejecutado correctamente.
- Revisa el bucket de Cloud Storage para confirmar que se escribieron los registros.
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
- Ve a Configuración de SIEM > Feeds.
- Haz clic en Add New Feed.
- Haz clic en Configura un feed único.
- En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo,
BeyondTrust EPM logs). - Selecciona Google Cloud Storage V2 como el Tipo de fuente.
- Selecciona BeyondTrust Endpoint Privilege Management 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 para usarla en el siguiente paso.
Otorga permisos de IAM a la cuenta de servicio de Google SecOps
La cuenta de servicio de Google SecOps necesita dos roles en tu bucket de Cloud Storage: Visualizador de objetos de Storage para leer los objetos de registro y un rol a nivel del bucket para leer los metadatos del bucket.
- Ve a Cloud Storage > Buckets.
- Haz clic en el nombre de tu bucket.
- Ve a la pestaña Permisos.
- Haz clic en Otorgar acceso.
- 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 las siguientes opciones:
- Visualizador de objetos de Storage: Lee los objetos de registro.
- Storage Legacy Bucket Reader: Lee los metadatos del bucket. Si seleccionaste la opción de borrado Borrar los archivos transferidos y los directorios vacíos, selecciona Storage Legacy Bucket Writer, que también otorga el permiso de borrado.
Haz clic en Guardar.
Configura un feed en Google SecOps para transferir registros de BeyondTrust EPM
- Ve a Configuración de SIEM > Feeds.
- Haz clic en Add New Feed.
- Haz clic en Configura un feed único.
- En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo,
BeyondTrust EPM logs). - Selecciona Google Cloud Storage V2 como el Tipo de fuente.
- Selecciona BeyondTrust Endpoint Privilege Management como el Tipo de registro.
- Haz clic en Siguiente.
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://beyondtrust-epm-logs/beyondtrust-epm/Reemplaza lo siguiente:
beyondtrust-epm-logs: Es el nombre de tu bucket de Cloud Storage.beyondtrust-epm/: Es el prefijo o la ruta de carpeta opcional en la que se almacenan los registros (déjalo vacío para la raíz).
Ejemplos:
- Bucket raíz:
gs://beyondtrust-epm-logs/ - Con prefijo:
gs://beyondtrust-epm-logs/beyondtrust-epm/
- Bucket raíz:
Opción de eliminación del código fuente: Selecciona la opción de eliminación según tu preferencia:
- No borrar archivos nunca: Nunca borra archivos de la fuente. Esto se recomienda para fines de prueba.
Borrar los archivos transferidos y los directorios vacíos: Borra los archivos y los directorios vacíos de la fuente después de que se complete una recuperación 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 transferencia: 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.
Tabla de asignación de UDM
| Campo de registro | Asignación de UDM | Lógica |
|---|---|---|
DomainIdentifier_label |
additional.fields |
Combinado |
WatsonEventType_label |
additional.fields |
Combinado |
action_label |
additional.fields |
Combinado |
activity_id_label |
additional.fields |
Combinado |
app_id_label |
additional.fields |
Combinado |
app_insights_instrumentation_key_label |
additional.fields |
Combinado |
asimov_instrumentation_key_label |
additional.fields |
Combinado |
authorization_request_control_authorization_label |
additional.fields |
Combinado |
bundle_name_label |
additional.fields |
Combinado |
changedBy_label |
additional.fields |
Combinado |
code_signature_subject_name_label |
additional.fields |
Combinado |
collector_api_key_label |
additional.fields |
Combinado |
command_label |
additional.fields |
Combinado |
configuration_application_group_description_label |
additional.fields |
Combinado |
configuration_application_group_identifier_label |
additional.fields |
Combinado |
configuration_application_group_name_label |
additional.fields |
Combinado |
configuration_application_identifier_label |
additional.fields |
Combinado |
configuration_application_type_label |
additional.fields |
Combinado |
configuration_identifier_label |
additional.fields |
Combinado |
configuration_message_identifier_label |
additional.fields |
Combinado |
configuration_message_name_label |
additional.fields |
Combinado |
configuration_message_type_label |
additional.fields |
Combinado |
configuration_name_label |
additional.fields |
Combinado |
configuration_revision_number_label |
additional.fields |
Combinado |
configuration_rule_identifier_label |
additional.fields |
Combinado |
configuration_rule_on_demand_label |
additional.fields |
Combinado |
configuration_rule_script_outcome_rule_affected_label |
additional.fields |
Combinado |
configuration_token_identifier_label |
additional.fields |
Combinado |
configuration_token_name_label |
additional.fields |
Combinado |
configuration_workstyle_description_label |
additional.fields |
Combinado |
configuration_workstyle_identifier_label |
additional.fields |
Combinado |
configuration_workstyle_name_label |
additional.fields |
Combinado |
content_length_label |
additional.fields |
Combinado |
content_type_label |
additional.fields |
Combinado |
domainNetBIOSName_label |
additional.fields |
Combinado |
domain_label |
additional.fields |
Combinado |
entity_label |
additional.fields |
Combinado |
entity_name_label |
additional.fields |
Combinado |
event_action_label |
additional.fields |
Combinado |
exitstatus_label |
additional.fields |
Combinado |
file_hash_md5_label |
additional.fields |
Combinado |
file_hash_sha1_label |
additional.fields |
Combinado |
file_hash_sha256_label |
additional.fields |
Combinado |
file_version_label |
additional.fields |
Combinado |
gid_label |
additional.fields |
Combinado |
group_data_id_label |
additional.fields |
Combinado |
group_id_label |
additional.fields |
Combinado |
group_label |
additional.fields |
Combinado |
handle_label |
additional.fields |
Combinado |
host_name_label |
additional.fields |
Combinado |
host_uptime_labels |
additional.fields |
Combinado |
http_host_label |
additional.fields |
Combinado |
id_label |
additional.fields |
Combinado |
iolog_label |
additional.fields |
Combinado |
is_opted_in_label |
additional.fields |
Combinado |
linenum_label |
additional.fields |
Combinado |
local_identifier_label |
additional.fields |
Combinado |
locale_label |
additional.fields |
Combinado |
master_utcoffset_label |
additional.fields |
Combinado |
masterlocale_label |
additional.fields |
Combinado |
owner_identifier_label |
additional.fields |
Combinado |
parent_entity_id_label |
additional.fields |
Combinado |
parent_process_exec_label |
additional.fields |
Combinado |
parent_process_label |
additional.fields |
Combinado |
pbmasterdnodename_label |
additional.fields |
Combinado |
pipeName_label |
additional.fields |
Combinado |
process_entity_id_label |
additional.fields |
Combinado |
process_hash_label |
additional.fields |
Combinado |
process_name_label |
additional.fields |
Combinado |
process_parent_name_label |
additional.fields |
Combinado |
process_start_time_label |
additional.fields |
Combinado |
processexe_label |
additional.fields |
Combinado |
product_label |
additional.fields |
Combinado |
product_type_label |
additional.fields |
Combinado |
product_version_label |
additional.fields |
Combinado |
requestuser_label |
additional.fields |
Combinado |
runargv_label |
additional.fields |
Combinado |
runcwd_label |
additional.fields |
Combinado |
runeffectivegroup_label |
additional.fields |
Combinado |
runeffectiveuser_label |
additional.fields |
Combinado |
runhost_label |
additional.fields |
Combinado |
schema_version_label |
additional.fields |
Combinado |
sku_name_label |
additional.fields |
Combinado |
telemetry_level_label |
additional.fields |
Combinado |
tenant_id_label |
additional.fields |
Combinado |
type_label |
additional.fields |
Combinado |
uid_label |
additional.fields |
Combinado |
user_id_label |
additional.fields |
Combinado |
user_name_label |
additional.fields |
Combinado |
vs_exe_version_label |
additional.fields |
Combinado |
inter_host |
intermediary.hostname |
Asignada directamente |
Processes.description |
metadata.description |
Asignada directamente |
details |
metadata.description |
Asignada directamente |
event_data.reason |
metadata.description |
Asignada directamente |
file.pe.description |
metadata.description |
Asignada directamente |
created |
metadata.event_timestamp |
Se analizó como ISO8601 |
datetime |
metadata.event_timestamp |
Se analizó como MMM dd HH:mm:ss |
has_principal |
metadata.event_type |
Asignado: true → STATUS_UPDATE |
has_user |
metadata.event_type |
Asignado: true → USER_UNCATEGORIZED |
parent_working_directory_label |
metadata.ingestion_labels |
Combinado |
working_directory_label |
metadata.ingestion_labels |
Combinado |
auditType |
metadata.product_event_type |
Asignada directamente |
event_datas.ActionId |
metadata.product_log_id |
Asignada directamente |
labels.related_item_id |
metadata.product_log_id |
Asignada directamente |
uniqueid |
metadata.product_log_id |
Asignada directamente |
masterdversion |
metadata.product_version |
Asignada directamente |
headers.http_version |
network.application_protocol_version |
Asignada directamente |
headers.request_method |
network.http.method |
Asignada directamente |
host.os.platform |
principal.administrative_domain |
Asignada directamente |
Processes.process |
principal.application |
Asignada directamente |
agent_ephemeral_id_label |
principal.asset.attribute.labels |
Combinado |
agent_id_label |
principal.asset.attribute.labels |
Combinado |
agent_version_label |
principal.asset.attribute.labels |
Combinado |
ecs_version_label |
principal.asset.attribute.labels |
Combinado |
_hardware |
principal.asset.hardware |
Combinado |
host.hostname |
principal.asset.hostname |
Asignada directamente |
submithost |
principal.asset.hostname |
Asignada directamente |
ip_address |
principal.asset.ip |
Combinado |
masterhostip |
principal.asset.ip |
Combinado |
submithostip |
principal.asset.ip |
Combinado |
file.path |
principal.file.full_path |
Asignada directamente |
lineinfile |
principal.file.full_path |
Asignada directamente |
host.hostname |
principal.hostname |
Asignada directamente |
submithost |
principal.hostname |
Asignada directamente |
ip_address |
principal.ip |
Combinado |
masterhostip |
principal.ip |
Combinado |
submithostip |
principal.ip |
Combinado |
mac |
principal.mac |
Combinado |
host.os.name |
principal.platform_version |
Asignada directamente |
host.os.version |
principal.platform_version |
Asignada directamente |
process.command_line |
principal.process.command_line |
Asignada directamente |
runcommand |
principal.process.command_line |
Asignada directamente |
process.executable |
principal.process.file.full_path |
Asignada directamente |
cmd |
principal.process.parent_process.command_line |
Asignada directamente |
Processes.process_path |
principal.process.parent_process.file.full_path |
Asignada directamente |
process.parent.executable |
principal.process.parent_process.file.full_path |
Asignada directamente |
Processes.parent_process_id |
principal.process.parent_process.pid |
Asignada directamente |
Processes.process_id |
principal.process.pid |
Asignada directamente |
logpid |
principal.process.pid |
Asignada directamente |
file.Owner.DomainName |
principal.user.company_name |
Asignada directamente |
file.Owner.Name |
principal.user.user_display_name |
Asignada directamente |
runuser |
principal.user.user_display_name |
Asignada directamente |
userName |
principal.user.user_display_name |
Asignada directamente |
Processes.user |
principal.user.userid |
Asignada directamente |
userId |
principal.user.userid |
Asignada directamente |
userid |
principal.user.userid |
Asignada directamente |
Processes.user_id |
principal.user.windows_sid |
Asignada directamente |
EPMWinMac.Configuration.Rule.Action |
security_result.action |
Combinado |
security_result_action |
security_result.action |
Combinado |
event_data.outcome |
security_result.category_details |
Combinado |
host.os.version |
security_result.category_details |
Combinado |
EPMWinMac.Configuration.Application.Description |
security_result.description |
Asignada directamente |
EPMWinMac.Configuration.Message.Description |
security_result.description |
Asignada directamente |
host.os.type |
src.administrative_domain |
Asignada directamente |
file.name |
src.file.names |
Combinado |
host.os.full |
src.platform_version |
Asignada directamente |
host.os.family |
target.administrative_domain |
Asignada directamente |
Processes.dest |
target.asset.hostname |
Asignada directamente |
file.extension |
target.file.mime_type |
Asignada directamente |
Processes.dest |
target.hostname |
Asignada directamente |
host.domain |
target.hostname |
Asignada directamente |
file_DriveType_label |
target.resource.attribute.labels |
Combinado |
file_drive_letter_label |
target.resource.attribute.labels |
Combinado |
owner_label |
target.resource.attribute.labels |
Combinado |
| N/A | metadata.event_type |
Constante: USER_UNCATEGORIZED |
| N/A | metadata.product_name |
Constante: Beyondtrust Privilege Management |
| N/A | metadata.vendor_name |
Constante: Beyondtrust Privilege Management |
| N/A | network.application_protocol |
Constante: HTTP |
| N/A | principal.platform |
Constante: MAC |
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.