Coletar conjuntos de dados principais do Code42 Incydr

Compatível com:

Este documento explica como ingerir conjuntos de dados principais do Code42 Incydr (usuários, sessões, auditoria, casos e, opcionalmente, eventos de arquivo) no Google Security Operations usando o Amazon S3.

Antes de começar

  • Instância do Google SecOps
  • Acesso privilegiado ao Code42 Incydr.
  • Acesso privilegiado à AWS (S3, IAM, Lambda, EventBridge)

Coletar pré-requisitos de origem (IDs, chaves de API, IDs da organização, tokens)

  1. Faça login na UI da Web do Code42 Incydr.
  2. Acesse Administração > Integrações > Clientes da API.
  3. Crie um novo cliente.
  4. Copie e salve os seguintes detalhes em um local seguro:
    1. ID do cliente.
    2. Chave secreta do cliente.
    3. URL base: por exemplo, https://api.us.code42.com, https://api.us2.code42.com, https://api.ie.code42.com, https://api.gov.code42.com.

Configurar o bucket do AWS S3 e o IAM para o Google SecOps

  1. Crie um bucket do Amazon S3 seguindo este guia do usuário: Como criar um bucket.
  2. Salve o Nome e a Região do bucket para uso posterior.
  3. Crie um usuário seguindo este guia: Como criar um usuário do IAM.
  4. Selecione o usuário criado.
  5. Selecione a guia Credenciais de segurança.
  6. Clique em Criar chave de acesso na seção Chaves de acesso.
  7. Selecione Serviço de terceiros como o Caso de uso.
  8. Clique em Próxima.
  9. Opcional: adicione uma tag de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Fazer o download do arquivo CSV para salvar a chave de acesso e a chave de acesso secreta para uso posterior.
  12. Clique em Concluído.
  13. Selecione a guia Permissões.
  14. Clique em Adicionar permissões na seção Políticas de permissões.
  15. Selecione Adicionar permissões.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise e selecione a política AmazonS3FullAccess.
  18. Clique em Próxima.
  19. Clique em Adicionar permissões

