OAuth2 프록시 로그 수집
이 문서에서는 Google Cloud Storage V2를 사용하여 OAuth2 Proxy 로그를 Google Security Operations로 수집하는 방법을 설명합니다.
OAuth2 Proxy는 CNCF 샌드박스 리버스 프록시로, OAuth2/OIDC 제공업체 (Google, GitHub, Keycloak, Azure AD 등)를 사용하여 이메일, 도메인 또는 그룹별로 계정을 검증하는 인증을 제공합니다. 인증 로그 (로그인 성공/실패), 요청 로그 (사용자 ID가 포함된 프록시 HTTP 요청), 표준 애플리케이션 로그를 생성합니다. OAuth2 Proxy는 Kubernetes에서 컨테이너로 실행되고 모든 로그를 stdout에 쓰기 때문에 Kubernetes 네이티브 로그 수집기 (Fluentd)가 Google SecOps 수집을 위해 로그를 GCS 버킷으로 전달하는 데 사용됩니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스
- Cloud Storage API가 사용 설정된 GCP 프로젝트
- GCS 버킷을 만들고 관리할 수 있는 권한
- GCS 버킷의 IAM 정책을 관리할 수 있는 권한
- OAuth2 프록시가 배포된 실행 중인 Kubernetes 클러스터 (Helm 차트 또는 수동 배포를 통해)
- DaemonSet, ConfigMap, 보안 비밀, 네임스페이스를 만들 권한이 있는 Kubernetes 클러스터에 대한
kubectl액세스 - 타겟 GCS 버킷에 대한
storage.objects.create권한이 있는 GCP 서비스 계정 JSON 키
Google Cloud Storage 버킷 만들기
- Google Cloud 콘솔로 이동합니다.
- 프로젝트를 선택하거나 새 프로젝트를 만듭니다.
- 탐색 메뉴에서 Cloud Storage> 버킷으로 이동합니다.
- 버킷 만들기를 클릭합니다.
다음 구성 세부정보를 제공합니다.
설정 값 버킷 이름 지정 전역적으로 고유한 이름 (예: oauth2-proxy-logs-bucket)을 입력합니다.위치 유형 필요에 따라 선택 (리전, 이중 리전, 멀티 리전) 위치 위치를 선택합니다 (예: us-central1).스토리지 클래스 Standard (자주 액세스하는 로그에 권장) 액세스 제어 균일 (권장) 보호 조치 선택사항: 객체 버전 관리 또는 보관 정책 사용 설정 만들기를 클릭합니다.
OAuth2 프록시 로깅 구성
OAuth2 프록시는 표준 로그, 인증 로그, 요청 로그의 세 가지 유형의 로그를 stdout에 씁니다. 세 가지 모두 기본적으로 사용 설정되어 있습니다. 로그에 보안 관련 세부정보가 최대한 많이 포함되도록 하려면 다음 로깅 플래그를 사용하여 OAuth2 프록시를 구성하세요.
옵션 1: Helm 차트 값을 통해 구성
공식 Helm 차트를 사용하여 OAuth2 프록시를 배포하는 경우
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: 명령줄 플래그를 통해 구성
Kubernetes 배포 매니페스트를 사용하여 OAuth2 프록시를 배포하는 경우 컨테이너 사양에 다음 인수를 추가합니다.
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 서비스 계정이 필요합니다.
- GCP 콘솔에서 IAM 및 관리자 > 서비스 계정으로 이동합니다.
- 서비스 계정 만들기를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 서비스 계정 이름:
fluentd-gcs-writer을 입력합니다. - 서비스 계정 설명:
Service account for Fluentd to write OAuth2 Proxy logs to GCS를 입력합니다.
- 서비스 계정 이름:
- 만들고 계속하기를 클릭합니다.
- 이 서비스 계정에 프로젝트에 대한 액세스 권한 부여 섹션에서 다음 단계를 따르세요.
- 역할 선택을 클릭합니다.
- 스토리지 객체 관리자를 검색하여 선택합니다.
- 계속을 클릭합니다.
- 완료를 클릭합니다.
서비스 계정 키 만들기 및 다운로드
- 서비스 계정 목록에서
fluentd-gcs-writer서비스 계정을 클릭합니다. - 키 탭으로 이동합니다.
- 키 추가 > 새 키 만들기를 클릭합니다.
- 키 유형으로 JSON을 선택합니다.
- 만들기를 클릭합니다.
- 다운로드한 JSON 키 파일을 안전하게 저장합니다. 이 파일은 다음 단계에서 사용됩니다.
서비스 계정 키의 Kubernetes 보안 비밀 만들기
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 프록시 로그 수집
Kubernetes 클러스터에 Fluentd를 DaemonSet으로 배포하여 OAuth2 프록시 포드에서 컨테이너 로그를 수집하고 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 배포 확인
Fluentd 포드가 각 노드에서 실행 중인지 확인합니다.
kubectl get pods -n logging -l k8s-app=fluentd-gcsFluentd 로그에서 GCS 쓰기가 성공했는지 확인합니다.
kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20로그 파일이 GCS 버킷에 표시되는지 확인합니다.
- GCP 콘솔에서 Cloud Storage> 버킷으로 이동합니다.
- 버킷 (예:
oauth2-proxy-logs-bucket)을 클릭합니다. oauth2-proxy-logs/폴더로 이동합니다..json파일이 최신 타임스탬프와 함께 있는지 확인합니다.
Google SecOps 서비스 계정 가져오기
Google SecOps는 고유한 서비스 계정을 사용하여 GCS 버킷에서 데이터를 읽습니다. 이 서비스 계정에 버킷 액세스 권한을 부여해야 합니다.
서비스 계정 이메일 가져오기
- SIEM 설정> 피드로 이동합니다.
- 새 피드 추가를 클릭합니다.
- 단일 피드 구성을 클릭합니다.
- 피드 이름 필드에 피드 이름을 입력합니다(예:
OAuth2 Proxy Logs). - 소스 유형으로 Google Cloud Storage V2를 선택합니다.
- 로그 유형으로 Kubernetes Auth Proxy를 선택합니다.
서비스 계정 가져오기를 클릭합니다. 고유한 서비스 계정 이메일이 표시됩니다(예:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com다음 단계에서 사용할 수 있도록 이 이메일 주소를 복사합니다.
다음을 클릭합니다.
다음 입력 매개변수의 값을 지정합니다.
스토리지 버킷 URL: 다음 접두사 경로를 사용하여 GCS 버킷 URI를 입력합니다.
gs://oauth2-proxy-logs-bucket/oauth2-proxy-logs/oauth2-proxy-logs-bucket을 GCS 버킷 이름으로 바꿉니다.
소스 삭제 옵션: 환경설정에 따라 삭제 옵션을 선택합니다.
- 삭제 안함: 전송 후 파일을 삭제하지 않습니다 (테스트에 권장).
- 전송된 파일 삭제: 전송이 완료되면 파일을 삭제합니다.
전송된 파일 및 빈 디렉터리 삭제: 전송이 완료되면 파일과 빈 디렉터리를 삭제합니다.
최대 파일 기간: 지난 일수 동안 수정된 파일을 포함합니다 (기본값은 180일).
애셋 네임스페이스: 애셋 네임스페이스
수집 라벨: 이 피드의 이벤트에 적용할 라벨입니다.
다음을 클릭합니다.
확정 화면에서 새 피드 구성을 검토한 다음 제출을 클릭합니다.
Google SecOps 서비스 계정에 IAM 권한 부여
Google SecOps 서비스 계정에는 GCS 버킷에 대한 스토리지 객체 뷰어 역할이 필요합니다.
- Cloud Storage> 버킷으로 이동합니다.
- 버킷 이름 (예:
oauth2-proxy-logs-bucket)을 클릭합니다. - 권한 탭으로 이동합니다.
- 액세스 권한 부여를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 주 구성원 추가: Google SecOps 서비스 계정 이메일 (예:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com)을 붙여넣습니다. - 역할 할당: 스토리지 객체 뷰어를 선택합니다.
- 주 구성원 추가: Google SecOps 서비스 계정 이메일 (예:
- 저장을 클릭합니다.
OAuth2 프록시 로그 참조
다음 표에서는 OAuth2 Proxy에서 생성된 로그 필드와 보안 관련성을 설명합니다.
인증 로그 필드
| 필드 | 예 | 설명 |
|---|---|---|
| 클라이언트 | 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 |
인증 결과: 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 |
호스트 헤더의 값 |
| 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 | principal | 이벤트에 관련된 주요 항목 |
| principal_hostname | principal.hostname | 주 구성원의 호스트 이름 |
| prin_userid | principal.user.userid | 주체의 사용자 ID |
| security_result | security_result | 보안 평가 결과 |
| target | target | 이벤트에 관련된 타겟 항목 |
| 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 전문가에게 문의하여 답변을 받으세요.