收集 OAuth2 代理日志

支持的平台:

本文档介绍了如何使用 Google Cloud Storage V2 将 OAuth2 代理日志注入到 Google Security Operations。

OAuth2 代理是一个 CNCF 沙盒反向代理,可使用 OAuth2/OIDC 提供程序(Google、GitHub、Keycloak、Azure AD 等)进行身份验证,并通过电子邮件、网域或群组验证账号。它会生成身份验证日志(登录成功/失败)、请求日志(包含用户身份的代理 HTTP 请求)和标准应用日志。由于 OAuth2 代理在 Kubernetes 中以容器形式运行,并将所有日志写入 stdout,因此使用 Kubernetes 原生日志收集器 (Fluentd) 将日志转发到 GCS 存储桶,以供 Google SecOps 提取。

准备工作

请确保满足以下前提条件:

  • Google SecOps 实例
  • 已启用 Cloud Storage API 的 GCP 项目
  • 创建和管理 GCS 存储分区的权限
  • 管理 GCS 存储分区的 IAM 政策的权限
  • 已部署 OAuth2 代理(通过 Helm 图表或手动部署)的正在运行的 Kubernetes 集群
  • kubectl 具有创建 DaemonSet、ConfigMap、Secret 和命名空间的权限的 Kubernetes 集群访问权限
  • 具有目标 GCS 存储桶的 storage.objects.create 权限的 GCP 服务账号 JSON 密钥

创建 Google Cloud Storage 存储桶

  1. 前往 Google Cloud 控制台
  2. 选择您的项目或创建新项目。
  3. 在导航菜单中,依次前往 Cloud Storage > 存储分区
  4. 点击创建存储分区
  5. 提供以下配置详细信息:

    设置
    为存储桶命名 输入一个全局唯一的名称(例如 oauth2-proxy-logs-bucket
    位置类型 根据您的需求进行选择(区域级、双区域、多区域)
    位置 选择营业地点(例如 us-central1
    存储类别 标准(建议用于经常访问的日志)
    访问权限控制 均匀(推荐)
    保护工具 可选:启用对象版本控制或保留政策
  6. 点击创建

配置 OAuth2 代理日志记录

OAuth2 代理会将三种类型的日志写入标准输出:标准日志、身份验证日志和请求日志。默认情况下,这三种日志均处于启用状态。为确保日志包含尽可能多的安全相关详细信息,请使用以下日志记录标志配置 OAuth2 代理。

方法 1:通过 Helm 图表值进行配置

  • 如果 OAuth2 代理是使用官方 Helm 图表部署的,请将以下内容添加到您的 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}}"
    
  • 应用更新后的 Helm 值:

    helm upgrade oauth2-proxy oauth2-proxy/oauth2-proxy -f values.yaml -n <your-namespace>
    

方法 2:通过命令行标志进行配置

  • 如果 OAuth2 代理是使用 Kubernetes Deployment 清单部署的,请将以下实参添加到容器规范中:

    args:
      - --standard-logging=true
      - --auth-logging=true
      - --request-logging=true
      - --silence-ping-logging=true
    

选项 3:通过环境变量进行配置

  • 在 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"
    

验证 OAuth2 代理日志记录

  • 应用配置后,验证 OAuth2 代理是否正在生成日志:

    kubectl logs -l app=oauth2-proxy -n <your-namespace> --tail=20
    
  • 输出包含类似于以下内容的行:

    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
    

为 Fluentd 创建 GCP 服务账号

Fluentd 需要具有对 GCS 存储桶的写入权限的 GCP 服务账号。

  1. GCP 控制台中,依次前往 IAM 和管理 > 服务账号
  2. 点击创建服务账号
  3. 提供以下配置详细信息:
    • 服务账号名称:输入 fluentd-gcs-writer
    • 服务账号说明:输入 Service account for Fluentd to write OAuth2 Proxy logs to GCS
  4. 点击创建并继续
  5. 向此服务账号授予对项目的访问权限部分:
    1. 点击选择角色
    2. 搜索并选择 Storage Object Admin
  6. 点击继续
  7. 点击完成

