TeamViewer-Logs erfassen

Unterstützt in:

In diesem Dokument wird beschrieben, wie Sie TeamViewer-Logs mit Amazon S3 in Google Security Operations aufnehmen. Der Parser extrahiert die Auditereignisse aus JSON-formatierten Logs. Dabei werden die Ereignisdetails durchlaufen, bestimmte Eigenschaften UDM-Feldern (Unified Data Model) zugeordnet, Informationen zu Teilnehmern und Referenten verarbeitet und Ereignisse anhand der Nutzeraktivität kategorisiert. Der Parser führt auch Datentransformationen durch, z. B. das Zusammenführen von Labels und das Konvertieren von Zeitstempeln in ein standardisiertes Format.

Hinweise

Prüfen Sie, ob folgende Voraussetzungen erfüllt sind:

  • Eine Google SecOps-Instanz.
  • Privilegierter Zugriff auf TeamViewer.
  • Privilegierter Zugriff auf AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).

TeamViewer-Voraussetzungen

  1. Melden Sie sich als Administrator in der TeamViewer Management Console an.
  2. Klicken Sie auf Mein Profil > Apps.
  3. Klicken Sie auf Anwendung erstellen.
  4. Geben Sie die folgenden Konfigurationsdetails an:
    • App-Name: Geben Sie einen aussagekräftigen Namen ein, z. B. Google SecOps Integration.
    • Beschreibung: Geben Sie eine Beschreibung für die App ein.
    • Berechtigungen: Wählen Sie die Berechtigungen für den Zugriff auf Audit-Logs aus.
  5. Klicken Sie auf Erstellen und speichern Sie die generierten API-Anmeldedaten an einem sicheren Ort.
  6. Notieren Sie sich die TeamViewer API Base URL (z. B. https://webapi.teamviewer.com/api/v1).
  7. Kopieren Sie die folgenden Details und speichern Sie sie an einem sicheren Ort:
    • CLIENT_ID
    • CLIENT_SECRET
    • API_BASE_URL

AWS S3-Bucket und IAM für Google SecOps konfigurieren

  1. Erstellen Sie einen Amazon S3-Bucket. Folgen Sie dazu der Anleitung unter Bucket erstellen.
  2. Speichern Sie den Namen und die Region des Buckets zur späteren Verwendung (z. B. teamviewer-logs).
  3. Erstellen Sie einen Nutzer gemäß dieser Anleitung: IAM-Nutzer erstellen.
  4. Wählen Sie den erstellten Nutzer aus.
  5. Wählen Sie den Tab Sicherheitsanmeldedaten aus.
  6. Klicken Sie im Abschnitt Zugriffsschlüssel auf Zugriffsschlüssel erstellen.
  7. Wählen Sie Drittanbieterdienst als Anwendungsfall aus.
  8. Klicken Sie auf Weiter.
  9. Optional: Fügen Sie ein Beschreibungstag hinzu.
  10. Klicken Sie auf Zugriffsschlüssel erstellen.
  11. Klicken Sie auf CSV-Datei herunterladen, um den Access Key (Zugriffsschlüssel) und den Secret Access Key (geheimer Zugriffsschlüssel) für die zukünftige Verwendung zu speichern.
  12. Klicken Sie auf Fertig.
  13. Wählen Sie den Tab Berechtigungen aus.
  14. Klicken Sie im Bereich Berechtigungsrichtlinien auf Berechtigungen hinzufügen.
  15. Wählen Sie Berechtigungen hinzufügen aus.
  16. Wählen Sie Richtlinien direkt anhängen aus.
  17. Suchen Sie nach der Richtlinie AmazonS3FullAccess.
  18. Wählen Sie die Richtlinie aus.
  19. Klicken Sie auf Weiter.
  20. Klicken Sie auf Berechtigungen hinzufügen.

IAM-Richtlinie und ‑Rolle für S3-Uploads konfigurieren

  1. Rufen Sie in der AWS-Konsole IAM > Richtlinien auf.
  2. Klicken Sie auf Richtlinie erstellen> Tab „JSON“.
  3. Kopieren Sie die folgende Richtlinie und fügen Sie sie ein.
  4. Policy JSON (ersetzen Sie teamviewer-logs, wenn Sie einen anderen Bucket-Namen eingegeben haben):

    {
      "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"
        }
      ]
    }
    
  5. Klicken Sie auf Weiter > Richtlinie erstellen.

  6. Rufen Sie IAM > Rollen > Rolle erstellen > AWS-Service > Lambda auf.

  7. Hängen Sie die neu erstellte Richtlinie an.

  8. Geben Sie der Rolle den Namen TeamViewerToS3Role und klicken Sie auf Rolle erstellen.

