Coletar registros do proxy OAuth2
Este documento explica como ingerir registros do OAuth2 Proxy no Google Security Operations usando o Google Cloud Storage V2.
O OAuth2 Proxy é um proxy reverso do Sandbox da CNCF que fornece autenticação usando provedores OAuth2/OIDC (Google, GitHub, Keycloak, Azure AD e outros) para validar contas por e-mail, domínio ou grupo. Ele gera registros de autenticação (sucesso/falha de login), registros de solicitação (solicitações HTTP proxy com identidade do usuário) e registros de aplicativo padrão. Como o OAuth2 Proxy é executado como um contêiner no Kubernetes e grava todos os registros em stdout, um coletor de registros nativo do Kubernetes (Fluentd) é usado para encaminhar registros a um bucket do GCS para ingestão do Google SecOps.
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 gerenciar políticas do IAM em buckets do GCS
- Um cluster do Kubernetes em execução com o OAuth2 Proxy implantado (usando um gráfico do Helm ou implantação manual)
- Acesso de
kubectlao cluster do Kubernetes com permissões para criar DaemonSets, ConfigMaps, Secrets e Namespaces - Uma chave JSON de conta de serviço do GCP com permissão
storage.objects.createno bucket de destino do GCS
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, oauth2-proxy-logs-bucket.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.
Configurar o registro do proxy OAuth2
O OAuth2 Proxy grava três tipos de registros em stdout: registros padrão, de autenticação e de solicitação. Todas as três estão ativadas por padrão. Para garantir que os registros contenham o máximo de detalhes relevantes para a segurança, configure o proxy OAuth2 com as seguintes flags de geração de registros.
Opção 1: configurar usando valores de gráfico do Helm
Se o OAuth2 Proxy for implantado usando o gráfico oficial do Helm, adicione o seguinte ao arquivo
values.yaml:config: configFile: |- standard_logging = true auth_logging = true request_logging = true silence_ping_logging = true standard_logging_format = "[{{.Timestamp}}] [{{.File}}] {{.Message}}" auth_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}" request_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"Aplique os valores atualizados do Helm:
helm upgrade oauth2-proxy oauth2-proxy/oauth2-proxy -f values.yaml -n <your-namespace>
Opção 2: configurar usando flags da linha de comando
Se o OAuth2 Proxy for implantado usando um manifesto de implantação do Kubernetes, adicione os seguintes argumentos à especificação do contêiner:
args: - --standard-logging=true - --auth-logging=true - --request-logging=true - --silence-ping-logging=true
Opção 3: configurar usando variáveis de ambiente
Defina as seguintes variáveis de ambiente no contêiner do proxy OAuth2:
env: - name: OAUTH2_PROXY_STANDARD_LOGGING value: "true" - name: OAUTH2_PROXY_AUTH_LOGGING value: "true" - name: OAUTH2_PROXY_REQUEST_LOGGING value: "true" - name: OAUTH2_PROXY_SILENCE_PING_LOGGING value: "true"
Verificar o registro do proxy OAuth2
Depois de aplicar a configuração, verifique se o proxy OAuth2 está gerando registros:
kubectl logs -l app=oauth2-proxy -n <your-namespace> --tail=20A saída inclui linhas semelhantes a estas:
10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:00] [AuthSuccess] Authenticated via OAuth2 10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:01] example.com GET 10.0.0.5:8080 "/dashboard" HTTP/1.1 "Mozilla/5.0" 200 1234 0.005
Criar uma conta de serviço do GCP para o Fluentd
O Fluentd exige uma conta de serviço do GCP com acesso de gravação ao bucket do GCS.
- 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
fluentd-gcs-writer - Descrição da conta de serviço: insira
Service account for Fluentd to write OAuth2 Proxy logs to GCS
- Nome da conta de serviço: insira
- Clique em Criar e continuar.
- Na seção Conceder a essa conta de serviço acesso ao projeto:
- Clique em Selecionar papel.
- Pesquise e selecione Administrador de objetos do Storage.
- Clique em Continuar.
- Clique em Concluído.
Criar e fazer o download da chave da conta de serviço
- Na lista Contas de serviço, clique na conta de serviço
fluentd-gcs-writer. - Acesse a guia Chaves.
- Clique em Adicionar chave > Criar nova chave.
- Selecione JSON como o tipo de chave.
- Clique em Criar.
- Salve o arquivo de chave JSON baixado com segurança. Esse arquivo será usado na próxima etapa.
Criar um secret do Kubernetes para a chave da conta de serviço
Crie um secret do Kubernetes que contenha a chave da conta de serviço do GCP no namespace em que o Fluentd será implantado:
kubectl create namespace logging kubectl create secret generic fluentd-gcs-key \ --from-file=service-account-key.json=<path-to-downloaded-key>.json \ -n logging
Implante o DaemonSet do Fluentd para coletar registros do proxy OAuth2
Implante o Fluentd como um DaemonSet no cluster do Kubernetes para coletar registros de contêineres dos pods do proxy OAuth2 e encaminhá-los para o bucket do GCS.
Criar ConfigMap do Fluentd
Crie um arquivo chamado
fluentd-configmap.yamlcom o conteúdo a seguir:apiVersion: v1 kind: ConfigMap metadata: name: fluentd-gcs-config namespace: logging data: fluent.conf: | <source> @type tail read_from_head true tag kubernetes.* path /var/log/containers/*oauth2-proxy*.log pos_file /var/log/fluentd-oauth2-proxy.log.pos <parse> @type regexp expression /^(?<time>[^ ]+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/ time_format %Y-%m-%dT%H:%M:%S.%N%z </parse> </source> <filter kubernetes.**> @type kubernetes_metadata @id filter_kube_metadata </filter> <match kubernetes.**> @type gcs project YOUR_GCP_PROJECT_ID keyfile /etc/secrets/service-account-key.json bucket oauth2-proxy-logs-bucket path oauth2-proxy-logs/%Y/%m/%d/ object_key_format %{path}%{time_slice}_%{hostname}_%{index}.%{file_extension} <buffer tag,time> @type file path /var/log/fluentd/gcs timekey 300 timekey_wait 60 timekey_use_utc true chunk_limit_size 10MB </buffer> <format> @type json </format> </match>
Substitua os seguintes valores:
YOUR_GCP_PROJECT_ID: o ID do projeto do GCP (por exemplo,my-project-123456)oauth2-proxy-logs-bucket: o nome do bucket do GCS criado anteriormenteAplique o ConfigMap:
kubectl apply -f fluentd-configmap.yaml
Criar o DaemonSet do Fluentd
Crie um arquivo chamado
fluentd-daemonset.yamlcom o conteúdo a seguir:apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-gcs namespace: logging labels: k8s-app: fluentd-gcs spec: selector: matchLabels: k8s-app: fluentd-gcs template: metadata: labels: k8s-app: fluentd-gcs spec: tolerations: - key: node-role.kubernetes.io/control-plane effect: NoSchedule - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd-gcs image: fluent/fluentd-kubernetes-daemonset:v1-debian-gcs resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: fluentd-gcs-config-volume mountPath: /fluentd/etc/fluent.conf subPath: fluent.conf readOnly: true - name: fluentd-gcs-secrets-volume mountPath: /etc/secrets/service-account-key.json subPath: service-account-key.json readOnly: true - name: varlog mountPath: /var/log - name: dockercontainerlogdirectory mountPath: /var/log/pods readOnly: true - name: fluentd-buffer mountPath: /var/log/fluentd terminationGracePeriodSeconds: 30 volumes: - name: fluentd-gcs-config-volume configMap: name: fluentd-gcs-config - name: fluentd-gcs-secrets-volume secret: secretName: fluentd-gcs-key - name: varlog hostPath: path: /var/log - name: dockercontainerlogdirectory hostPath: path: /var/log/pods - name: fluentd-buffer emptyDir: {}Aplique o DaemonSet:
kubectl apply -f fluentd-daemonset.yaml
Verificar a implantação do Fluentd
Verifique se os pods do Fluentd estão em execução em cada nó:
kubectl get pods -n logging -l k8s-app=fluentd-gcsVerifique os registros do Fluentd para gravações bem-sucedidas do GCS:
kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20Verifique se os arquivos de registro aparecem no bucket do GCS:
- Acesse Cloud Storage > Buckets no console do GCP.
- Clique no bucket (por exemplo,
oauth2-proxy-logs-bucket). - Navegue até a pasta
oauth2-proxy-logs/. - Verifique se os arquivos
.jsonestão presentes com carimbos de data/hora recentes.
Recuperar a conta de serviço do Google SecOps
O Google SecOps usa uma conta de serviço exclusiva para ler dados do seu bucket do GCS. Você precisa conceder a essa conta de serviço acesso ao seu bucket.
Receber o e-mail da conta de serviço
- 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,
OAuth2 Proxy Logs). - Selecione Google Cloud Storage V2 como o Tipo de origem.
- Selecione Proxy de autenticação do Kubernetes 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://oauth2-proxy-logs-bucket/oauth2-proxy-logs/Substitua
oauth2-proxy-logs-bucketpelo nome do seu bucket do GCS.
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 (por exemplo,
oauth2-proxy-logs-bucket). - Acesse a guia Permissões.
- Clique em Conceder acesso.
- Informe os seguintes detalhes de configuração:
- Adicionar participantes: cole o e-mail da conta de serviço do Google SecOps (por exemplo,
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com). - Atribuir papéis: selecione Leitor de objetos do Storage.
- Adicionar participantes: cole o e-mail da conta de serviço do Google SecOps (por exemplo,
- Clique em Salvar.
Referência de registros do proxy OAuth2
A tabela a seguir descreve os campos de registro gerados pelo proxy OAuth2 e a relevância deles para a segurança:
Campos de registro de autenticação
| Campo | Exemplo | Descrição |
|---|---|---|
| Cliente | 10.0.0.1 |
Endereço IP do cliente/remoto (usa o cabeçalho X-Real-IP se --reverse-proxy=true) |
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f |
ID da solicitação do cabeçalho X-Request-Id (UUID aleatório se estiver vazio) |
| Nome de usuário | user@example.com |
E-mail ou nome de usuário da solicitação de autenticação |
| Carimbo de data/hora | 2024/01/15 10:30:00 |
Data e hora do evento de autenticação |
| Status | AuthSuccess |
Resultado da autenticação: AuthSuccess, AuthFailure ou AuthError |
| Mensagem | Authenticated via OAuth2 |
Detalhes da tentativa de autenticação |
Campos de registros de solicitação
| Campo | Exemplo | Descrição |
|---|---|---|
| Cliente | 10.0.0.1 |
Endereço IP do cliente/remoto |
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f |
ID da solicitação |
| Nome de usuário | user@example.com |
E-mail do usuário autenticado |
| Carimbo de data/hora | 2024/01/15 10:30:01 |
Data e hora da solicitação |
| Host | app.example.com |
Valor do cabeçalho "Host" |
| RequestMethod | GET |
Método de solicitação HTTP |
| Upstream | 10.0.0.5:8080 |
Servidor upstream que processou a solicitação |
| RequestURI | /dashboard |
Caminho do URI da solicitação |
| Protocolo | HTTP/1.1 |
Protocolo de solicitação |
| UserAgent | Mozilla/5.0 |
String completa do user agent |
| StatusCode | 200 |
Código de status da resposta HTTP |
| ResponseSize | 1234 |
Tamanho da resposta em bytes |
| RequestDuration | 0.005 |
Tempo de processamento da solicitação em segundos |
Tabela de mapeamento de UDM
| Campo de registro | Mapeamento do UDM | Lógica |
|---|---|---|
| sobre | sobre | Informações sobre o evento |
| http_req_id_field | additional.fields | Campos adicionais não cobertos pelo esquema UDM padrão |
| http_req_path_field | additional.fields | |
| k8s_pod_app_field | additional.fields | |
| k8s_pod_template_hash_field | additional.fields | |
| k8s_pod_tls_mode_field | additional.fields | |
| k8s_pod_canonical_revision_field | additional.fields | |
| k8s_pod_canonical_name_field | additional.fields | |
| pod_name | additional.fields | |
| cntnr_name | additional.fields | |
| destination_canonical_revision | additional.fields | |
| requested_server | additional.fields | |
| nodename_label | additional.fields | |
| componentName_label | additional.fields | |
| componentVersion_label | additional.fields | |
| azureResourceID_label | additional.fields | |
| producer_label | additional.fields | |
| first_label | additional.fields | |
| last_label | additional.fields | |
| meta_name | additional.fields | |
| resource_version_label | additional.fields | |
| request_apiVersion | additional.fields | |
| request_kind_label | additional.fields | |
| request_type_label | additional.fields | |
| response_apiVersion | additional.fields | |
| response_kind_label | additional.fields | |
| response_type_label | additional.fields | |
| jsonPayload.message | metadata.description | A descrição do evento |
| event_type | metadata.event_type | Tipo de evento |
| labels.request_id | metadata.product_log_id | Identificador de registro específico do produto. |
| insertId | metadata.product_log_id | |
| jsonPayload.chartVersion | metadata.product_version | Versão do produto |
| httpRequest.protocol | network.application_protocol | Protocolo de aplicativo usado na conexão de rede. |
| network.direction | network.direction | Direção do tráfego de rede |
| httpRequest.requestMethod | network.http.method | Método HTTP |
| http_method | network.http.method | |
| httpRequest.status | network.http.response_code | Código de resposta HTTP |
| httpRequest.userAgent | network.http.user_agent | User agent HTTP |
| requestMetadata.callerSuppliedUserAgent | network.http.user_agent | |
| labels.protocol | network.ip_protocol | Protocolo IP |
| httpRequest.responseSize | network.received_bytes | Número de bytes recebidos |
| labels.total_received_bytes | network.received_bytes | |
| httpRequest.requestSize | network.sent_bytes | Número de bytes enviados |
| labels.total_sent_bytes | network.sent_bytes | |
| jsonPayload.session | network.session_id | Identificador de sessão |
| labels.service_authentication_policy | network.tls.cipher | Pacote de criptografia TLS |
| participante | participante | Entidade principal envolvida no evento |
| principal_hostname | principal.hostname | Nome do host do principal |
| prin_userid | principal.user.userid | ID do usuário do principal |
| security_result | security_result | Resultado da avaliação de segurança |
| destino | destino | Entidade de destino envolvida no evento |
| target_hostname | target.hostname | Nome do host do destino |
| resource_sub_type | target.resource.resource_subtype | Subtipo do recurso de destino |
| target_userid | target.user.userid | ID do usuário do destino |
| metadata.product_name | metadata.product_name | Nome do produto |
| metadata.vendor_name | metadata.vendor_name | Nome do fornecedor |
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.