Coletar registros do Citrix Analytics
Versão do analisador:1.0
Este documento explica como ingerir registros do Citrix Analytics no Google Security Operations usando o Google Cloud Storage. O Citrix Analytics for Performance (Cloud Software Group) fornece dados de desempenho agregados dos ambientes Citrix Virtual Apps and Desktops, permitindo buscar dados de sessão, máquina e dados do usuário pela API OData. O Citrix Analytics for Security fornece insights de risco e eventos de origem de dados que podem ser exportados por meio da integração do SIEM baseada em Kafka.
Antes de começar
Verifique se você tem os pré-requisitos a seguir:
- Uma instância do Google SecOps
- Um projeto do GCP com a API Storage do Cloud ativada
- Permissões para criar e gerenciar intervalos do GCS
- Permissões para criar serviços do Cloud Run, tópicos do Pub/Sub e jobs do Cloud Scheduler
- Acesso privilegiado a um locatário do Citrix Analytics for Performance
- Credenciais da API Citrix Cloud (ID do cliente, chave secreta do cliente, ID do cliente)
Coletar credenciais da API Citrix Analytics
Receber credenciais da API Citrix Cloud
- Faça login no Citrix Cloud Console.
- Clique no ícone de menu no canto superior esquerdo da tela.
- Selecione Identity and Access Management no menu.
- Selecione a guia Acesso à API.
- Clique em Criar cliente.
- Copie e salve os seguintes detalhes em um local seguro:
- ID do cliente
- Chave secreta do cliente
- ID do cliente (localizado no URL do Citrix Cloud ou na página do IAM)
Determinar o URL de base da API
O URL base da API OData depende da sua região do Citrix Cloud:
| Região | URL base da API |
|---|---|
| Estados Unidos | https://api.cloud.com/casodata |
| União Europeia | https://api.eu.cloud.com/casodata |
| Ásia-Pacífico Sul | https://api.ap-s.cloud.com/casodata |
Verifique as permissões
Para verificar se a conta tem as permissões necessárias:
- Faça login no Citrix Cloud.
- Acesse Identity and Access Management > Administradores.
- Verifique se a conta usada para criar as credenciais da API tem Acesso total ou Acesso personalizado com as permissões do Citrix Analytics for Performance ativadas.
- Se você não encontrar as permissões necessárias, entre em contato com o administrador do Citrix Cloud para conceder acesso.
Testar o acesso à API
Teste suas credenciais antes de prosseguir com a integração:
CITRIX_CUSTOMER_ID="your-customer-id" CITRIX_CLIENT_ID="your-client-id" CITRIX_CLIENT_SECRET="your-client-secret" # Get bearer token TOKEN=$(curl -s -X POST \ "https://api.cloud.com/cctrustoauth2/${CITRIX_CUSTOMER_ID}/tokens/clients" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=${CITRIX_CLIENT_ID}&client_secret=${CITRIX_CLIENT_SECRET}" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") # Test OData API access curl -v -H "Authorization: CwsAuth bearer=${TOKEN}" \ -H "Citrix-CustomerId: ${CITRIX_CUSTOMER_ID}" \ -H "Accept: application/json" \ "https://api.cloud.com/casodata/sessions?\$top=1"
Criar um bucket do Google Cloud Storage
- Acesse o Console do Google Cloud.
- Selecione ou crie um projeto.
- No menu de navegação, acesse Cloud Storage > Buckets.
- Clique em Criar bucket.
Informe os seguintes detalhes de configuração:
Configuração Valor Nomeie seu bucket Insira um nome globalmente exclusivo, por exemplo, citrix-analytics-logs.Tipo de local Escolha de acordo com suas necessidades (região, birregional, multirregional) Local Selecione o local (por exemplo, us-central1).Classe de armazenamento Padrão (recomendado para registros acessados com frequência) Controle de acesso Uniforme (recomendado) Ferramentas de proteção Opcional: ativar o controle de versões de objetos ou a política de retenção Clique em Criar.
Criar uma conta de serviço para a função do Cloud Run
A função do Cloud Run precisa de uma conta de serviço com permissões para gravar no bucket do GCS e ser invocada pelo Pub/Sub.
Criar conta de serviço
- No console do GCP, acesse IAM e administrador > Contas de serviço.
- Clique em Criar conta de serviço.
- Informe os seguintes detalhes de configuração:
- Nome da conta de serviço: insira
citrix-analytics-collector-sa - Descrição da conta de serviço: insira
Service account for Cloud Run function to collect Citrix Analytics logs
- Nome da conta de serviço: insira
- Clique em Criar e continuar.
- Na seção Conceder acesso a essa conta de serviço ao projeto, adicione os seguintes papéis:
- Clique em Selecionar papel.
- Pesquise e selecione Administrador de objetos do Storage.
- Clique em + Adicionar outro papel.
- Pesquise e selecione Invocador do Cloud Run.
- Clique em + Adicionar outro papel.
- Pesquise e selecione Invocador de funções do Cloud.
- Clique em Continuar.
- Clique em Concluído.
Essas funções são necessárias para:
- Administrador de objetos do Storage: grava registros no bucket do GCS e gerencia arquivos de estado.
- Invocador do Cloud Run: permite que o Pub/Sub invoque a função.
- Invocador do Cloud Functions: permite a invocação de funções.
Conceder permissões do IAM no bucket do GCS
Conceda permissões de gravação à conta de serviço no bucket do GCS:
- Acesse Cloud Storage > Buckets.
- Clique no nome do bucket.
- Acesse a guia Permissões.
- Clique em Conceder acesso.
- Informe os seguintes detalhes de configuração:
- Adicionar principais: insira o e-mail da conta de serviço (por exemplo,
citrix-analytics-collector-sa@PROJECT_ID.iam.gserviceaccount.com). - Atribuir papéis: selecione Administrador de objetos do Storage.
- Adicionar principais: insira o e-mail da conta de serviço (por exemplo,
- Clique em Salvar.
Criar tópico Pub/Sub
Crie um tópico do Pub/Sub em que o Cloud Scheduler vai publicar e a função do Cloud Run vai se inscrever.
- No Console do GCP, acesse Pub/Sub > Tópicos.
- Selecione Criar tópico.
- Informe os seguintes detalhes de configuração:
- ID do tópico: insira
citrix-analytics-trigger - Não mude as outras configurações.
- ID do tópico: insira
- Clique em Criar.
Criar uma função do Cloud Run para coletar registros
A função do Cloud Run será acionada por mensagens do Pub/Sub do Cloud Scheduler para buscar registros da API OData do Citrix Analytics e gravá-los no GCS.
- No console do GCP, acesse o Cloud Run.
- Clique em Criar serviço.
- Selecione Função (use um editor in-line para criar uma função).
Na seção Configurar, forneça os seguintes detalhes de configuração:
Configuração Valor Nome do serviço citrix-analytics-collectorRegião Selecione a região correspondente ao seu bucket do GCS (por exemplo, us-central1).Ambiente de execução Selecione Python 3.12 ou uma versão mais recente. Na seção Gatilho (opcional):
- Clique em + Adicionar gatilho.
- Selecione Cloud Pub/Sub.
- Em Selecionar um tópico do Cloud Pub/Sub, escolha o tópico
citrix-analytics-trigger. - Clique em Salvar.
Na seção Autenticação:
- Selecione Exigir autenticação.
- Confira o Identity and Access Management (IAM).
Role a tela para baixo e abra Contêineres, rede, segurança.
Acesse a guia Segurança:
- Conta de serviço: selecione a conta de serviço
citrix-analytics-collector-sa.
- Conta de serviço: selecione a conta de serviço
Acesse a guia Contêineres:
- Clique em Variáveis e secrets.
- Clique em + Adicionar variável para cada variável de ambiente:
Nome da variável Valor de exemplo Descrição GCS_BUCKETcitrix-analytics-logsNome do bucket do GCS GCS_PREFIXcitrix_analyticsPrefixo para arquivos de registro STATE_KEYcitrix_analytics/state.jsonCaminho do arquivo de estado CITRIX_CLIENT_IDyour-client-idID do cliente do Citrix Cloud CITRIX_CLIENT_SECRETyour-client-secretChave secreta do cliente do Citrix Cloud CITRIX_CUSTOMER_IDyour-customer-idID do cliente do Citrix Cloud API_BASEhttps://api.cloud.com/casodataURL base da API OData ENTITIESsessions,machines,usersTipos de entidades a serem coletadas TOP_N1000Registros por página LOOKBACK_MINUTES75Período de lookback inicial Role a tela para baixo na seção Variáveis e secrets até Solicitações:
- Tempo limite da solicitação: insira
600segundos (10 minutos)
- Tempo limite da solicitação: insira
Acesse a guia Configurações:
- Na seção Recursos:
- Memória: selecione 512 MiB ou mais.
- CPU: selecione 1
- Na seção Recursos:
Na seção Escalonamento de revisão:
- Número mínimo de instâncias: insira
0 - Número máximo de instâncias: insira
100ou ajuste com base na carga esperada.
- Número mínimo de instâncias: insira
Clique em Criar.
Aguarde a criação do serviço (1 a 2 minutos).
Depois que o serviço for criado, o editor de código inline será aberto automaticamente.
Adicionar código da função
- Insira main no campo Ponto de entrada.
No editor de código inline, crie dois arquivos:
main.py:
import functions_framework from google.cloud import storage import json import os import urllib3 from datetime import datetime, timedelta, timezone import urllib.parse import time # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=30.0), retries=False, ) # Initialize Storage client storage_client = storage.Client() CITRIX_TOKEN_URL_TMPL = "https://api.cloud.com/cctrustoauth2/{customerid}/tokens/clients" DEFAULT_API_BASE = "https://api.cloud.com/casodata" @functions_framework.cloud_event def main(cloud_event): """ Cloud Run function triggered by Pub/Sub to fetch logs from Citrix Analytics OData API and write to GCS. Args: cloud_event: CloudEvent object containing Pub/Sub message """ # Get environment variables bucket_name = os.environ.get('GCS_BUCKET') prefix = os.environ.get('GCS_PREFIX', 'citrix_analytics').strip('/') state_key = os.environ.get('STATE_KEY') or f"{prefix}/state.json" customer_id = os.environ.get('CITRIX_CUSTOMER_ID') client_id = os.environ.get('CITRIX_CLIENT_ID') client_secret = os.environ.get('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')) if not all([bucket_name, customer_id, client_id, client_secret]): print('Error: Missing required environment variables') return try: # Get GCS bucket bucket = storage_client.bucket(bucket_name) # Determine target hour to collect now = datetime.now(timezone.utc) fallback_target = (now - timedelta(minutes=lookback_minutes)).replace(minute=0, second=0, microsecond=0) # Load state (last processed timestamp) state = load_state(bucket, state_key) last_processed_str = state.get('last_hour_utc') if last_processed_str: last_processed = datetime.fromisoformat(last_processed_str.replace('Z', '+00:00')).replace(tzinfo=None) target_hour = last_processed + timedelta(hours=1) else: target_hour = fallback_target print(f'Processing logs for hour: {target_hour.isoformat()}Z') # 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.now(timezone.utc).isoformat() + 'Z', 'raw': row } records.append(enriched_record) # Write in batches to avoid memory issues if len(records) >= 1000: blob_name = f"{prefix}/{entity}/year={target_hour.year:04d}/month={target_hour.month:02d}/day={target_hour.day:02d}/hour={target_hour.hour:02d}/part-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S%f')}.ndjson" write_ndjson_to_gcs(bucket, blob_name, records) total_records += len(records) records = [] # Write remaining records if records: blob_name = f"{prefix}/{entity}/year={target_hour.year:04d}/month={target_hour.month:02d}/day={target_hour.day:02d}/hour={target_hour.hour:02d}/part-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S%f')}.ndjson" write_ndjson_to_gcs(bucket, blob_name, records) total_records += len(records) # Update state file save_state(bucket, state_key, {'last_hour_utc': target_hour.isoformat() + 'Z'}) print(f'Successfully processed {total_records} records for hour {target_hour.isoformat()}Z') except Exception as e: print(f'Error processing logs: {str(e)}') raise 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, } data = urllib.parse.urlencode(payload).encode('utf-8') response = http.request( 'POST', url, body=data, headers={ 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', } ) if response.status != 200: print(f'Token request failed with status {response.status}') print(f'Response: {response.data.decode("utf-8")}') raise Exception(f'Failed to get Citrix token: HTTP {response.status}') token_response = json.loads(response.data.decode('utf-8')) 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 and rate limiting.""" 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 backoff = 1.0 while True: url = f"{base_url}&$top={top}&$skip={skip}" response = http.request('GET', url, headers=headers) # Handle rate limiting with exponential backoff if response.status == 429: retry_after = int(response.headers.get('Retry-After', str(int(backoff)))) print(f'Rate limited (429). Retrying after {retry_after}s...') time.sleep(retry_after) backoff = min(backoff * 2, 30.0) continue backoff = 1.0 if response.status != 200: print(f'HTTP Error: {response.status}') response_text = response.data.decode('utf-8') print(f'Response body: {response_text}') return data = json.loads(response.data.decode('utf-8')) items = data.get('value', []) if not items: break for item in items: yield item if len(items) < top: break skip += top def load_state(bucket, key): """Load state from GCS.""" try: blob = bucket.blob(key) if blob.exists(): state_data = blob.download_as_text() return json.loads(state_data) except Exception as e: print(f'Warning: Could not load state: {str(e)}') return {} def save_state(bucket, key, state): """Save state to GCS.""" try: blob = bucket.blob(key) blob.upload_from_string( json.dumps(state, separators=(',', ':')), content_type='application/json' ) except Exception as e: print(f'Warning: Could not save state: {str(e)}') def write_ndjson_to_gcs(bucket, key, records): """Write records as NDJSON to GCS.""" 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') blob = bucket.blob(key) blob.upload_from_string(body, content_type='application/x-ndjson')requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0
Clique em Implantar para salvar e implantar a função.
Aguarde a conclusão da implantação (2 a 3 minutos).
Criar o job do Cloud Scheduler
O Cloud Scheduler vai publicar mensagens no tópico do Pub/Sub em intervalos regulares, acionando a função do Cloud Run.
- No console do GCP, acesse o Cloud Scheduler.
- Clique em Criar job.
Informe os seguintes detalhes de configuração:
Configuração Valor Nome citrix-analytics-collector-hourlyRegião Selecione a mesma região da função do Cloud Run Frequência 0 * * * *(de hora em hora)Fuso horário Selecionar fuso horário (UTC recomendado) Tipo de destino Pub/Sub Tópico Selecione o tópico citrix-analytics-trigger.Corpo da mensagem {}(objeto JSON vazio)Clique em Criar.
Opções de frequência de programação
Escolha a frequência com base no volume de registros e nos requisitos de latência:
| Frequência | Expressão Cron | Caso de uso |
|---|---|---|
| A cada hora | 0 * * * * |
Padrão (recomendado) |
| A cada 2 horas | 0 */2 * * * |
Diminuir o volume |
| A cada 6 horas | 0 */6 * * * |
Baixo volume, processamento em lote |
Testar a integração
- No console do Cloud Scheduler, encontre seu job.
- Clique em Forçar execução para acionar o job manualmente.
- Aguarde alguns segundos.
- Acesse Cloud Run > Serviços.
- Clique no nome da função
citrix-analytics-collector. - Clique na guia Registros.
Verifique se a função foi executada com sucesso. Procure:
Processing logs for hour: YYYY-MM-DDTHH:00:00Z Successfully processed X records for hour YYYY-MM-DDTHH:00:00ZAcesse Cloud Storage > Buckets.
Clique no nome do bucket.
Navegue até a pasta de prefixo
citrix_analytics/.Verifique se os novos arquivos
.ndjsonforam criados com o carimbo de data/hora atual.
Se você encontrar erros nos registros:
- HTTP 401: verifique as credenciais da API nas variáveis de ambiente
- HTTP 403: verifique se a conta tem as permissões necessárias no Citrix Cloud
- HTTP 429: limitação de taxa. A função vai tentar de novo automaticamente com espera.
- Variáveis de ambiente ausentes: verifique se todas as variáveis necessárias estão definidas.
Configurar um feed no Google SecOps para ingerir registros do Citrix Analytics
- Acesse Configurações do SIEM > Feeds.
- Clique em Adicionar novo feed.
- Clique em Configurar um único feed.
- No campo Nome do feed, insira um nome para o feed (por exemplo,
Citrix Analytics logs). - Selecione Google Cloud Storage V2 como o Tipo de origem.
- Selecione Citrix Analytics como o Tipo de registro.
Clique em Receber conta de serviço. Um e-mail exclusivo da conta de serviço será exibido, por exemplo:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comCopie esse endereço de e-mail para usar na próxima etapa.
Clique em Próxima.
Especifique valores para os seguintes parâmetros de entrada:
URL do bucket de armazenamento: insira o URI do bucket do GCS com o caminho do prefixo:
gs://citrix-analytics-logs/citrix_analytics/- Substitua:
citrix-analytics-logs: o nome do bucket do GCS.citrix_analytics: prefixo/caminho da pasta opcional onde os registros são armazenados (deixe em branco para a raiz).
- Substitua:
Opção de exclusão da fonte: selecione a opção de exclusão de acordo com sua preferência:
- Nunca: nunca exclui arquivos após as transferências (recomendado para testes).
- Excluir arquivos transferidos: exclui os arquivos após a transferência.
- Excluir arquivos transferidos e diretórios vazios: exclui arquivos e diretórios vazios após a transferência.
Idade máxima do arquivo: inclua arquivos modificados no último número de dias (o padrão é 180 dias).
Namespace do recurso: o namespace do recurso
Rótulos de ingestão: o rótulo a ser aplicado aos eventos deste feed
Clique em Próxima.
Revise a nova configuração do feed na tela Finalizar e clique em Enviar.
Conceder permissões do IAM à conta de serviço do Google SecOps
A conta de serviço do Google SecOps precisa do papel Leitor de objetos do Storage no seu bucket do GCS.
- Acesse Cloud Storage > Buckets.
- Clique no nome do bucket.
- Acesse a guia Permissões.
- Clique em Conceder acesso.
- Informe os seguintes detalhes de configuração:
- Adicionar principais: cole o e-mail da conta de serviço do Google SecOps.
- Atribuir papéis: selecione Leitor de objetos do Storage.
Clique em Salvar.
Tabela de mapeamento de UDM
| Campo de registro | Mapeamento do UDM | Lógica |
|---|---|---|
occurrence_event_type |
extensions.auth.type |
Mapeado: Session.Logon → AUTHTYPE_UNSPECIFIED, Session.End → AUTHTYPE_UNSPECIFIED |
server_name |
intermediary.asset.hostname |
Mapeado diretamente |
server_name |
intermediary.hostname |
Mapeado diretamente |
event_type |
metadata.description |
Mapeado diretamente |
timestamp |
metadata.event_timestamp |
Analisado como ISO8601 |
udm_event_type |
metadata.event_type |
Mapeado: "USER_LOGIN", "USER_LOGOUT" → GENERIC_EVENT |
tenant_id |
metadata.product_deployment_id |
Mapeado diretamente |
occurrence_event_type |
metadata.product_event_type |
Mapeado diretamente |
event_id |
metadata.product_log_id |
Mapeado diretamente |
product |
metadata.product_name |
Mapeado diretamente |
product_version |
metadata.product_version |
Mapeado diretamente |
ui_link |
metadata.url_back_to_product |
Mapeado diretamente |
session_key |
network.session_id |
Mapeado diretamente |
domain |
principal.administrative_domain |
Mapeado diretamente |
device_id |
principal.asset.hostname |
Mapeado diretamente |
client_ip |
principal.asset.ip |
Mesclado |
vulnerability |
principal.asset.vulnerabilities |
Mesclado |
device_id |
principal.hostname |
Mapeado diretamente |
client_ip |
principal.ip |
Mesclado |
os_name |
principal.platform |
Valores mapeados (6 no total, por exemplo, (?i)windows → WINDOWS, (?i)windows → MAC, (?i)windows... |
os_extra_info |
principal.platform_patch_level |
Mapeado diretamente |
os_version |
principal.platform_version |
Mapeado diretamente |
entity_id |
principal.user.email_addresses |
Mapeado: ^.+@.+$ → entity_id |
entity_type |
principal.user.email_addresses |
Mapeado: user → entity_id |
session_user_name |
principal.user.user_display_name |
Mapeado diretamente |
entity_id |
principal.user.userid |
Mapeado diretamente |
session_user_name |
principal.user.userid |
Mapeado diretamente |
alert_message |
security_result.action_details |
Mapeado diretamente |
analytic |
security_result.analytics_metadata |
Mesclado |
category |
security_result.category |
Mesclado |
indicator_category |
security_result.category |
Mapeado: Data exfiltration → category |
indicator_name |
security_result.description |
Mapeado diretamente |
label |
security_result.detection_fields |
Mesclado |
label |
security_result.outcomes |
Mesclado |
severity |
security_result.severity |
Mapeado diretamente |
app_name |
target.application |
Mapeado diretamente |
app_name |
target.process.file.names |
Mesclado |
printer_name |
target.resource.name |
Mapeado diretamente |
entity_id |
target.user.email_addresses |
Mapeado: ^.+@.+$ → entity_id |
entity_type |
target.user.email_addresses |
Mapeado: user → entity_id |
session_user_name |
target.user.user_display_name |
Mapeado diretamente |
entity_id |
target.user.userid |
Mapeado diretamente |
session_user_name |
target.user.userid |
Mapeado diretamente |
| N/A | extensions.auth.type |
Constante: AUTHTYPE_UNSPECIFIED |
| N/A | metadata.event_type |
Constante: GENERIC_EVENT |
| N/A | metadata.vendor_name |
Constante: CITRIX_ANALYTICS |
| N/A | principal.platform |
Constante: WINDOWS |
| N/A | security_result.confidence_score |
Constante: risk_probability |
| N/A | security_result.risk_score |
Constante: cur_riskscore |
Registro de alterações
Ver o registro de alterações deste analisador
Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.