Mengumpulkan log Citrix Analytics

Didukung di:

Dokumen ini menjelaskan cara menyerap log Citrix Analytics ke Google Security Operations menggunakan Amazon S3.

Sebelum memulai

Pastikan Anda memenuhi prasyarat berikut:

  • Instance Google SecOps
  • Akses istimewa ke tenant Citrix Analytics for Performance
  • Akses istimewa ke AWS (S3, IAM, Lambda, EventBridge)

Mengumpulkan prasyarat Citrix Analytics

  1. Login ke Citrix Cloud Console.
  2. Buka Identity and Access Management > API Access.
  3. Klik Buat Klien.
  4. Salin dan simpan detail berikut di lokasi yang aman:
    • Client ID
    • Rahasia Klien
    • ID Pelanggan (terdapat di URL Citrix Cloud atau halaman IAM)
    • URL Dasar API: https://api.cloud.com/casodata

Mengonfigurasi bucket AWS S3 dan IAM untuk Google SecOps

  1. Buat bucket Amazon S3 dengan mengikuti panduan pengguna ini: Membuat bucket
  2. Simpan Name dan Region bucket untuk referensi di masa mendatang (misalnya, citrix-analytics-logs).
  3. Buat pengguna dengan mengikuti panduan pengguna ini: Membuat pengguna IAM.
  4. Pilih Pengguna yang dibuat.
  5. Pilih tab Kredensial keamanan.
  6. Klik Create Access Key di bagian Access Keys.
  7. Pilih Layanan pihak ketiga sebagai Kasus penggunaan.
  8. Klik Berikutnya.
  9. Opsional: tambahkan tag deskripsi.
  10. Klik Create access key.
  11. Klik Download CSV file untuk menyimpan Access Key dan Secret Access Key untuk digunakan nanti.
  12. Klik Selesai.
  13. Pilih tab Izin.
  14. Klik Tambahkan izin di bagian Kebijakan izin.
  15. Pilih Tambahkan izin.
  16. Pilih Lampirkan kebijakan secara langsung
  17. Telusuri dan pilih kebijakan AmazonS3FullAccess.
  18. Klik Berikutnya.
  19. Klik Add permissions.

