Raccogliere i log di TeamViewer
Questo documento spiega come importare i log di TeamViewer in Google Security Operations utilizzando Amazon S3. Il parser estrae gli eventi di controllo dai log in formato JSON. Itera i dettagli degli eventi, mappando proprietà specifiche ai campi Unified Data Model (UDM), gestendo le informazioni di partecipanti e relatori e classificando gli eventi in base all'attività utente. Il parser esegue anche trasformazioni dei dati, ad esempio unisce le etichette e converte i timestamp in un formato standardizzato.
Prima di iniziare
Assicurati di soddisfare i seguenti prerequisiti:
- Un'istanza Google SecOps.
- Accesso privilegiato a TeamViewer.
- Accesso privilegiato ad AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).
Requisiti di TeamViewer
- Accedi alla console di gestione di TeamViewer come amministratore.
- Vai a Il mio profilo > App.
- Fai clic su Crea app.
- Fornisci i seguenti dettagli di configurazione:
- Nome app: inserisci un nome descrittivo (ad esempio,
Google SecOps Integration
). - Descrizione: inserisci la descrizione dell'app.
- Autorizzazioni: seleziona le autorizzazioni per l'accesso ai log di controllo.
- Nome app: inserisci un nome descrittivo (ad esempio,
- Fai clic su Crea e salva le credenziali API generate in una posizione sicura.
- Registra l'URL di base dell'API TeamViewer (ad esempio,
https://webapi.teamviewer.com/api/v1
). - Copia e salva in una posizione sicura i seguenti dettagli:
- CLIENT_ID
- CLIENT_SECRET
- API_BASE_URL
Configura il bucket AWS S3 e IAM per Google SecOps
- Crea un bucket Amazon S3 seguendo questa guida utente: Creazione di un bucket
- Salva il nome e la regione del bucket per riferimento futuro (ad esempio,
teamviewer-logs
). - Crea un utente seguendo questa guida utente: Creazione di un utente IAM.
- Seleziona l'utente creato.
- Seleziona la scheda Credenziali di sicurezza.
- Fai clic su Crea chiave di accesso nella sezione Chiavi di accesso.
- Seleziona Servizio di terze parti come Caso d'uso.
- Fai clic su Avanti.
- (Facoltativo) Aggiungi il tag della descrizione.
- Fai clic su Crea chiave di accesso.
- Fai clic su Scarica file CSV per salvare la chiave di accesso e la chiave di accesso segreta per riferimento futuro.
- Fai clic su Fine.
- Seleziona la scheda Autorizzazioni.
- Fai clic su Aggiungi autorizzazioni nella sezione Criteri per le autorizzazioni.
- Seleziona Aggiungi autorizzazioni.
- Seleziona Allega direttamente i criteri.
- Cerca i criteri AmazonS3FullAccess.
- Seleziona la policy.
- Fai clic su Avanti.
- Fai clic su Aggiungi autorizzazioni.
Configura il ruolo e il criterio IAM per i caricamenti S3
- Nella console AWS, vai a IAM > Policy.
- Fai clic su Crea criterio > scheda JSON.
- Copia e incolla i seguenti criteri.
JSON delle policy (sostituisci
teamviewer-logs
se hai inserito un nome del bucket diverso):{ "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" } ] }
Fai clic su Avanti > Crea criterio.
Vai a IAM > Ruoli > Crea ruolo > Servizio AWS > Lambda.
Allega il criterio appena creato.
Assegna al ruolo il nome
TeamViewerToS3Role
e fai clic su Crea ruolo.
Crea la funzione Lambda
- Nella console AWS, vai a Lambda > Funzioni > Crea funzione.
- Fai clic su Crea autore da zero.
Fornisci i seguenti dettagli di configurazione:
Impostazione Valore Nome teamviewer_to_s3
Tempo di esecuzione Python 3.13 Architettura x86_64 Ruolo di esecuzione TeamViewerToS3Role
Dopo aver creato la funzione, apri la scheda Codice, elimina lo stub e incolla il seguente codice (
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())
Vai a Configurazione > Variabili di ambiente.
Fai clic su Modifica > Aggiungi nuova variabile di ambiente.
Inserisci le variabili di ambiente fornite nella tabella seguente sostituendo i valori di esempio con i tuoi valori.
Variabili di ambiente
Chiave Valore di esempio 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
(dal passaggio 2)CLIENT_SECRET
your-client-secret
(dal passaggio 2)Dopo aver creato la funzione, rimani sulla relativa pagina (o apri Lambda > Funzioni > la tua funzione).
Seleziona la scheda Configurazione.
Nel riquadro Configurazione generale, fai clic su Modifica.
Modifica Timeout impostando 5 minuti (300 secondi) e fai clic su Salva.
Creare una pianificazione EventBridge
- Vai a Amazon EventBridge > Scheduler > Crea pianificazione.
- Fornisci i seguenti dettagli di configurazione:
- Programma ricorrente: Tariffa (
1 hour
). - Target: la tua funzione Lambda
teamviewer_to_s3
. - Nome:
teamviewer-audit-1h
- Programma ricorrente: Tariffa (
- Fai clic su Crea pianificazione.
(Facoltativo) Crea chiavi e utenti IAM di sola lettura per Google SecOps
- Vai alla console AWS > IAM > Utenti > Aggiungi utenti.
- Fai clic su Add users (Aggiungi utenti).
- Fornisci i seguenti dettagli di configurazione:
- Utente: inserisci
secops-reader
. - Tipo di accesso: seleziona Chiave di accesso - Accesso programmatico.
- Utente: inserisci
- Fai clic su Crea utente.
- Collega la criterio per la lettura minima (personalizzata): Utenti > secops-reader > Autorizzazioni > Aggiungi autorizzazioni > Collega le norme direttamente > Crea norma.
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" } ] }
Name =
secops-reader-policy
.Fai clic su Crea criterio > cerca/seleziona > Avanti > Aggiungi autorizzazioni.
Crea la chiave di accesso per
secops-reader
: Credenziali di sicurezza > Chiavi di accesso.Fai clic su Crea chiave di accesso.
Scarica il
CSV
. Incollerai questi valori nel feed.
Configura un feed in Google SecOps per importare i log di TeamViewer
- Vai a Impostazioni SIEM > Feed.
- Fai clic su + Aggiungi nuovo feed.
- Nel campo Nome feed, inserisci un nome per il feed (ad esempio,
TeamViewer logs
). - Seleziona Amazon S3 V2 come Tipo di origine.
- Seleziona TeamViewer come Tipo di log.
- Fai clic su Avanti.
- Specifica i valori per i seguenti parametri di input:
- URI S3:
s3://teamviewer-logs/teamviewer/audit/
- Opzioni di eliminazione dell'origine: seleziona l'opzione di eliminazione in base alle tue preferenze.
- Età massima del file: includi i file modificati nell'ultimo numero di giorni. Il valore predefinito è 180 giorni.
- ID chiave di accesso: chiave di accesso utente con accesso al bucket S3.
- Chiave di accesso segreta: chiave segreta dell'utente con accesso al bucket S3.
- Spazio dei nomi dell'asset: lo spazio dei nomi dell'asset.
- Etichette di importazione: l'etichetta applicata agli eventi di questo feed.
- URI S3:
- Fai clic su Avanti.
- Controlla la nuova configurazione del feed nella schermata Finalizza e poi fai clic su Invia.
Tabella di mappatura UDM
Campo log | Mappatura UDM | Logic |
---|---|---|
AffectedItem |
metadata.product_log_id |
Il valore di AffectedItem del log non elaborato viene mappato direttamente a questo campo UDM. |
EventDetails.NewValue |
principal.resource.attribute.labels.value |
Se PropertyName contiene (server) , NewValue viene utilizzato come valore di un'etichetta in principal.resource.attribute.labels . |
EventDetails.NewValue |
principal.user.user_display_name |
Se PropertyName è Name of participant , NewValue viene utilizzato come nome visualizzato dell'utente per l'entità. |
EventDetails.NewValue |
principal.user.userid |
Se PropertyName è ID of participant , NewValue viene utilizzato come ID utente per l'entità. |
EventDetails.NewValue |
security_result.about.labels.value |
Per tutti gli altri valori di PropertyName (tranne quelli gestiti da condizioni specifiche), NewValue viene utilizzato come valore di un'etichetta all'interno dell'array security_result.about.labels . |
EventDetails.NewValue |
target.file.full_path |
Se PropertyName è Source file , NewValue viene utilizzato come percorso completo per il file di destinazione. |
EventDetails.NewValue |
target.resource.attribute.labels.value |
Se PropertyName contiene (client) , NewValue viene utilizzato come valore di un'etichetta in target.resource.attribute.labels . |
EventDetails.NewValue |
target.user.user_display_name |
Se PropertyName è Name of presenter , viene analizzato NewValue . Se è un numero intero, viene ignorato. In caso contrario, viene utilizzato come nome visualizzato dell'utente per la destinazione. |
EventDetails.NewValue |
target.user.userid |
Se PropertyName è ID of presenter , NewValue viene utilizzato come ID utente per il target. |
EventDetails.PropertyName |
principal.resource.attribute.labels.key |
Se PropertyName contiene (server) , PropertyName viene utilizzato come chiave di un'etichetta in principal.resource.attribute.labels . |
EventDetails.PropertyName |
security_result.about.labels.key |
Per tutti gli altri valori di PropertyName (tranne quelli gestiti da condizioni specifiche), PropertyName viene utilizzato come chiave di un'etichetta all'interno dell'array security_result.about.labels . |
EventDetails.PropertyName |
target.resource.attribute.labels.key |
Se PropertyName contiene (client) , PropertyName viene utilizzato come chiave di un'etichetta in target.resource.attribute.labels . |
EventName |
metadata.product_event_type |
Il valore di EventName del log non elaborato viene mappato direttamente a questo campo UDM. |
Timestamp |
metadata.event_timestamp |
Il valore di Timestamp del log non elaborato viene analizzato e utilizzato come timestamp dell'evento nei metadati. Imposta su USER_UNCATEGORIZED se src_user (derivato da ID of participant ) non è vuoto, altrimenti imposta su USER_RESOURCE_ACCESS . Codificato in TEAMVIEWER . Codificato in TEAMVIEWER . Codificato in TEAMVIEWER . |
Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.