Configurar o AWS Lambda para sondar o Code42 Incydr (sem transformação)

  1. No console da AWS, acesse Lambda > Functions > Create function.
  2. Clique em Criar do zero.
  3. Informe os seguintes detalhes de configuração:
    • Nome: insira um nome exclusivo e significativo (por exemplo, code42-incydr-pull).
    • Ambiente de execução: selecione Python 3.13.
    • Permissões: selecione uma função com s3:PutObject e Cloudwatch.
  4. Clique em Criar função.
  5. Selecione Configuração > Configuração geral > Editar.
  6. Configure Timeout=5m e Memory=1024 MB.
  7. Clique em Salvar.
  8. Selecione Configuração > Variáveis de ambiente > Editar > Adicionar.
    1. INCYDR_BASE_URL = https://api.us.code42.com
    2. INCYDR_CLIENT_ID = <Client ID>
    3. INCYDR_CLIENT_SECRET = <Client Secret>
    4. S3_BUCKET = code42-incydr
    5. S3_PREFIX = code42/
    6. PAGE_SIZE = 500
    7. LOOKBACK_MINUTES = 60
    8. STREAMS = users,sessions,audit,cases
    9. Opcional: FE_ADV_QUERY_JSON = ``
    10. Opcional: FE_PAGE_SIZE = 1000
  9. Clique em Salvar.
  10. Selecione Código e insira o seguinte código Python:

    import base64, json, os, time
    from datetime import datetime, timedelta, timezone
    from urllib.parse import urlencode
    from urllib.request import Request, urlopen
    import boto3
    
    BASE = os.environ["INCYDR_BASE_URL"].rstrip("/")
    CID = os.environ["INCYDR_CLIENT_ID"]
    CSECRET = os.environ["INCYDR_CLIENT_SECRET"]
    BUCKET = os.environ["S3_BUCKET"]
    PREFIX_BASE = os.environ.get("S3_PREFIX", "code42/")
    PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "500"))
    LOOKBACK_MINUTES = int(os.environ.get("LOOKBACK_MINUTES", "60"))
    STREAMS = [s.strip() for s in os.environ.get("STREAMS", "users").split(",") if s.strip()]
    FE_ADV_QUERY_JSON = os.environ.get("FE_ADV_QUERY_JSON", "").strip()
    FE_PAGE_SIZE = int(os.environ.get("FE_PAGE_SIZE", "1000"))
    
    s3 = boto3.client("s3")
    
    def now_utc():
        return datetime.now(timezone.utc)
    
    def iso_minus(minutes: int):
        return (now_utc() - timedelta(minutes=minutes)).strftime("%Y-%m-%dT%H:%M:%SZ")
    
    def put_bytes(key: str, body: bytes):
        s3.put_object(Bucket=BUCKET, Key=key, Body=body)
    
    def put_json(prefix: str, page_label: str, data):
        ts = now_utc().strftime("%Y/%m/%d/%H%M%S")
        key = f"{PREFIX_BASE}{prefix}{ts}-{page_label}.json"
        put_bytes(key, json.dumps(data).encode("utf-8"))
        return key
    
    def auth_header():
        auth = base64.b64encode(f"{CID}:{CSECRET}".encode()).decode()
        req = Request(f"{BASE}/v1/oauth", data=b"", method="POST")
        req.add_header("Authorization", f"Basic {auth}")
        req.add_header("Accept", "application/json")
        with urlopen(req, timeout=30) as r:
            data = json.loads(r.read().decode())
        return {"Authorization": f"Bearer {data['access_token']}", "Accept": "application/json"}
    
    def http_get(path: str, params: dict | None = None, headers: dict | None = None):
        url = f"{BASE}{path}"
        if params:
            url += ("?" + urlencode(params))
        req = Request(url, method="GET")
        for k, v in (headers or {}).items():
            req.add_header(k, v)
        with urlopen(req, timeout=60) as r:
            return r.read()
    
    def http_post_json(path: str, body: dict, headers: dict | None = None):
        url = f"{BASE}{path}"
        req = Request(url, data=json.dumps(body).encode("utf-8"), method="POST")
        req.add_header("Content-Type", "application/json")
        for k, v in (headers or {}).items():
            req.add_header(k, v)
        with urlopen(req, timeout=120) as r:
            return r.read()
    
    # USERS (/v1/users)
    
    def pull_users(hdrs):
        next_token = None
        pages = 0
        while True:
            params = {"active": "true", "blocked": "false", "pageSize": PAGE_SIZE}
            if next_token:
                params["pgToken"] = next_token
            raw = http_get("/v1/users", params, hdrs)
            data = json.loads(raw.decode())
            put_json("users/", f"users-page-{pages}", data)
            pages += 1
            next_token = data.get("nextPgToken") or data.get("next_pg_token")
            if not next_token:
                break
        return pages
    
    # SESSIONS (/v1/sessions) — alerts live inside sessions
    
    def pull_sessions(hdrs):
        start_iso = iso_minus(LOOKBACK_MINUTES)
        next_token = None
        pages = 0
        while True:
            params = {
                "hasAlerts": "true",
                "startTime": start_iso,
                "pgSize": PAGE_SIZE,
            }
            if next_token:
                params["pgToken"] = next_token
            raw = http_get("/v1/sessions", params, hdrs)
            data = json.loads(raw.decode())
            put_json("sessions/", f"sessions-page-{pages}", data)
            pages += 1
            next_token = data.get("nextPgToken") or data.get("next_page_token")
            if not next_token:
                break
        return pages
    
    # AUDIT LOG (/v1/audit) — CSV export or paged JSON; write as received
    
    def pull_audit(hdrs):
        start_iso = iso_minus(LOOKBACK_MINUTES)
        next_token = None
        pages = 0
        while True:
            params = {"startTime": start_iso, "pgSize": PAGE_SIZE}
            if next_token:
                params["pgToken"] = next_token
            raw = http_get("/v1/audit", params, hdrs)
            try:
                data = json.loads(raw.decode())
                put_json("audit/", f"audit-page-{pages}", data)
                next_token = data.get("nextPgToken") or data.get("next_page_token")
                pages += 1
                if not next_token:
                    break
            except Exception:
                ts = now_utc().strftime("%Y/%m/%d/%H%M%S")
                key = f"{PREFIX_BASE}audit/{ts}-audit-export.bin"
                put_bytes(key, raw)
                pages += 1
                break
        return pages
    
    # CASES (/v1/cases)
    
    def pull_cases(hdrs):
        next_token = None
        pages = 0
        while True:
            params = {"pgSize": PAGE_SIZE}
            if next_token:
                params["pgToken"] = next_token
            raw = http_get("/v1/cases", params, hdrs)
            data = json.loads(raw.decode())
            put_json("cases/", f"cases-page-{pages}", data)
            pages += 1
            next_token = data.get("nextPgToken") or data.get("next_page_token")
            if not next_token:
                break
        return pages
    
    # FILE EVENTS (/v2/file-events/search) — enabled only if you provide FE_ADV_QUERY_JSON
    
    def pull_file_events(hdrs):
        if not FE_ADV_QUERY_JSON:
            return 0
        try:
            base_query = json.loads(FE_ADV_QUERY_JSON)
        except Exception:
            raise RuntimeError("FE_ADV_QUERY_JSON is not valid JSON")
    
        pages = 0
        next_token = None
        while True:
            body = dict(base_query)
            body["pgSize"] = FE_PAGE_SIZE
            if next_token:
                body["pgToken"] = next_token
            raw = http_post_json("/v2/file-events/search", body, hdrs)
            data = json.loads(raw.decode())
            put_json("file_events/", f"fileevents-page-{pages}", data)
            pages += 1
            next_token = (
                data.get("nextPgToken")
                or data.get("next_page_token")
                or (data.get("file_events") or {}).get("nextPgToken")
            )
            if not next_token:
                break
        return pages
    
    def handler(event, context):
        hdrs = auth_header()
        report = {}
        if "users" in STREAMS:
            report["users_pages"] = pull_users(hdrs)
        if "sessions" in STREAMS:
            report["sessions_pages"] = pull_sessions(hdrs)
        if "audit" in STREAMS:
            report["audit_pages"] = pull_audit(hdrs)
        if "cases" in STREAMS:
            report["cases_pages"] = pull_cases(hdrs)
        if "file_events" in STREAMS:
            report["file_events_pages"] = pull_file_events(hdrs)
        return report
    
    def lambda_handler(event, context):
        return handler(event, context)
    
  11. Clique em Implantar.