Mengonfigurasi kebijakan dan peran IAM untuk upload S3

  1. Di konsol AWS, buka IAM > Policies > Create policy > JSON tab.
  2. Masukkan kebijakan berikut:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::citrix-analytics-logs/*"
        },
        {
          "Sid": "AllowGetStateObject",
          "Effect": "Allow",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::citrix-analytics-logs/citrix_analytics/state.json"
        }
      ]
    }
    
    • Ganti citrix-analytics-logs jika Anda memasukkan nama bucket yang berbeda.
  3. Klik Berikutnya > Buat kebijakan.

  4. Buka IAM > Roles > Create role > AWS service > Lambda.

  5. Lampirkan kebijakan yang baru dibuat.

  6. Beri nama peran CitrixAnalyticsLambdaRole, lalu klik Buat peran.

Buat fungsi Lambda

  1. Di Konsol AWS, buka Lambda > Functions > Create function.
  2. Klik Buat dari awal.
  3. Berikan detail konfigurasi berikut:

    Setelan Nilai
    Nama CitrixAnalyticsCollector
    Runtime Python 3.13
    Arsitektur x86_64
    Peran eksekusi CitrixAnalyticsLambdaRole
  4. Setelah fungsi dibuat, buka tab Code, hapus stub, dan masukkan kode berikut (CitrixAnalyticsCollector.py):

    import os
    import json
    import uuid
    import datetime
    import urllib.parse
    import urllib.request
    import boto3
    import botocore
    
    CITRIX_TOKEN_URL_TMPL = "https://api.cloud.com/cctrustoauth2/{customerid}/tokens/clients"
    DEFAULT_API_BASE = "https://api.cloud.com/casodata"
    
    s3 = boto3.client("s3")
    
    def _http_post_form(url, data_dict):
        """POST form data to get authentication token."""
        data = urllib.parse.urlencode(data_dict).encode("utf-8")
        req = urllib.request.Request(url, data=data, headers={
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded",
        })
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    
    def _http_get_json(url, headers):
        """GET JSON data from API endpoint."""
        req = urllib.request.Request(url, headers=headers)
        with urllib.request.urlopen(req, timeout=60) as response:
            return json.loads(response.read().decode("utf-8"))
    
    def get_citrix_token(customer_id, client_id, client_secret):
        """Get Citrix Cloud authentication token."""
        url = CITRIX_TOKEN_URL_TMPL.format(customerid=customer_id)
        payload = {
            "grant_type": "client_credentials",
            "client_id": client_id,
            "client_secret": client_secret,
        }
        token_response = _http_post_form(url, payload)
        return token_response["access_token"]
    
    def fetch_odata_entity(entity, when_utc, top, headers, api_base):
        """Fetch data from Citrix Analytics OData API with pagination."""
        year = when_utc.year
        month = when_utc.month
        day = when_utc.day
        hour = when_utc.hour
    
        base_url = f"{api_base.rstrip('/')}/{entity}?year={year:04d}&month={month:02d}&day={day:02d}&hour={hour:02d}"
        skip = 0
    
        while True:
            url = f"{base_url}&$top={top}&$skip={skip}"
            data = _http_get_json(url, headers)
            items = data.get("value", [])
    
            if not items:
                break
    
            for item in items:
                yield item
    
            if len(items) < top:
                break
    
            skip += top
    
    def read_state_file(bucket, state_key):
        """Read the last processed timestamp from S3 state file."""
        try:
            obj = s3.get_object(Bucket=bucket, Key=state_key)
            content = obj["Body"].read().decode("utf-8")
            state = json.loads(content)
            timestamp_str = state.get("last_hour_utc")
            if timestamp_str:
                return datetime.datetime.fromisoformat(timestamp_str.replace("Z", "+00:00")).replace(tzinfo=None)
        except botocore.exceptions.ClientError as e:
            if e.response["Error"]["Code"] == "NoSuchKey":
                return None
            raise
        return None
    
    def write_state_file(bucket, state_key, dt_utc):
        """Write the current processed timestamp to S3 state file."""
        state_data = {"last_hour_utc": dt_utc.isoformat() + "Z"}
        s3.put_object(
            Bucket=bucket, 
            Key=state_key,
            Body=json.dumps(state_data, separators=(",", ":")),
            ContentType="application/json"
        )
    
    def write_ndjson_to_s3(bucket, key, records):
        """Write records as NDJSON to S3."""
        body_lines = []
        for record in records:
            json_line = json.dumps(record, separators=(",", ":"), ensure_ascii=False)
            body_lines.append(json_line)
    
        body = ("n".join(body_lines) + "n").encode("utf-8")
        s3.put_object(
            Bucket=bucket, 
            Key=key, 
            Body=body, 
            ContentType="application/x-ndjson"
        )
    
    def lambda_handler(event, context):
        """Main Lambda handler function."""
    
        # Environment variables
        bucket = os.environ["S3_BUCKET"]
        prefix = os.environ.get("S3_PREFIX", "").strip("/")
        state_key = os.environ.get("STATE_KEY") or f"{prefix}/state.json"
        customer_id = os.environ["CITRIX_CUSTOMER_ID"]
        client_id = os.environ["CITRIX_CLIENT_ID"]
        client_secret = os.environ["CITRIX_CLIENT_SECRET"]
        api_base = os.environ.get("API_BASE", DEFAULT_API_BASE)
        entities = [e.strip() for e in os.environ.get("ENTITIES", "sessions,machines,users").split(",") if e.strip()]
        top_n = int(os.environ.get("TOP_N", "1000"))
        lookback_minutes = int(os.environ.get("LOOKBACK_MINUTES", "75"))
    
        # Determine target hour to collect
        now = datetime.datetime.utcnow()
        fallback_target = (now - datetime.timedelta(minutes=lookback_minutes)).replace(minute=0, second=0, microsecond=0)
    
        last_processed = read_state_file(bucket, state_key)
        if last_processed:
            target_hour = last_processed + datetime.timedelta(hours=1)
        else:
            target_hour = fallback_target
    
        # Get authentication token
        token = get_citrix_token(customer_id, client_id, client_secret)
        headers = {
            "Authorization": f"CwsAuth bearer={token}",
            "Citrix-CustomerId": customer_id,
            "Accept": "application/json",
            "Content-Type": "application/json",
        }
    
        total_records = 0
    
        # Process each entity type
        for entity in entities:
            records = []
    
            for row in fetch_odata_entity(entity, target_hour, top_n, headers, api_base):
                enriched_record = {
                    "citrix_entity": entity,
                    "citrix_hour_utc": target_hour.isoformat() + "Z",
                    "collection_timestamp": datetime.datetime.utcnow().isoformat() + "Z",
                    "raw": row
                }
                records.append(enriched_record)
    
                # Write in batches to avoid memory issues
                if len(records) >= 1000:
                    s3_key = f"{prefix}/{entity}/year={target_hour.year:04d}/month={target_hour.month:02d}/day={target_hour.day:02d}/hour={target_hour.hour:02d}/part-{uuid.uuid4().hex}.ndjson"
                    write_ndjson_to_s3(bucket, s3_key, records)
                    total_records += len(records)
                    records = []
    
            # Write remaining records
            if records:
                s3_key = f"{prefix}/{entity}/year={target_hour.year:04d}/month={target_hour.month:02d}/day={target_hour.day:02d}/hour={target_hour.hour:02d}/part-{uuid.uuid4().hex}.ndjson"
                write_ndjson_to_s3(bucket, s3_key, records)
                total_records += len(records)
    
        # Update state file
        write_state_file(bucket, state_key, target_hour)
    
        return {
            "statusCode": 200,
            "body": json.dumps({
                "success": True,
                "hour_collected": target_hour.isoformat() + "Z",
                "records_written": total_records,
                "entities_processed": entities
            })
        }
    
  5. Buka Configuration > Environment variables > Edit > Add new environment variable.

  6. Masukkan variabel lingkungan berikut, ganti dengan nilai Anda:

    Kunci Nilai contoh
    S3_BUCKET citrix-analytics-logs
    S3_PREFIX citrix_analytics
    STATE_KEY citrix_analytics/state.json
    CITRIX_CLIENT_ID your-client-id
    CITRIX_CLIENT_SECRET your-client-secret
    API_BASE https://api.cloud.com/casodata
    CITRIX_CUSTOMER_ID your-customer-id
    ENTITIES sessions,machines,users
    TOP_N 1000
    LOOKBACK_MINUTES 75
  7. Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda &gt Functions > CitrixAnalyticsCollector).

  8. Pilih tab Configuration

  9. Di panel General configuration, klik Edit.

  10. Ubah Waktu tunggu menjadi 5 menit (300 detik), lalu klik Simpan.

Membuat jadwal EventBridge

  1. Buka Amazon EventBridge > Scheduler > Create schedule.
  2. Berikan detail konfigurasi berikut:
    • Jadwal berulang: Tarif (1 hour)
    • Target: fungsi Lambda Anda CitrixAnalyticsCollector
    • Nama: CitrixAnalyticsCollector-1h
  3. Klik Buat jadwal.

Opsional: Buat pengguna & kunci IAM hanya baca untuk Google SecOps

  1. Di Konsol AWS, buka IAM > Pengguna > Tambahkan pengguna.
  2. Klik Add users.
  3. Berikan detail konfigurasi berikut:
    • Pengguna: secops-reader
    • Jenis akses: Kunci akses — Akses terprogram
  4. Klik Buat pengguna.
  5. Lampirkan kebijakan baca minimal (kustom): Pengguna > secops-reader > Izin > Tambahkan izin > Lampirkan kebijakan secara langsung > Buat kebijakan.
  6. Di editor JSON, masukkan kebijakan berikut:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::citrix-analytics-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::citrix-analytics-logs"
        }
      ]
    }
    
  7. Tetapkan nama ke secops-reader-policy.

  8. Buka Buat kebijakan > cari/pilih > Berikutnya > Tambahkan izin.

  9. Buka Kredensial keamanan > Kunci akses > Buat kunci akses.

  10. Download CSV (nilai ini dimasukkan ke dalam feed).

Mengonfigurasi feed di Google SecOps untuk memproses log Citrix Analytics

  1. Buka Setelan SIEM > Feed.
  2. Klik + Tambahkan Feed Baru.
  3. Di kolom Nama feed, masukkan nama untuk feed (misalnya, Citrix Analytics Performance logs).
  4. Pilih Amazon S3 V2 sebagai Jenis sumber.
  5. Pilih Citrix Analytics sebagai Jenis log.
  6. Klik Berikutnya.
  7. Tentukan nilai untuk parameter input berikut:
    • URI S3: s3://citrix-analytics-logs/citrix_analytics/
    • Opsi penghapusan sumber: Pilih opsi penghapusan sesuai preferensi Anda.
    • Usia File Maksimum: Menyertakan file yang diubah dalam jumlah hari terakhir. Default 180 Hari.
    • ID Kunci Akses: Kunci akses pengguna dengan akses ke bucket S3.
    • Kunci Akses Rahasia: Kunci rahasia pengguna dengan akses ke bucket S3.
    • Namespace aset: Namespace aset.
    • Label penyerapan: Label yang diterapkan ke peristiwa dari feed ini.
  8. Klik Berikutnya.
  9. Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.

Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.