创建并下载服务账号密钥

  1. 服务账号列表中,点击 fluentd-gcs-writer 服务账号。
  2. 前往密钥标签页。
  3. 点击添加密钥 > 创建新密钥
  4. 选择 JSON 作为密钥类型。
  5. 点击创建
  6. 请妥善保存下载的 JSON 密钥文件。此文件将在下一步中使用。

为服务账号密钥创建 Kubernetes Secret

  • 在将部署 Fluentd 的命名空间中,创建一个包含 GCP 服务账号密钥的 Kubernetes Secret:

    kubectl create namespace logging
    kubectl create secret generic fluentd-gcs-key \
      --from-file=service-account-key.json=<path-to-downloaded-key>.json \
      -n logging
    

部署 Fluentd DaemonSet 以收集 OAuth2 代理日志

在 Kubernetes 集群中将 Fluentd 部署为 DaemonSet,以从 OAuth2 代理 pod 收集容器日志并将其转发到 GCS 存储桶。

创建 Fluentd ConfigMap

  • 创建一个名为 fluentd-configmap.yaml 的文件,其中包含以下内容:

    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>
    

替换以下值:

  • YOUR_GCP_PROJECT_ID:您的 GCP 项目 ID(例如 my-project-123456
  • oauth2-proxy-logs-bucket:之前创建的 GCS 存储桶的名称

  • 应用 ConfigMap:

    kubectl apply -f fluentd-configmap.yaml
    

创建 Fluentd DaemonSet

  • 创建一个名为 fluentd-daemonset.yaml 的文件,其中包含以下内容:

    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: {}
    
  • 应用 DaemonSet:

    kubectl apply -f fluentd-daemonset.yaml
    

验证 Fluentd 部署

  1. 验证 Fluentd pod 是否在每个节点上运行:

    kubectl get pods -n logging -l k8s-app=fluentd-gcs
    
  2. 检查 Fluentd 日志,确认是否已成功写入 GCS:

    kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20
    
  3. 验证日志文件是否显示在 GCS 存储桶中:

    1. 在 GCP Console 中,前往 Cloud Storage > 存储分区
    2. 点击相应存储桶(例如 oauth2-proxy-logs-bucket)。
    3. 转到 oauth2-proxy-logs/ 文件夹。
    4. 验证是否存在具有最新时间戳的 .json 文件。

检索 Google SecOps 服务账号

Google SecOps 使用唯一的服务账号从您的 GCS 存储桶中读取数据。您必须授予此服务账号对您的存储桶的访问权限。

获取服务账号电子邮件地址

  1. 依次前往 SIEM 设置 > Feed
  2. 点击添加新 Feed
  3. 点击配置单个 Feed
  4. Feed 名称字段中,输入 Feed 的名称(例如 OAuth2 Proxy Logs)。
  5. 选择 Google Cloud Storage V2 作为来源类型
  6. 选择 Kubernetes Auth Proxy 作为日志类型
  7. 点击获取服务账号。系统会显示一个唯一的服务账号电子邮件地址,例如:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. 复制此电子邮件地址,以便在下一步中使用。

  9. 点击下一步

  10. 为以下输入参数指定值:

    • 存储桶网址:输入带有前缀路径的 GCS 存储桶 URI:

      gs://oauth2-proxy-logs-bucket/oauth2-proxy-logs/
      
    • oauth2-proxy-logs-bucket 替换为您的 GCS 存储桶名称。

    • 来源删除选项:根据您的偏好选择删除选项:

      • 永不:转移后永不删除任何文件(建议用于测试)。
      • 删除已转移的文件:在成功转移后删除文件。
      • 删除已转移的文件和空目录:成功转移后删除文件和空目录。

    • 文件存在时间上限:包含在过去指定天数内修改的文件(默认值为 180 天)

    • 资产命名空间资产命名空间

    • 注入标签:要应用于此 Feed 中事件的标签

  11. 点击下一步

  12. 最终确定界面中查看新的 Feed 配置,然后点击提交

向 Google SecOps 服务账号授予 IAM 权限

Google SecOps 服务账号需要您的 GCS 存储桶的 Storage Object Viewer 角色。

  1. 前往 Cloud Storage > 存储分区
  2. 点击您的存储桶名称(例如 oauth2-proxy-logs-bucket)。
  3. 前往权限标签页。
  4. 点击授予访问权限
  5. 提供以下配置详细信息:
    • 添加主账号:粘贴 Google SecOps 服务账号电子邮件地址(例如 chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    • 分配角色:选择 Storage Object Viewer
  6. 点击保存

OAuth2 代理日志参考文档

下表介绍了 OAuth2 代理生成的日志字段及其安全相关性:

身份验证日志字段

字段 示例 说明
客户 10.0.0.1 客户端/远程 IP 地址(如果 --reverse-proxy=true,则使用 X-Real-IP 标头)
RequestID 00010203-0405-4607-8809-0a0b0c0d0e0f X-Request-Id 标头中的请求 ID(如果为空,则为随机 UUID)
用户名 user@example.com 身份验证请求的电子邮件地址或用户名
时间戳 2024/01/15 10:30:00 身份验证事件的日期和时间
状态 AuthSuccess 身份验证结果:AuthSuccessAuthFailureAuthError
消息 Authenticated via OAuth2 身份验证尝试的详细信息

请求日志字段

字段 示例 说明
客户 10.0.0.1 客户端/远程 IP 地址
RequestID 00010203-0405-4607-8809-0a0b0c0d0e0f 请求 ID
用户名 user@example.com 经过身份验证的用户的电子邮件地址
时间戳 2024/01/15 10:30:01 请求的日期和时间
主机 app.example.com Host 标头的值
RequestMethod GET HTTP 请求方法
上行 10.0.0.5:8080 处理请求的上游服务器
RequestURI /dashboard 请求的 URI 路径
协议 HTTP/1.1 请求协议
UserAgent Mozilla/5.0 完整的用户代理字符串
StatusCode 200 HTTP 响应状态代码
ResponseSize 1234 响应大小(以字节为单位)
RequestDuration 0.005 请求处理时间(以秒为单位)

UDM 映射表

日志字段 UDM 映射 逻辑
关于 关于 活动相关信息
http_req_id_field additional.fields 标准 UDM 架构未涵盖的其他字段
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 事件的说明
event_type metadata.event_type 事件类型
labels.request_id metadata.product_log_id 特定于产品的日志标识符
insertId metadata.product_log_id
jsonPayload.chartVersion metadata.product_version 产品版本
httpRequest.protocol network.application_protocol 网络连接中使用的应用协议
network.direction network.direction 网络流量方向
httpRequest.requestMethod network.http.method HTTP 方法
http_method network.http.method
httpRequest.status network.http.response_code HTTP 响应代码
httpRequest.userAgent network.http.user_agent HTTP 用户代理
requestMetadata.callerSuppliedUserAgent network.http.user_agent
labels.protocol network.ip_protocol IP 协议
httpRequest.responseSize network.received_bytes 接收的字节数
labels.total_received_bytes network.received_bytes
httpRequest.requestSize network.sent_bytes 发送的字节数
labels.total_sent_bytes network.sent_bytes
jsonPayload.session network.session_id 会话标识符
labels.service_authentication_policy network.tls.cipher TLS 加密套件
主账号 主账号 涉及事件的主账号实体
principal_hostname principal.hostname 主账号的主机名
prin_userid principal.user.userid 主账号的用户 ID
security_result security_result 安全评估结果
目标 目标 涉及事件的目标实体
target_hostname target.hostname 目标的主机名
resource_sub_type target.resource.resource_subtype 目标资源的子类型
target_userid target.user.userid 目标的相应用户 ID
metadata.product_name metadata.product_name 产品名称
metadata.vendor_name metadata.vendor_name 供应商名称

更新日志

查看相应解析器的更改日志

需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。