Criar uma programação do EventBridge

  1. No console da AWS, acesse Amazon EventBridge > Regras.
  2. Clique em Criar regra.
  3. Informe os seguintes detalhes de configuração:
    • Padrão de programação: selecione Taxa fixa de 1 hora.
    • Nome: insira um nome exclusivo e significativo (por exemplo, code42-incydr-hourly).
    • Destino: selecione Função do Lambda e escolha code42-incydr-pull.
  4. Clique em Criar regra.

Opcional: criar um usuário e chaves do IAM somente leitura para o Google SecOps

  1. No console da AWS, acesse IAM > Usuários e clique em Adicionar usuários.
  2. Informe os seguintes detalhes de configuração:
    • Usuário: insira um nome exclusivo (por exemplo, secops-reader)
    • Tipo de acesso: selecione Chave de acesso - Acesso programático.
    • Clique em Criar usuário.
  3. Anexe a política de leitura mínima (personalizada): Usuários > selecione secops-reader > Permissões > Adicionar permissões > Anexar políticas diretamente > Criar política
  4. No editor JSON, insira a seguinte política:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "s3:GetObject"
          ],
          "Resource": "arn:aws:s3:::<your-bucket>/*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "s3:ListBucket"
          ],
          "Resource": "arn:aws:s3:::<your-bucket>"
        }
      ]
    }
    
  5. Defina o nome como secops-reader-policy.

  6. Acesse Criar política > pesquise/selecione > Próxima > Adicionar permissões.

  7. Acesse Credenciais de segurança > Chaves de acesso > Criar chave de acesso.

  8. Faça o download do CSV (esses valores são inseridos no feed).

Configurar um feed no Google SecOps para ingerir o registro do Code42 Incydr

  1. Acesse Configurações do SIEM > Feeds.
  2. Clique em Adicionar novo feed.
  3. No campo Nome do feed, insira um nome para o feed (por exemplo, Code42 Incydr Datasets).
  4. Selecione Amazon S3 V2 como o Tipo de origem.
  5. Selecione Code42 Incydr como o Tipo de registro.
  6. Clique em Próxima.
  7. Especifique valores para os seguintes parâmetros de entrada:
    • URI do S3: s3://code42-incydr/code42/
    • Opções de exclusão de fontes: selecione a opção de exclusão de acordo com sua preferência.
    • Idade máxima do arquivo: padrão de 180 dias.
    • ID da chave de acesso: chave de acesso do usuário com acesso ao bucket do S3.
    • Chave de acesso secreta: chave secreta do usuário com acesso ao bucket do S3.
    • Namespace do recurso: o namespace do recurso.
    • Rótulos de ingestão: o rótulo a ser aplicado aos eventos deste feed.
  8. Clique em Próxima.
  9. Revise a nova configuração do feed na tela Finalizar e clique em Enviar.

Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.