Recolha registos do Sentry
Este documento explica como carregar registos do Sentry para o Google Security Operations através do Amazon S3. O Sentry produz dados operacionais sob a forma de eventos, problemas, dados de monitorização do desempenho e informações de acompanhamento de erros. Esta integração permite-lhe enviar estes registos para o Google SecOps para análise e monitorização, oferecendo visibilidade dos erros da aplicação, dos problemas de desempenho e das interações dos utilizadores nas suas aplicações monitorizadas pelo Sentry.
Antes de começar
Certifique-se de que cumpre os seguintes pré-requisitos:
- Uma instância do Google SecOps.
- Acesso privilegiado ao inquilino do Sentry (token de autorização com âmbitos da API).
- Acesso privilegiado à AWS (S3, Identity and Access Management [IAM], Lambda e EventBridge).
Recolha os pré-requisitos do Sentry (IDs, chaves da API, IDs da organização e tokens)
- Inicie sessão no Sentry.
- Encontre o slug da organização:
- Aceda a Definições > Organização > Definições > ID da organização (o slug aparece junto ao nome da organização).
- Crie um token de autorização:
- Aceda a Definições > Definições de programador > Tokens pessoais.
- Clique em Criar novo
- Âmbitos (mínimo):
org:read
,project:read
eevent:read
. - Copie o valor do token (apresentado uma vez). É usado como:
Authorization: Bearer <token>
.
- (Se estiver alojado por si): tome nota do seu URL de base (por exemplo,
https://<your-domain>
); caso contrário, usehttps://sentry.io
.
Configure o contentor do AWS S3 e o IAM para o Google SecOps
- Crie um contentor do Amazon S3 seguindo este guia do utilizador: Criar um contentor
- Guarde o nome e a região do contentor para referência futura (por exemplo,
sentry-logs
). - Crie um utilizador seguindo este guia do utilizador: criar um utilizador do IAM.
- Selecione o utilizador criado.
- Selecione o separador Credenciais de segurança.
- Clique em Criar chave de acesso na secção Chaves de acesso.
- Selecione Serviço de terceiros como Exemplo de utilização.
- Clicar em Seguinte.
- Opcional: adicione uma etiqueta de descrição.
- Clique em Criar chave de acesso.
- Clique em Transferir ficheiro CSV para guardar a chave de acesso e a chave de acesso secreta para referência futura.
- Clique em Concluído.
- Selecione o separador Autorizações.
- Clique em Adicionar autorizações na secção Políticas de autorizações.
- Selecione Adicionar autorizações.
- Selecione Anexar políticas diretamente.
- Pesquise a política AmazonS3FullAccess.
- Selecione a política.
- Clicar em Seguinte.
- Clique em Adicionar autorizações.
Configure a política e a função de IAM para carregamentos do S3
- Na consola da AWS, aceda a IAM > Políticas.
- Clique em Criar política > separador JSON.
- Copie e cole a seguinte política.
JSON da política (substitua
sentry-logs
se tiver introduzido um nome de contentor diferente):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::sentry-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::sentry-logs/sentry/events/state.json" } ] }
Clique em Seguinte > Criar política.
Aceda a IAM > Funções > Criar função > Serviço AWS > Lambda.
Anexe a política criada recentemente.
Dê o nome
WriteSentryToS3Role
à função e clique em Criar função.
Crie a função Lambda
- Na consola da AWS, aceda a Lambda > Functions > Create function.
- Clique em Criar do zero.
Faculte os seguintes detalhes de configuração:
Definição Valor Nome sentry_to_s3
Runtime Python 3.13 Arquitetura x86_64 Função de execução WriteSentryToS3Role
Depois de criar a função, abra o separador Código, elimine o fragmento e cole o seguinte código (
sentry_to_s3.py
).#!/usr/bin/env python3 # Lambda: Pull Sentry project events (raw JSON) to S3 using Link "previous" cursor for duplicate-safe polling import os, json, time from urllib.request import Request, urlopen from urllib.parse import urlencode, urlparse, parse_qs import boto3 ORG = os.environ["SENTRY_ORG"].strip() TOKEN = os.environ["SENTRY_AUTH_TOKEN"].strip() S3_BUCKET = os.environ["S3_BUCKET"] S3_PREFIX = os.environ.get("S3_PREFIX", "sentry/events/") STATE_KEY = os.environ.get("STATE_KEY", "sentry/events/state.json") BASE = os.environ.get("SENTRY_API_BASE", "https://sentry.io").rstrip("/") MAX_PROJECTS = int(os.environ.get("MAX_PROJECTS", "100")) MAX_PAGES_PER_PROJECT = int(os.environ.get("MAX_PAGES_PER_PROJECT", "5")) s3 = boto3.client("s3") HDRS = {"Authorization": f"Bearer {TOKEN}", "Accept": "application/json", "User-Agent": "chronicle-s3-sentry-lambda/1.0"} def _get_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) raw = obj["Body"].read() return json.loads(raw) if raw else {"projects": {}} except Exception: return {"projects": {}} def _put_state(state: dict): s3.put_object(Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(state, separators=(",", ":")).encode("utf-8")) def _req(path: str, params: dict | None = None): url = f"{BASE}{path}" if params: url = f"{url}?{urlencode(params)}" req = Request(url, method="GET", headers=HDRS) with urlopen(req, timeout=60) as r: data = json.loads(r.read().decode("utf-8")) link = r.headers.get("Link") return data, link def _parse_link(link_header: str | None): """Return (prev_cursor, prev_has_more, next_cursor, next_has_more).""" if not link_header: return None, False, None, False prev_cursor, next_cursor = None, None prev_more, next_more = False, False parts = [p.strip() for p in link_header.split(",")] for p in parts: if "<" not in p or ">" not in p: continue url = p.split("<", 1)[1].split(">", 1)[0] rel = "previous" if 'rel="previous"' in p else ("next" if 'rel="next"' in p else None) has_more = 'results="true"' in p try: q = urlparse(url).query cur = parse_qs(q).get("cursor", [None])[0] except Exception: cur = None if rel == "previous": prev_cursor, prev_more = cur, has_more elif rel == "next": next_cursor, next_more = cur, has_more return prev_cursor, prev_more, next_cursor, next_more def _write_page(project_slug: str, payload: object, page_idx: int) -> str: ts = time.gmtime() key = f"{S3_PREFIX.rstrip('/')}/{time.strftime('%Y/%m/%d', ts)}/sentry-{project_slug}-{page_idx:05d}.json" s3.put_object(Bucket=S3_BUCKET, Key=key, Body=json.dumps(payload, separators=(",", ":")).encode("utf-8")) return key def list_projects(max_projects: int): projects, cursor = [], None while len(projects) < max_projects: params = {"cursor": cursor} if cursor else {} data, link = _req(f"/api/0/organizations/{ORG}/projects/", params) for p in data: slug = p.get("slug") if slug: projects.append(slug) if len(projects) >= max_projects: break # advance pagination _, _, next_cursor, next_more = _parse_link(link) cursor = next_cursor if next_more else None if not next_more: break return projects def fetch_project_events(project_slug: str, start_prev_cursor: str | None): # If we have a stored "previous" cursor, poll forward (newer) until no more results. # If not (first run), fetch the latest page, then optionally follow "next" (older) for initial backfill up to the limit. pages = 0 total = 0 latest_prev_cursor_to_store = None def _one(cursor: str | None): nonlocal pages, total, latest_prev_cursor_to_store params = {"cursor": cursor} if cursor else {} data, link = _req(f"/api/0/projects/{ORG}/{project_slug}/events/", params) _write_page(project_slug, data, pages) total += len(data) if isinstance(data, list) else 0 prev_c, prev_more, next_c, next_more = _parse_link(link) # capture the most recent "previous" cursor observed to store for the next run latest_prev_cursor_to_store = prev_c or latest_prev_cursor_to_store pages += 1 return prev_c, prev_more, next_c, next_more if start_prev_cursor: # Poll new pages toward "previous" until no more cur = start_prev_cursor while pages < MAX_PAGES_PER_PROJECT: prev_c, prev_more, _, _ = _one(cur) if not prev_more: break cur = prev_c else: # First run: start at newest, then (optionally) backfill a few older pages prev_c, _, next_c, next_more = _one(None) cur = next_c while next_more and pages < MAX_PAGES_PER_PROJECT: _, _, next_c, next_more = _one(cur) cur = next_c return {"project": project_slug, "pages": pages, "written": total, "store_prev_cursor": latest_prev_cursor_to_store} def lambda_handler(event=None, context=None): state = _get_state() state.setdefault("projects", {}) projects = list_projects(MAX_PROJECTS) summary = [] for slug in projects: start_prev = state["projects"].get(slug, {}).get("prev_cursor") res = fetch_project_events(slug, start_prev) if res.get("store_prev_cursor"): state["projects"][slug] = {"prev_cursor": res["store_prev_cursor"]} summary.append(res) _put_state(state) return {"ok": True, "projects": len(projects), "summary": summary} if __name__ == "__main__": print(lambda_handler())
Aceda a Configuração > Variáveis de ambiente.
Clique em Editar > Adicionar nova variável de ambiente.
Introduza as variáveis de ambiente fornecidas na tabela seguinte, substituindo os valores de exemplo pelos seus valores.
Variáveis de ambiente
Chave Exemplo de valor Descrição S3_BUCKET
sentry-logs
Nome do contentor do S3 onde os dados vão ser armazenados. S3_PREFIX
sentry/events/
Prefixo S3 opcional (subpasta) para objetos. STATE_KEY
sentry/events/state.json
Chave de ficheiro de estado/ponto de verificação opcional. SENTRY_ORG
your-org-slug
Slug da organização do Sentry. SENTRY_AUTH_TOKEN
sntrys_************************
Token de autorização do Sentry com org:read, project:read e event:read. SENTRY_API_BASE
https://sentry.io
URL base da API Sentry (autoalojado: https://<your-domain>
).MAX_PROJECTS
100
Número máximo de projetos a processar. MAX_PAGES_PER_PROJECT
5
Número máximo de páginas por projeto por execução. Depois de criar a função, permaneça na respetiva página (ou abra Lambda > Functions > a sua função).
Selecione o separador Configuração.
No painel Configuração geral, clique em Editar.
Altere Tempo limite para 5 minutos (300 segundos) e clique em Guardar.
Crie um horário do EventBridge
- Aceda a Amazon EventBridge > Scheduler > Create schedule.
- Indique os seguintes detalhes de configuração:
- Agenda recorrente: Taxa (
1 hour
). - Destino: a sua função Lambda
sentry_to_s3
. - Nome:
sentry-1h
.
- Agenda recorrente: Taxa (
- Clique em Criar programação.
(Opcional) Crie um utilizador e chaves da IAM só de leitura para o Google SecOps
- Na consola da AWS, aceda a IAM > Utilizadores.
- Clique em Adicionar utilizadores.
- Indique os seguintes detalhes de configuração:
- Utilizador: introduza
secops-reader
. - Tipo de acesso: selecione Chave de acesso – Acesso programático.
- Utilizador: introduza
- Clique em Criar utilizador.
- Anexe a política de leitura mínima (personalizada): Users > secops-reader > Permissions > Add permissions > Attach policies directly > Create policy.
JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::sentry-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::sentry-logs" } ] }
Nome =
secops-reader-policy
.Clique em Criar política > procure/selecione > Seguinte > Adicionar autorizações.
Crie uma chave de acesso para
secops-reader
: Credenciais de segurança > Chaves de acesso.Clique em Criar chave de acesso.
Transfira o
.CSV
. (Vai colar estes valores no feed).
Configure um feed no Google SecOps para carregar registos do Sentry
- Aceda a Definições do SIEM > Feeds.
- Clique em + Adicionar novo feed.
- No campo Nome do feed, introduza um nome para o feed (por exemplo,
Sentry Logs
). - Selecione Amazon S3 V2 como o Tipo de origem.
- Selecione Sentry como o Tipo de registo.
- Clicar em Seguinte.
- Especifique valores para os seguintes parâmetros de entrada:
- URI do S3:
s3://sentry-logs/sentry/events/
- Opções de eliminação de origens: selecione a opção de eliminação de acordo com a sua preferência.
- Idade máxima do ficheiro: inclua ficheiros modificados no último número de dias. A predefinição é 180 dias.
- ID da chave de acesso: chave de acesso do utilizador com acesso ao contentor do S3.
- Chave de acesso secreta: chave secreta do utilizador com acesso ao contentor do S3.
- Espaço de nomes do recurso: o espaço de nomes do recurso.
- Etiquetas de carregamento: a etiqueta aplicada aos eventos deste feed.
- URI do S3:
- Clicar em Seguinte.
- Reveja a nova configuração do feed no ecrã Finalizar e, de seguida, clique em Enviar.
Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais da Google SecOps.