Lambda-Funktion erstellen

  1. Rufen Sie in der AWS Console Lambda > Funktionen > Funktion erstellen auf.
  2. Klicken Sie auf Von Grund auf erstellen.
  3. Geben Sie die folgenden Konfigurationsdetails an:

    Einstellung Wert
    Name teamviewer_to_s3
    Laufzeit Python 3.13
    Architektur x86_64
    Ausführungsrolle TeamViewerToS3Role
  4. Nachdem die Funktion erstellt wurde, öffnen Sie den Tab Code, löschen Sie den Stub und fügen Sie den folgenden Code (teamviewer_to_s3.py) ein.

    #!/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())
    
  5. Rufen Sie Konfiguration > Umgebungsvariablen auf.

  6. Klicken Sie auf Bearbeiten> Neue Umgebungsvariable hinzufügen.

  7. Geben Sie die in der folgenden Tabelle angegebenen Umgebungsvariablen ein und ersetzen Sie die Beispielwerte durch Ihre Werte.

    Umgebungsvariablen

    Schlüssel Beispielwert
    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 (aus Schritt 2)
    CLIENT_SECRET your-client-secret (aus Schritt 2)
  8. Bleiben Sie nach dem Erstellen der Funktion auf der zugehörigen Seite oder öffnen Sie Lambda > Funktionen > Ihre Funktion.

  9. Wählen Sie den Tab Konfiguration aus.

  10. Klicken Sie im Bereich Allgemeine Konfiguration auf Bearbeiten.

  11. Ändern Sie Zeitlimit in 5 Minuten (300 Sekunden) und klicken Sie auf Speichern.

EventBridge-Zeitplan erstellen

  1. Gehen Sie zu Amazon EventBridge > Scheduler > Create schedule (Amazon EventBridge > Scheduler > Zeitplan erstellen).
  2. Geben Sie die folgenden Konfigurationsdetails an:
    • Wiederkehrender Zeitplan: Preis (1 hour).
    • Ziel: Ihre Lambda-Funktion teamviewer_to_s3.
    • Name: teamviewer-audit-1h.
  3. Klicken Sie auf Zeitplan erstellen.

(Optional) IAM-Nutzer mit Lesezugriff und Schlüssel für Google SecOps erstellen

  1. Rufen Sie die AWS-Konsole > IAM > Nutzer > Nutzer hinzufügen auf.
  2. Klicken Sie auf Add users (Nutzer hinzufügen).
  3. Geben Sie die folgenden Konfigurationsdetails an:
    • Nutzer: Geben Sie secops-reader ein.
    • Zugriffstyp: Wählen Sie Zugriffsschlüssel – programmatischer Zugriff aus.
  4. Klicken Sie auf Nutzer erstellen.
  5. Minimale Leseberechtigung (benutzerdefiniert) anhängen: Nutzer > secops-reader > Berechtigungen > Berechtigungen hinzufügen > Richtlinien direkt anhängen > Richtlinie erstellen.
  6. 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"
        }
      ]
    }
    
  7. Name = secops-reader-policy.

  8. Klicken Sie auf Richtlinie erstellen> suchen/auswählen> Weiter> Berechtigungen hinzufügen.

  9. Erstellen Sie einen Zugriffsschlüssel für secops-reader: Sicherheitsanmeldedaten > Zugriffsschlüssel.

  10. Klicken Sie auf Zugriffsschlüssel erstellen.

  11. Laden Sie die Datei CSV herunter. Sie fügen diese Werte in den Feed ein.

