收集 OAuth2 Proxy 記錄
本文說明如何使用 Google Cloud Storage V2,將 OAuth2 Proxy 記錄檔擷取至 Google Security Operations。
OAuth2 Proxy 是 CNCF Sandbox 反向 Proxy,可使用 OAuth2/OIDC 提供者 (Google、GitHub、Keycloak、Azure AD 等) 進行驗證,並依據電子郵件、網域或群組驗證帳戶。這項服務會產生驗證記錄 (登入成功/失敗)、要求記錄 (含使用者身分識別的代理 HTTP 要求) 和標準應用程式記錄。由於 OAuth2 Proxy 會在 Kubernetes 中以容器形式執行,並將所有記錄寫入 stdout,因此系統會使用 Kubernetes 原生記錄收集器 (Fluentd),將記錄轉送至 GCS 儲存空間,供 Google SecOps 擷取。
事前準備
請確認您已完成下列事前準備事項:
- Google SecOps 執行個體
- 已啟用 Cloud Storage API 的 GCP 專案
- 建立及管理 GCS bucket 的權限
- 管理 Google Cloud Storage 值區 IAM 政策的權限
- 已部署 OAuth2 Proxy 的 Kubernetes 叢集 (透過 Helm 資訊套件或手動部署)
kubectl存取 Kubernetes 叢集,並具備建立 DaemonSet、ConfigMap、密鑰和命名空間的權限- 具有目標 GCS bucket
storage.objects.create權限的 GCP 服務帳戶 JSON 金鑰
建立 Google Cloud Storage bucket
- 前往 Google Cloud 控制台。
- 選取專案或建立新專案。
- 在導覽選單中,依序前往「Cloud Storage」>「Bucket」。
- 按一下「建立值區」。
請提供下列設定詳細資料:
設定 值 為 bucket 命名 輸入全域不重複的名稱 (例如 oauth2-proxy-logs-bucket)位置類型 根據需求選擇 (區域、雙區域、多區域) 位置 選取位置 (例如 us-central1)儲存空間級別 標準 (建議用於經常存取的記錄) 存取控管 統一 (建議) 保護工具 選用:啟用物件版本管理或資料保留政策 點選「建立」。
設定 OAuth2 Proxy 記錄
OAuth2 Proxy 會將三種類型的記錄寫入 stdout:標準記錄、驗證記錄和要求記錄。這三項功能預設為啟用。為確保記錄包含與安全性相關的詳細資料,請使用下列記錄旗標設定 OAuth2 Proxy。
方法 1:透過 Helm 資訊套件值設定
如果 OAuth2 Proxy 是使用官方 Helm Chart 部署,請將下列內容新增至
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 Proxy 是使用 Kubernetes Deployment 資訊清單部署,請將下列引數新增至容器規格:
args: - --standard-logging=true - --auth-logging=true - --request-logging=true - --silence-ping-logging=true
選項 3:透過環境變數設定
在 OAuth2 Proxy 容器中設定下列環境變數:
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 Proxy 記錄
套用設定後,請確認 OAuth2 Proxy 是否產生記錄:
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 bucket 寫入權限的 GCP 服務帳戶。
- 在 GCP 控制台中,依序前往「IAM & Admin」(IAM 與管理) >「Service Accounts」(服務帳戶)。
- 按一下「Create Service Account」(建立服務帳戶)。
- 請提供下列設定詳細資料:
- 服務帳戶名稱:輸入
fluentd-gcs-writer - 服務帳戶說明:輸入
Service account for Fluentd to write OAuth2 Proxy logs to GCS
- 服務帳戶名稱:輸入
- 按一下「建立並繼續」。
- 在「將專案存取權授予這個服務帳戶」部分:
- 按一下「選擇角色」。
- 搜尋並選取「Storage 物件管理員」。
- 按一下「繼續」。
- 按一下 [完成]。
建立及下載服務帳戶金鑰
- 在「Service Accounts」(服務帳戶) 清單中,按一下
fluentd-gcs-writer服務帳戶。 - 前往「金鑰」分頁標籤。
- 依序點選「新增金鑰」>「建立新的金鑰」。
- 選取「JSON」做為金鑰類型。
- 點選「建立」。
- 請妥善儲存下載的 JSON 金鑰檔案。這個檔案會在下一個步驟中使用。
為服務帳戶金鑰建立 Kubernetes Secret
在要部署 Fluentd 的命名空間中,建立包含 GCP 服務帳戶金鑰的 Kubernetes 密鑰:
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 Proxy 記錄
在 Kubernetes 叢集中將 Fluentd 部署為 DaemonSet,從 OAuth2 Proxy Pod 收集容器記錄,並轉送至 GCS bucket。
建立 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 bucket 名稱套用 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 部署作業
確認每個節點上都正在執行 Fluentd Pod:
kubectl get pods -n logging -l k8s-app=fluentd-gcs檢查 Fluentd 記錄,確認是否成功寫入 GCS:
kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20確認記錄檔顯示在 GCS bucket 中:
- 前往 GCP Console 的「Cloud Storage」>「Buckets」頁面。
- 點選 bucket (例如
oauth2-proxy-logs-bucket)。 - 前往
oauth2-proxy-logs/資料夾。 - 確認是否有時間戳記為最近的
.json檔案。
擷取 Google SecOps 服務帳戶
Google SecOps 會使用專屬服務帳戶,從 GCS bucket 讀取資料。您必須授予這個服務帳戶值區存取權。
取得服務帳戶電子郵件地址
- 依序前往「SIEM 設定」>「動態饋給」。
- 按一下「新增動態消息」。
- 按一下「設定單一動態饋給」。
- 在「動態饋給名稱」欄位中輸入動態饋給名稱 (例如
OAuth2 Proxy Logs)。 - 選取「Google Cloud Storage V2」做為「來源類型」。
- 選取「Kubernetes Auth Proxy」做為「記錄類型」。
按一下「取得服務帳戶」,系統會顯示專屬服務帳戶電子郵件地址,例如:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com複製這個電子郵件地址,以便在下一步中使用。
點選「下一步」。
指定下列輸入參數的值:
儲存空間 bucket URL:輸入 GCS bucket URI,並加上前置路徑:
gs://oauth2-proxy-logs-bucket/oauth2-proxy-logs/請將
oauth2-proxy-logs-bucket替換為您的 GCS bucket 名稱。
來源刪除選項:根據偏好設定選取刪除選項:
- 永不:移轉後一律不刪除任何檔案 (建議用於測試)。
- 刪除已轉移的檔案:成功轉移檔案後刪除檔案。
刪除已轉移的檔案和空白目錄:成功轉移後刪除檔案和空白目錄。
檔案存在時間上限:包含在過去天數內修改的檔案 (預設為 180 天)
資產命名空間:資產命名空間
擷取標籤:要套用至這個動態饋給事件的標籤
點選「下一步」。
在「Finalize」(完成) 畫面中檢查新的動態饋給設定,然後按一下「Submit」(提交)。
將 IAM 權限授予 Google SecOps 服務帳戶
Google SecOps 服務帳戶需要 GCS bucket 的「Storage 物件檢視者」角色。
- 依序前往「Cloud Storage」>「Buckets」。
- 點按 bucket 名稱 (例如
oauth2-proxy-logs-bucket)。 - 前往「權限」分頁標籤。
- 按一下「授予存取權」。
- 請提供下列設定詳細資料:
- 新增主體:貼上 Google SecOps 服務帳戶電子郵件地址 (例如
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com) - 指派角色:選取「Storage 物件檢視者」
- 新增主體:貼上 Google SecOps 服務帳戶電子郵件地址 (例如
- 按一下 [儲存]。
OAuth2 Proxy 記錄參考資料
下表說明 OAuth2 Proxy 產生的記錄檔欄位,以及這些欄位與安全性的關聯:
驗證記錄欄位
| 欄位 | 範例 | 說明 |
|---|---|---|
| 客戶 | 10.0.0.1 |
用戶端/遠端 IP 位址 (使用 X-Real-IP 標頭,如果 --reverse-proxy=true) |
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f |
X-Request-Id 標頭中的要求 ID (如果為空白,則為隨機 UUID) |
| 使用者名稱 | user@example.com |
驗證要求的電子郵件地址或使用者名稱 |
| 時間戳記 | 2024/01/15 10:30:00 |
驗證事件的日期和時間 |
| 狀態 | AuthSuccess |
驗證結果:AuthSuccess、AuthFailure 或 AuthError |
| 訊息 | 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 | 產品專屬記錄 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 | 工作階段 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 專業人員尋求答案。