Recopila registros de TeamViewer
En este documento, se explica cómo transferir registros de TeamViewer a Google Security Operations con Amazon S3. El analizador extrae los eventos de auditoría de los registros con formato JSON. Itera los detalles del evento, asigna propiedades específicas a los campos del Modelo de datos unificado (UDM), controla la información del participante y del presentador, y categoriza los eventos según la actividad del usuario. El analizador también realiza transformaciones de datos, como la combinación de etiquetas y la conversión de marcas de tiempo a un formato estandarizado.
Antes de comenzar
Asegúrate de cumplir con los siguientes requisitos previos:
- Es una instancia de Google SecOps.
- Acceso con privilegios a TeamViewer
- Acceso con privilegios a AWS (S3, Identity and Access Management [IAM], Lambda, EventBridge).
Obtén los requisitos previos de TeamViewer
- Accede a la consola de administración de TeamViewer como administrador.
- Ve a Mi perfil > Apps.
- Haz clic en Crear aplicación.
- Proporciona los siguientes detalles de configuración:
- Nombre de la app: Ingresa un nombre descriptivo (por ejemplo,
Google SecOps Integration
). - Descripción: Ingresa una descripción para la app.
- Permisos: Selecciona los permisos para acceder a los registros de auditoría.
- Nombre de la app: Ingresa un nombre descriptivo (por ejemplo,
- Haz clic en Crear y guarda las credenciales de API generadas en una ubicación segura.
- Registra la URL base de la API de TeamViewer (por ejemplo,
https://webapi.teamviewer.com/api/v1
). - Copia y guarda en una ubicación segura los siguientes detalles:
- CLIENT_ID
- CLIENT_SECRET
- API_BASE_URL
Configura el bucket de AWS S3 y el IAM para Google SecOps
- Crea un bucket de Amazon S3 siguiendo esta guía del usuario: Crea un bucket
- Guarda el Nombre y la Región del bucket para futuras referencias (por ejemplo,
teamviewer-logs
). - Crea un usuario siguiendo esta guía del usuario: Cómo crear un usuario de IAM.
- Selecciona el usuario creado.
- Selecciona la pestaña Credenciales de seguridad.
- Haz clic en Crear clave de acceso en la sección Claves de acceso.
- Selecciona Servicio de terceros como Caso de uso.
- Haz clic en Siguiente.
- Opcional: Agrega una etiqueta de descripción.
- Haz clic en Crear clave de acceso.
- Haz clic en Descargar archivo CSV para guardar la clave de acceso y la clave de acceso secreta para consultarlas en el futuro.
- Haz clic en Listo.
- Selecciona la pestaña Permisos.
- Haz clic en Agregar permisos en la sección Políticas de permisos.
- Selecciona Agregar permisos.
- Selecciona Adjuntar políticas directamente.
- Busca la política AmazonS3FullAccess.
- Selecciona la política.
- Haz clic en Siguiente.
- Haz clic en Agregar permisos.
Configura la política y el rol de IAM para las cargas de S3
- En la consola de AWS, ve a IAM > Políticas.
- Haz clic en Crear política > pestaña JSON.
- Copia y pega la siguiente política.
JSON de la política (reemplaza
teamviewer-logs
si ingresaste un nombre de bucket diferente):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::teamviewer-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::teamviewer-logs/teamviewer/audit/state.json" } ] }
Haz clic en Siguiente > Crear política.
Ve a IAM > Roles > Crear rol > Servicio de AWS > Lambda.
Adjunta la política recién creada.
Asigna el nombre
TeamViewerToS3Role
al rol y haz clic en Crear rol.
Crea la función Lambda
- En la consola de AWS, ve a Lambda > Functions > Create function.
- Haz clic en Author from scratch.
Proporciona los siguientes detalles de configuración:
Configuración Valor Nombre teamviewer_to_s3
Tiempo de ejecución Python 3.13 Arquitectura x86_64 Rol de ejecución TeamViewerToS3Role
Después de crear la función, abre la pestaña Code, borra el código auxiliar y pega el siguiente código (
teamviewer_to_s3.py
).#!/usr/bin/env python3 # Lambda: Pull TeamViewer audit logs and store raw JSON payloads to S3 # - Time window via {FROM}/{TO} placeholders (UTC ISO8601), URL-encoded. # - Preserves vendor-native JSON format for audit and session data. # - Retries with exponential backoff; unique S3 keys to avoid overwrites. import os, json, time, uuid, urllib.parse 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", "teamviewer/audit/") STATE_KEY = os.environ.get("STATE_KEY", "teamviewer/audit/state.json") WINDOW_SEC = int(os.environ.get("WINDOW_SECONDS", "3600")) # default 1h HTTP_TIMEOUT= int(os.environ.get("HTTP_TIMEOUT", "60")) API_BASE_URL = os.environ["API_BASE_URL"] CLIENT_ID = os.environ["CLIENT_ID"] CLIENT_SECRET = os.environ["CLIENT_SECRET"] MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "3")) USER_AGENT = os.environ.get("USER_AGENT", "teamviewer-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 _iso(ts: float) -> str: return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts)) def _get_access_token() -> str: # OAuth2 Client Credentials flow for TeamViewer API token_url = f"{API_BASE_URL.rstrip('/')}/oauth2/token" data = urllib.parse.urlencode({ 'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET }).encode('utf-8') req = Request(token_url, data=data, method="POST") req.add_header("Content-Type", "application/x-www-form-urlencoded") req.add_header("User-Agent", USER_AGENT) with urlopen(req, timeout=HTTP_TIMEOUT) as r: response = json.loads(r.read()) return response["access_token"] def _build_audit_url(from_ts: float, to_ts: float, access_token: str) -> str: # Build URL for TeamViewer audit API endpoint base_endpoint = f"{API_BASE_URL.rstrip('/')}/reports/connections" params = { "from_date": _iso(from_ts), "to_date": _iso(to_ts) } query_string = urllib.parse.urlencode(params) return f"{base_endpoint}?{query_string}" def _fetch_audit_data(url: str, access_token: str) -> tuple[bytes, str]: attempt = 0 while True: req = Request(url, method="GET") req.add_header("User-Agent", USER_AGENT) req.add_header("Authorization", f"Bearer {access_token}") req.add_header("Accept", "application/json") try: with urlopen(req, timeout=HTTP_TIMEOUT) as r: return r.read(), (r.headers.get("Content-Type") or "application/json") 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 _put_audit_data(blob: bytes, content_type: str, from_ts: float, to_ts: float) -> str: # Create unique S3 key for audit data ts_path = time.strftime("%Y/%m/%d", time.gmtime(to_ts)) uniq = f"{int(time.time()*1e6)}_{uuid.uuid4().hex[:8]}" key = f"{S3_PREFIX}{ts_path}/teamviewer_audit_{int(from_ts)}_{int(to_ts)}_{uniq}.json" s3.put_object( Bucket=S3_BUCKET, Key=key, Body=blob, ContentType=content_type, Metadata={ 'source': 'teamviewer-audit', 'from_timestamp': str(int(from_ts)), 'to_timestamp': str(int(to_ts)) } ) 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 # Get OAuth2 access token access_token = _get_access_token() url = _build_audit_url(from_ts, to_ts, access_token) print(f"Fetching TeamViewer audit data from: {url}") blob, ctype = _fetch_audit_data(url, access_token) # Validate that we received valid JSON data try: audit_data = json.loads(blob) print(f"Successfully retrieved {len(audit_data.get('records', []))} audit records") except json.JSONDecodeError as e: print(f"Warning: Invalid JSON received: {e}") key = _put_audit_data(blob, ctype, 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": ctype, "from_timestamp": from_ts, "to_timestamp": to_ts } } if __name__ == "__main__": print(lambda_handler())
Ve a Configuration > Environment variables.
Haz clic en Editar > Agregar nueva variable de entorno.
Ingresa las variables de entorno que se proporcionan en la siguiente tabla y reemplaza los valores de ejemplo por tus valores.
Variables de entorno
Clave Valor de ejemplo S3_BUCKET
teamviewer-logs
S3_PREFIX
teamviewer/audit/
STATE_KEY
teamviewer/audit/state.json
WINDOW_SECONDS
3600
HTTP_TIMEOUT
60
MAX_RETRIES
3
USER_AGENT
teamviewer-to-s3/1.0
API_BASE_URL
https://webapi.teamviewer.com/api/v1
CLIENT_ID
your-client-id
(del paso 2)CLIENT_SECRET
your-client-secret
(del paso 2)Después de crear la función, permanece en su página (o abre Lambda > Funciones > tu-función).
Selecciona la pestaña Configuración.
En el panel Configuración general, haz clic en Editar.
Cambia Tiempo de espera a 5 minutos (300 segundos) y haz clic en Guardar.
Crea una programación de EventBridge
- Ve a Amazon EventBridge > Scheduler > Create schedule.
- Proporciona los siguientes detalles de configuración:
- Programación recurrente: Frecuencia (
1 hour
) - Destino: Tu función Lambda
teamviewer_to_s3
. - Nombre:
teamviewer-audit-1h
.
- Programación recurrente: Frecuencia (
- Haz clic en Crear programación.
(Opcional) Crea un usuario y claves de IAM de solo lectura para Google SecOps
- Ve a Consola de AWS > IAM > Usuarios > Agregar usuarios.
- Haz clic en Agregar usuarios.
- Proporciona los siguientes detalles de configuración:
- Usuario: Ingresa
secops-reader
. - Tipo de acceso: Selecciona Clave de acceso: Acceso programático.
- Usuario: Ingresa
- Haz clic en Crear usuario.
- Adjunta una política de lectura mínima (personalizada): Usuarios > secops-reader > Permisos > Agregar permisos > Adjuntar políticas directamente > Crear política.
JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::teamviewer-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::teamviewer-logs" } ] }
Nombre =
secops-reader-policy
.Haz clic en Crear política > busca o selecciona > Siguiente > Agregar permisos.
Crea una clave de acceso para
secops-reader
: Credenciales de seguridad > Claves de acceso.Haz clic en Crear clave de acceso.
Descarga el
CSV
. (Pegarás estos valores en el feed).
Configura un feed en Google SecOps para transferir registros de TeamViewer
- Ve a Configuración de SIEM > Feeds.
- Haz clic en + Agregar feed nuevo.
- En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo,
TeamViewer logs
). - Selecciona Amazon S3 V2 como el Tipo de fuente.
- Selecciona TeamViewer como el Tipo de registro.
- Haz clic en Siguiente.
- Especifica valores para los siguientes parámetros de entrada:
- URI de S3:
s3://teamviewer-logs/teamviewer/audit/
- Opciones de borrado de la fuente: Selecciona la opción de borrado según tu preferencia.
- 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.
- ID de clave de acceso: Clave de acceso del usuario con acceso al bucket de S3.
- Clave de acceso secreta: Clave secreta del usuario con acceso al bucket de S3.
- Espacio de nombres del recurso: Es el espacio de nombres del recurso.
- Etiquetas de transmisión: Es la etiqueta que se aplica a los eventos de este feed.
- URI de S3:
- 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 |
---|---|---|
AffectedItem |
metadata.product_log_id |
El valor de AffectedItem del registro sin procesar se asigna directamente a este campo de UDM. |
EventDetails.NewValue |
principal.resource.attribute.labels.value |
Si PropertyName contiene (server) , NewValue se usa como el valor de una etiqueta en principal.resource.attribute.labels . |
EventDetails.NewValue |
principal.user.user_display_name |
Si PropertyName es Name of participant , se usa NewValue como el nombre visible del usuario para el principal. |
EventDetails.NewValue |
principal.user.userid |
Si PropertyName es ID of participant , se usa NewValue como ID de usuario para el principal. |
EventDetails.NewValue |
security_result.about.labels.value |
Para todos los demás valores de PropertyName (excepto los que se controlan con condiciones específicas), se usa NewValue como el valor de una etiqueta dentro del array security_result.about.labels . |
EventDetails.NewValue |
target.file.full_path |
Si PropertyName es Source file , se usa NewValue como la ruta de acceso completa para el archivo de destino. |
EventDetails.NewValue |
target.resource.attribute.labels.value |
Si PropertyName contiene (client) , NewValue se usa como el valor de una etiqueta en target.resource.attribute.labels . |
EventDetails.NewValue |
target.user.user_display_name |
Si PropertyName es Name of presenter , se analiza NewValue . Si es un número entero, se descarta. De lo contrario, se usa como el nombre visible del usuario para el destino. |
EventDetails.NewValue |
target.user.userid |
Si PropertyName es ID of presenter , se usa NewValue como ID de usuario para el objetivo. |
EventDetails.PropertyName |
principal.resource.attribute.labels.key |
Si PropertyName contiene (server) , PropertyName se usa como la clave de una etiqueta en principal.resource.attribute.labels . |
EventDetails.PropertyName |
security_result.about.labels.key |
Para todos los demás valores de PropertyName (excepto los que se controlan con condiciones específicas), se usa PropertyName como clave de una etiqueta dentro del array security_result.about.labels . |
EventDetails.PropertyName |
target.resource.attribute.labels.key |
Si PropertyName contiene (client) , PropertyName se usa como la clave de una etiqueta en target.resource.attribute.labels . |
EventName |
metadata.product_event_type |
El valor de EventName del registro sin procesar se asigna directamente a este campo de UDM. |
Timestamp |
metadata.event_timestamp |
El valor de Timestamp del registro sin procesar se analiza y se usa como la marca de tiempo del evento en los metadatos. Se establece en USER_UNCATEGORIZED si src_user (derivado de ID of participant ) no está vacío; de lo contrario, se establece en USER_RESOURCE_ACCESS . Se codificó como TEAMVIEWER . Se codificó como TEAMVIEWER . Se codificó como TEAMVIEWER . |
¿Necesitas más ayuda? Obtén respuestas de miembros de la comunidad y profesionales de Google SecOps.