Feed in Google SecOps konfigurieren, um TeamViewer-Logs aufzunehmen

  1. Rufen Sie die SIEM-Einstellungen > Feeds auf.
  2. Klicken Sie auf + Neuen Feed hinzufügen.
  3. Geben Sie im Feld Feedname einen Namen für den Feed ein, z. B. TeamViewer logs.
  4. Wählen Sie Amazon S3 V2 als Quelltyp aus.
  5. Wählen Sie TeamViewer als Protokolltyp aus.
  6. Klicken Sie auf Weiter.
  7. Geben Sie Werte für die folgenden Eingabeparameter an:
    • S3-URI: s3://teamviewer-logs/teamviewer/audit/
    • Optionen zum Löschen der Quelle: Wählen Sie die gewünschte Option zum Löschen aus.
    • Maximales Dateialter: Dateien einschließen, die in den letzten Tagen geändert wurden. Der Standardwert ist 180 Tage.
    • Zugriffsschlüssel-ID: Nutzerzugriffsschlüssel mit Zugriff auf den S3-Bucket.
    • Geheimer Zugriffsschlüssel: Der geheime Schlüssel des Nutzers mit Zugriff auf den S3-Bucket.
    • Asset-Namespace: Der Asset-Namespace.
    • Aufnahmelabels: Das Label, das auf die Ereignisse aus diesem Feed angewendet wird.
  8. Klicken Sie auf Weiter.
  9. Prüfen Sie die neue Feedkonfiguration auf dem Bildschirm Abschließen und klicken Sie dann auf Senden.

UDM-Zuordnungstabelle

Logfeld UDM-Zuordnung Logik
AffectedItem metadata.product_log_id Der Wert von AffectedItem aus dem Rohlog wird direkt diesem UDM-Feld zugeordnet.
EventDetails.NewValue principal.resource.attribute.labels.value Wenn PropertyName (server) enthält, wird NewValue als Wert eines Labels in principal.resource.attribute.labels verwendet.
EventDetails.NewValue principal.user.user_display_name Wenn PropertyName Name of participant ist, wird NewValue als Anzeigename des Nutzers für den Prinzipal verwendet.
EventDetails.NewValue principal.user.userid Wenn PropertyName ID of participant ist, wird NewValue als Nutzer-ID für das Prinzipal verwendet.
EventDetails.NewValue security_result.about.labels.value Für alle anderen PropertyName-Werte (mit Ausnahme der Werte, die durch bestimmte Bedingungen verarbeitet werden) wird NewValue als Wert eines Labels im security_result.about.labels-Array verwendet.
EventDetails.NewValue target.file.full_path Wenn PropertyName gleich Source file ist, wird NewValue als vollständiger Pfad für die Zieldatei verwendet.
EventDetails.NewValue target.resource.attribute.labels.value Wenn PropertyName (client) enthält, wird NewValue als Wert eines Labels in target.resource.attribute.labels verwendet.
EventDetails.NewValue target.user.user_display_name Wenn PropertyName Name of presenter ist, wird NewValue geparst. Wenn es sich um eine Ganzzahl handelt, wird sie verworfen. Andernfalls wird er als Anzeigename des Nutzers für das Ziel verwendet.
EventDetails.NewValue target.user.userid Wenn PropertyName gleich ID of presenter ist, wird NewValue als Nutzer-ID für das Ziel verwendet.
EventDetails.PropertyName principal.resource.attribute.labels.key Wenn PropertyName (server) enthält, wird PropertyName als Schlüssel eines Labels in principal.resource.attribute.labels verwendet.
EventDetails.PropertyName security_result.about.labels.key Für alle anderen PropertyName-Werte (außer denen, die durch bestimmte Bedingungen verarbeitet werden) wird PropertyName als Schlüssel eines Labels im security_result.about.labels-Array verwendet.
EventDetails.PropertyName target.resource.attribute.labels.key Wenn PropertyName (client) enthält, wird PropertyName als Schlüssel eines Labels in target.resource.attribute.labels verwendet.
EventName metadata.product_event_type Der Wert von EventName aus dem Rohlog wird direkt diesem UDM-Feld zugeordnet.
Timestamp metadata.event_timestamp Der Wert von Timestamp aus dem Rohlog wird geparst und als Ereigniszeitstempel in den Metadaten verwendet. Wird auf USER_UNCATEGORIZED gesetzt, wenn src_user (abgeleitet von ID of participant) nicht leer ist. Andernfalls wird der Wert auf USER_RESOURCE_ACCESS gesetzt. Hartcodiert auf TEAMVIEWER. Hartcodiert auf TEAMVIEWER. Hartcodiert auf TEAMVIEWER.

Benötigen Sie weitere Hilfe? Antworten von Community-Mitgliedern und Google SecOps-Experten erhalten