Workday 사용자 활동 로그 수집
이 문서에서는 Google Cloud Storage V2를 사용하여 Workday 사용자 활동 로그를 Google Security Operations로 수집하는 방법을 설명합니다.
Workday는 사용자 활동, 인증, 보안 감사 로그를 생성하는 클라우드 기반 인적 자본 관리 (HCM) 및 재무 플랫폼입니다. 사용자 활동 로깅 REST API는 이러한 로그에 대한 프로그래매틱 액세스를 제공합니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스
- Cloud Storage API가 사용 설정된 GCP 프로젝트
- GCS 버킷을 만들고 관리할 수 있는 권한
- GCS 버킷의 IAM 정책을 관리할 수 있는 권한
- Cloud Run 서비스, Pub/Sub 주제, Cloud Scheduler 작업을 만들 수 있는 권한
- 보안 관리자 또는 이에 준하는 권한으로 Workday에 대한 권한 액세스
- Workday 테넌트에서 사용자 활동 로깅이 사용 설정됨
Google Cloud Storage 버킷 만들기
- Google Cloud 콘솔로 이동합니다.
- 프로젝트를 선택하거나 새 프로젝트를 만듭니다.
- 탐색 메뉴에서 Cloud Storage> 버킷으로 이동합니다.
- 버킷 만들기를 클릭합니다.
다음 구성 세부정보를 제공합니다.
설정 값 버킷 이름 지정 전역적으로 고유한 이름 (예: workday-activity-logs)을 입력합니다.위치 유형 필요에 따라 선택 (리전, 이중 리전, 멀티 리전) 위치 위치를 선택합니다 (예: us-central1).스토리지 클래스 Standard (자주 액세스하는 로그에 권장) 액세스 제어 균일 (권장) 보호 조치 선택사항: 객체 버전 관리 또는 보관 정책 사용 설정 만들기를 클릭합니다.
Workday API 사용자 인증 정보 수집
사용자 활동 로깅 사용 설정
- 보안 관리자로 Workday 테넌트에 로그인합니다.
- Workday 검색창에서 Edit Tenant Setup - System 작업을 검색하여 엽니다.
- 보안 섹션에서 사용자 활동 로깅 사용 설정 체크박스를 선택합니다.
확인을 클릭하여 변경사항을 저장합니다.
통합 시스템 사용자 (ISU) 만들기
- Workday 검색창에서 통합 시스템 사용자 만들기를 검색합니다.
- 사용자 이름 (예:
ISU_SIEM_Export)과 안전한 비밀번호를 입력합니다. - 다음 로그인 시 새 비밀번호 요구 체크박스를 선택 해제합니다.
- 확인을 클릭합니다.
- 보안 그룹 만들기를 검색하고 통합 시스템 보안 그룹 (제약 없음)을 만듭니다.
- 이 보안 그룹에 ISU (
ISU_SIEM_Export)를 추가합니다. - 도메인 보기를 검색하고 사용자 활동 로깅 도메인을 찾습니다. 이 도메인의 ISU 보안 그룹에 Get 액세스 권한을 부여합니다.
- 보류 중인 보안 정책 변경사항 활성화를 검색하고 변경사항을 활성화합니다.
OAuth용 API 클라이언트 등록
- Workday 검색창에서 Register API Client for Integrations를 검색합니다.
- Client Name(클라이언트 이름)(예:
SIEM_OAuth_Client)을 입력합니다. - 만료되지 않는 새로고침 토큰 옵션을 선택합니다.
- System (또는 사용자 활동 Logging API에 필요한 범위) 범위를 추가합니다.
- 확인을 클릭합니다.
- 다음 세부정보를 복사하여 안전한 위치에 저장합니다.
- 클라이언트 ID: API 클라이언트 식별자입니다.
- 클라이언트 보안 비밀번호: API 클라이언트 보안 비밀번호입니다.
- 통합의 갱신 토큰 관리를 검색합니다.
- ISU 계정 (
ISU_SIEM_Export)을 선택합니다. - API 클라이언트의 새 갱신 토큰을 생성합니다.
- Refresh Token을 복사하여 저장합니다.
테넌트 URL 확인
API 엔드포인트는 Workday 테넌트를 기반으로 합니다. 형식은 다음과 같습니다.
| 구성요소 | 값 |
|---|---|
| 토큰 엔드포인트 | https://HOST/ccx/oauth2/TENANT/token |
| Activity Logging API | https://HOST/ccx/api/privacy/v1/TENANT/activityLogging |
권한 확인
ISU에 필요한 권한이 있는지 확인하려면 다음 단계를 따르세요.
- 보안 관리자로 Workday에 로그인합니다.
- 통합 시스템 사용자 보기를 검색하고 ISU (
ISU_SIEM_Export)를 선택합니다. - 사용자가 사용자 활동 로깅 도메인에 대한 Get 액세스 권한이 있는 보안 그룹의 구성원인지 확인합니다.
- 액세스 권한이 누락된 경우 보안 그룹을 업데이트하고 대기 중인 보안 정책 변경사항을 활성화합니다.
API 액세스 테스트
통합을 진행하기 전에 사용자 인증 정보를 테스트하세요.
# Replace with your actual credentials CLIENT_ID="your-client-id" CLIENT_SECRET="your-client-secret" REFRESH_TOKEN="your-refresh-token" HOST="your-workday-host" TENANT="your-tenant" # Get OAuth access token TOKEN=$(curl -s -X POST "https://${HOST}/ccx/oauth2/${TENANT}/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&refresh_token=${REFRESH_TOKEN}" \ | jq -r '.access_token') # Test API access curl -v -H "Authorization: Bearer ${TOKEN}" \ "https://${HOST}/ccx/api/privacy/v1/${TENANT}/activityLogging?from=2024-01-01T00:00:00Z&to=2024-01-01T01:00:00Z"
Cloud Run 함수의 서비스 계정 만들기
Cloud Run 함수에는 GCS 버킷에 쓸 수 있고 Pub/Sub에 의해 호출될 수 있는 권한이 있는 서비스 계정이 필요합니다.
서비스 계정 만들기
- GCP 콘솔에서 IAM 및 관리자 > 서비스 계정으로 이동합니다.
- 서비스 계정 만들기를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 서비스 계정 이름:
workday-activity-collector-sa을 입력합니다. - 서비스 계정 설명:
Service account for Cloud Run function to collect Workday User Activity logs을 입력합니다.
- 서비스 계정 이름:
- 만들고 계속하기를 클릭합니다.
- 이 서비스 계정에 프로젝트에 대한 액세스 권한 부여 섹션에서 다음 역할을 추가합니다.
- 역할 선택을 클릭합니다.
- 스토리지 객체 관리자를 검색하여 선택합니다.
- + 다른 역할 추가를 클릭합니다.
- Cloud Run 호출자를 검색하여 선택합니다.
- + 다른 역할 추가를 클릭합니다.
- Cloud Functions 호출자를 검색하여 선택합니다.
- 계속을 클릭합니다.
- 완료를 클릭합니다.
이러한 역할은 다음 작업에 필요합니다. - 스토리지 객체 관리자: GCS 버킷에 로그를 쓰고 상태 파일을 관리합니다. - Cloud Run 호출자: Pub/Sub에서 함수를 호출하도록 허용합니다. - Cloud Functions 호출자: 함수 호출을 허용합니다.
GCS 버킷에 IAM 권한 부여
GCS 버킷에 대한 쓰기 권한을 서비스 계정에 부여합니다.
- Cloud Storage> 버킷으로 이동합니다.
- 버킷 이름 (예:
workday-activity-logs)을 클릭합니다. - 권한 탭으로 이동합니다.
- 액세스 권한 부여를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 주 구성원 추가: 서비스 계정 이메일 (예:
workday-activity-collector-sa@your-project.iam.gserviceaccount.com)을 입력합니다. - 역할 할당: 스토리지 객체 관리자를 선택합니다.
- 주 구성원 추가: 서비스 계정 이메일 (예:
- 저장을 클릭합니다.
Pub/Sub 주제 만들기
Cloud Scheduler가 게시하고 Cloud Run 함수가 구독할 Pub/Sub 주제를 만듭니다.
- GCP Console에서 Pub/Sub > 주제로 이동합니다.
- 주제 만들기를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 주제 ID:
workday-activity-trigger를 입력합니다. - 다른 설정은 기본값으로 둡니다.
- 주제 ID:
- 만들기를 클릭합니다.
로그를 수집하는 Cloud Run 함수 만들기
Cloud Run 함수는 Cloud Scheduler의 Pub/Sub 메시지에 의해 트리거되어 Workday User Activity Logging API에서 로그를 가져와 GCS에 작성합니다.
- GCP 콘솔에서 Cloud Run으로 이동합니다.
- 서비스 만들기를 클릭합니다.
- 함수를 선택합니다 (인라인 편집기를 사용하여 함수 만들기).
구성 섹션에서 다음 구성 세부정보를 입력합니다.
설정 값 서비스 이름 workday-activity-collector리전 GCS 버킷과 일치하는 리전을 선택합니다 (예: us-central1).런타임 Python 3.12 이상 선택 트리거 (선택사항) 섹션에서 다음을 수행합니다.
- + 트리거 추가를 클릭합니다.
- Cloud Pub/Sub를 선택합니다.
- Cloud Pub/Sub 주제 선택에서
workday-activity-trigger주제를 선택합니다. - 저장을 클릭합니다.
인증 섹션에서 다음을 구성합니다.
- 인증 필요를 선택합니다.
- ID 및 액세스 관리 (IAM)를 확인합니다.
아래로 스크롤하고 컨테이너, 네트워킹, 보안을 펼칩니다.
보안 탭으로 이동합니다.
- 서비스 계정: 서비스 계정
workday-activity-collector-sa를 선택합니다.
- 서비스 계정: 서비스 계정
컨테이너 탭으로 이동합니다.
- 변수 및 보안 비밀을 클릭합니다.
- 각 환경 변수에 대해 + 변수 추가를 클릭합니다.
변수 이름 예시 값 설명 GCS_BUCKETworkday-activity-logsGCS 버킷 이름 GCS_PREFIXworkday로그 파일의 접두사 STATE_KEYworkday/state.json상태 파일 경로 WORKDAY_HOSTyour-workday-hostWorkday 호스트 이름 WORKDAY_TENANTyour-tenantWorkday 테넌트 이름 CLIENT_IDyour-client-idOAuth 클라이언트 ID CLIENT_SECRETyour-client-secretOAuth 클라이언트 보안 비밀번호 REFRESH_TOKENyour-refresh-tokenOAuth 갱신 토큰 MAX_RECORDS10000실행당 최대 레코드 수 PAGE_SIZE100페이지당 레코드 수 LOOKBACK_HOURS24초기 전환 확인 기간 변수 및 보안 비밀 탭에서 요청까지 아래로 스크롤합니다.
- 요청 제한 시간:
600초 (10분)를 입력합니다.
- 요청 제한 시간:
컨테이너의 설정 탭으로 이동합니다.
- 리소스 섹션에서 다음을 수행합니다.
- 메모리: 512MiB 이상을 선택합니다.
- CPU: 1을 선택합니다.
- 리소스 섹션에서 다음을 수행합니다.
버전 확장 섹션에서 다음을 수행합니다.
- 최소 인스턴스 수:
0를 입력합니다. - 최대 인스턴스 수:
100을 입력합니다 (또는 예상 부하에 따라 조정).
- 최소 인스턴스 수:
만들기를 클릭합니다.
서비스가 생성될 때까지 기다립니다 (1~2분).
서비스가 생성되면 인라인 코드 편집기가 자동으로 열립니다.
함수 코드 추가
- 함수 진입점에 main을 입력합니다.
인라인 코드 편집기에서 다음 두 파일을 만듭니다.
- 첫 번째 파일: main.py:
import functions_framework from google.cloud import storage import json import os import urllib3 from datetime import datetime, timezone, timedelta import time # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=30.0), retries=False, ) # Initialize Storage client storage_client = storage.Client() # Environment variables GCS_BUCKET = os.environ.get('GCS_BUCKET') GCS_PREFIX = os.environ.get('GCS_PREFIX', 'workday') STATE_KEY = os.environ.get('STATE_KEY', 'workday/state.json') WORKDAY_HOST = os.environ.get('WORKDAY_HOST', '') WORKDAY_TENANT = os.environ.get('WORKDAY_TENANT', '') CLIENT_ID = os.environ.get('CLIENT_ID', '') CLIENT_SECRET = os.environ.get('CLIENT_SECRET', '') REFRESH_TOKEN = os.environ.get('REFRESH_TOKEN', '') MAX_RECORDS = int(os.environ.get('MAX_RECORDS', '10000')) PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '100')) LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24')) def parse_datetime(value: str) -> datetime: """Parse ISO datetime string to datetime object.""" if value.endswith("Z"): value = value[:-1] + "+00:00" return datetime.fromisoformat(value) def get_token(): """Get OAuth 2.0 access token using refresh token flow.""" token_url = f"https://{WORKDAY_HOST}/ccx/oauth2/{WORKDAY_TENANT}/token" body = f"grant_type=refresh_token&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&refresh_token={REFRESH_TOKEN}" headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } backoff = 1.0 max_retries = 3 for attempt in range(max_retries): response = http.request('POST', token_url, body=body.encode('utf-8'), headers=headers) if response.status == 429: retry_after = int(response.headers.get('Retry-After', str(int(backoff)))) print(f"Rate limited (429) on token request. Retrying after {retry_after}s...") time.sleep(retry_after) backoff = min(backoff * 2, 30.0) continue if response.status != 200: raise RuntimeError(f"Failed to get access token: {response.status} - {response.data.decode('utf-8')}") data = json.loads(response.data.decode('utf-8')) return data['access_token'] raise RuntimeError(f"Failed to get token after {max_retries} retries due to rate limiting") @functions_framework.cloud_event def main(cloud_event): """ Cloud Run function triggered by Pub/Sub to fetch Workday User Activity logs and write to GCS. Args: cloud_event: CloudEvent object containing Pub/Sub message """ if not all([GCS_BUCKET, WORKDAY_HOST, WORKDAY_TENANT, CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN]): print('Error: Missing required environment variables') return try: bucket = storage_client.bucket(GCS_BUCKET) # Load state state = load_state(bucket, STATE_KEY) # Determine time window now = datetime.now(timezone.utc) last_time = None if isinstance(state, dict) and state.get("last_event_time"): try: last_time = parse_datetime(state["last_event_time"]) last_time = last_time - timedelta(minutes=2) except Exception as e: print(f"Warning: Could not parse last_event_time: {e}") if last_time is None: last_time = now - timedelta(hours=LOOKBACK_HOURS) print(f"Fetching logs from {last_time.isoformat()} to {now.isoformat()}") # Get access token token = get_token() # Fetch logs records, newest_event_time = fetch_logs( token=token, start_time=last_time, end_time=now, page_size=PAGE_SIZE, max_records=MAX_RECORDS, ) if not records: print("No new log records found.") save_state(bucket, STATE_KEY, now.isoformat()) return # Write to GCS as NDJSON timestamp = now.strftime('%Y%m%d_%H%M%S') object_key = f"{GCS_PREFIX}/logs_{timestamp}.ndjson" blob = bucket.blob(object_key) ndjson = '\n'.join([json.dumps(record, ensure_ascii=False) for record in records]) + '\n' blob.upload_from_string(ndjson, content_type='application/x-ndjson') print(f"Wrote {len(records)} records to gs://{GCS_BUCKET}/{object_key}") if newest_event_time: save_state(bucket, STATE_KEY, newest_event_time) else: save_state(bucket, STATE_KEY, now.isoformat()) print(f"Successfully processed {len(records)} records") except Exception as e: print(f'Error processing logs: {str(e)}') raise def load_state(bucket, key): """Load state from GCS.""" try: blob = bucket.blob(key) if blob.exists(): state_data = blob.download_as_text() return json.loads(state_data) except Exception as e: print(f"Warning: Could not load state: {e}") return {} def save_state(bucket, key, last_event_time_iso: str): """Save the last event timestamp to GCS state file.""" try: state = {'last_event_time': last_event_time_iso} blob = bucket.blob(key) blob.upload_from_string( json.dumps(state, indent=2), content_type='application/json' ) print(f"Saved state: last_event_time={last_event_time_iso}") except Exception as e: print(f"Warning: Could not save state: {e}") def fetch_logs(token: str, start_time: datetime, end_time: datetime, page_size: int, max_records: int): """ Fetch user activity logs from Workday Activity Logging API with pagination and rate limiting. Args: token: OAuth 2.0 access token start_time: Start time for log query end_time: End time for log query page_size: Number of records per page max_records: Maximum total records to fetch Returns: Tuple of (records list, newest_event_time ISO string) """ base_url = f"https://{WORKDAY_HOST}/ccx/api/privacy/v1/{WORKDAY_TENANT}/activityLogging" headers = { 'Authorization': f'Bearer {token}', 'Accept': 'application/json', 'User-Agent': 'GoogleSecOps-WorkdayCollector/1.0' } records = [] newest_time = None page_num = 0 backoff = 1.0 offset = 0 from_iso = start_time.strftime('%Y-%m-%dT%H:%M:%SZ') to_iso = end_time.strftime('%Y-%m-%dT%H:%M:%SZ') while True: page_num += 1 if len(records) >= max_records: print(f"Reached max_records limit ({max_records})") break current_limit = min(page_size, max_records - len(records)) url = f"{base_url}?from={from_iso}&to={to_iso}&limit={current_limit}&offset={offset}" try: response = http.request('GET', url, headers=headers) if response.status == 429: retry_after = int(response.headers.get('Retry-After', str(int(backoff)))) print(f"Rate limited (429). Retrying after {retry_after}s...") time.sleep(retry_after) backoff = min(backoff * 2, 30.0) continue backoff = 1.0 if response.status != 200: print(f"HTTP Error: {response.status}") response_text = response.data.decode('utf-8') print(f"Response body: {response_text}") return [], None data = json.loads(response.data.decode('utf-8')) page_results = data.get('data', []) if not page_results: print(f"No more results (empty page)") break print(f"Page {page_num}: Retrieved {len(page_results)} events") records.extend(page_results) # Track newest event time for event in page_results: try: event_time = event.get('requestTime') or event.get('systemAccount', {}).get('signOnDateTime') if event_time: if newest_time is None or parse_datetime(event_time) > parse_datetime(newest_time): newest_time = event_time except Exception as e: print(f"Warning: Could not parse event time: {e}") # Check pagination total = data.get('total', 0) if offset + len(page_results) >= total: print(f"Reached end of results (offset={offset + len(page_results)}, total={total})") break offset += len(page_results) except Exception as e: print(f"Error fetching logs: {e}") return [], None print(f"Retrieved {len(records)} total records from {page_num} pages") return records, newest_time두 번째 파일: requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0
배포를 클릭하여 함수를 저장하고 배포합니다.
배포가 완료될 때까지 기다립니다 (2~3분).
Cloud Scheduler 작업 만들기
Cloud Scheduler는 일정 간격으로 Pub/Sub 주제에 메시지를 게시하여 Cloud Run 함수를 트리거합니다.
- GCP Console에서 Cloud Scheduler로 이동합니다.
- 작업 만들기를 클릭합니다.
다음 구성 세부정보를 제공합니다.
설정 값 이름 workday-activity-collector-hourly리전 Cloud Run 함수와 동일한 리전 선택 주파수 0 * * * *(매시간 정각)시간대 시간대 선택 (UTC 권장) 타겟 유형 Pub/Sub 주제 workday-activity-trigger주제를 선택합니다.메일 본문 {}(빈 JSON 객체)만들기를 클릭합니다.
일정 빈도 옵션
로그 볼륨 및 지연 시간 요구사항에 따라 빈도를 선택합니다.
| 빈도 | 크론 표현식 | 사용 사례 |
|---|---|---|
| 5분마다 | */5 * * * * |
대용량, 저지연 |
| 15분마다 | */15 * * * * |
검색량 보통 |
| 1시간마다 | 0 * * * * |
표준 (권장) |
| 6시간마다 | 0 */6 * * * |
양이 적은 일괄 처리 |
| 매일 | 0 0 * * * |
이전 데이터 수집 |
통합 테스트
- Cloud Scheduler 콘솔에서 작업을 찾습니다 (
workday-activity-collector-hourly). - 강제 실행을 클릭하여 수동으로 트리거합니다.
- 몇 초간 기다린 후 Cloud Run > 서비스 > workday-activity-collector > 로그로 이동합니다.
함수가 성공적으로 실행되었는지 확인합니다. 다음 항목을 찾습니다.
Fetching logs from YYYY-MM-DDTHH:MM:SS+00:00 to YYYY-MM-DDTHH:MM:SS+00:00 Page 1: Retrieved X events Wrote X records to gs://workday-activity-logs/workday/logs_YYYYMMDD_HHMMSS.ndjson Successfully processed X recordsGCS 버킷 (
workday-activity-logs)을 확인하여 로그가 작성되었는지 확인합니다.
로그에 오류가 표시되는 경우:
- HTTP 401: 환경 변수에서 OAuth 사용자 인증 정보 확인
- HTTP 403: ISU에 사용자 활동 로깅 도메인에 필요한 권한이 있는지 확인
- HTTP 429: 비율 제한 - 함수가 백오프를 사용하여 자동으로 재시도됩니다.
- 액세스 토큰을 가져오지 못함:
WORKDAY_HOST,WORKDAY_TENANT,CLIENT_ID,CLIENT_SECRET,REFRESH_TOKEN이 올바른지 확인
Workday 사용자 활동 로그를 수집하도록 Google SecOps에서 피드 구성
- SIEM 설정> 피드로 이동합니다.
- 새 피드 추가를 클릭합니다.
- 단일 피드 구성을 클릭합니다.
- 피드 이름 필드에 피드 이름을 입력합니다(예:
Workday User Activity Logs). - 소스 유형으로 Google Cloud Storage V2를 선택합니다.
- 로그 유형으로 Workday 사용자 활동을 선택합니다.
서비스 계정 가져오기를 클릭합니다. 고유한 서비스 계정 이메일이 표시됩니다(예:
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com이 이메일 주소를 복사합니다. 다음 단계에서 사용합니다.
다음을 클릭합니다.
다음 입력 매개변수의 값을 지정합니다.
스토리지 버킷 URL: 다음 접두사 경로를 사용하여 GCS 버킷 URI를 입력합니다.
gs://workday-activity-logs/workday/- 다음과 같이 바꿉니다.
workday-activity-logs: GCS 버킷 이름입니다.workday: 로그가 저장되는 선택적 접두사/폴더 경로입니다 (루트의 경우 비워 둠).
- 다음과 같이 바꿉니다.
소스 삭제 옵션: 환경설정에 따라 삭제 옵션을 선택합니다.
- 삭제 안함: 전송 후 파일을 삭제하지 않습니다 (테스트에 권장).
- 전송된 파일 삭제: 전송이 완료되면 파일을 삭제합니다.
전송된 파일 및 빈 디렉터리 삭제: 전송이 완료되면 파일과 빈 디렉터리를 삭제합니다.
최대 파일 기간: 지난 일수 동안 수정된 파일을 포함합니다 (기본값은 180일).
애셋 네임스페이스: 애셋 네임스페이스입니다.
수집 라벨: 이 피드의 이벤트에 적용할 라벨입니다.
다음을 클릭합니다.
확정 화면에서 새 피드 구성을 검토한 다음 제출을 클릭합니다.
Google SecOps 서비스 계정에 IAM 권한 부여
Google SecOps 서비스 계정에는 GCS 버킷에 대한 스토리지 객체 뷰어 역할이 필요합니다.
- Cloud Storage> 버킷으로 이동합니다.
- 버킷 이름 (
workday-activity-logs)을 클릭합니다. - 권한 탭으로 이동합니다.
- 액세스 권한 부여를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 주 구성원 추가: Google SecOps 서비스 계정 이메일을 붙여넣습니다.
- 역할 할당: 스토리지 객체 뷰어를 선택합니다.
저장을 클릭합니다.
UDM 매핑 테이블
| 로그 필드 | UDM 매핑 | 논리 |
|---|---|---|
| data, EVP_NME, EVPMINUS1_NME, Pay_Rate_type, Signon_Day_of_the_Week, Tenant_Access_Read_Only, Sign-on_Time, UI_Client_Type, Signoff_Time, Request_Originator, SAML_Identity_Provider, Worker_Type_for_Signon_Worker, Worker_Active_for_Signon_Worker, AccountLocked_Disabled_or_Expired, Created_Moment, Current_OMS_Version, ElapsedTime_Minutes, Failed_Sign_On, Authentication_Type, Operating_System, Hire_Date_for_Signon_Worker, Job_Family_for_Signon_Worker, Device_is_Trusted, Signon_Hour_of_Day, Signon_IP_Address | additional.fields | 이 필드에서 생성된 라벨과 병합됨 |
| event_type | metadata.event_type | 값이 직접 복사됨 |
| tenant, status_code | metadata.product_event_type | 테넌트의 값입니다. status_code가 'Terminated'인 경우 status_code로 설정됩니다. |
| taskId | metadata.product_log_id | 값이 직접 복사됨 |
| metadata.product_name | 'WORKDAY_USER_ACTIVITY'로 설정됩니다. | |
| metadata.vendor_name | 'WORKDAY_USER_ACTIVITY'로 설정됩니다. | |
| User_Agent | network.http.parsed_user_agent | parseduseragent로 변환한 후 이름이 변경됨 |
| userAgent, Browser_Type, User_Agent | network.http.user_agent | userAgent의 값입니다. 비어 있지 않으면 Browser_Type으로 설정되고, 비어 있지 않으면 User_Agent로 설정됩니다. |
| sessionId, Session_ID | network.session_id | sessionId의 값입니다. 비어 있지 않으면 Session_ID로 설정됩니다. |
| Client_TLS_Version | network.tls.version | 값이 직접 복사됨 |
| 작업자 | principal.asset.hostname | 값이 직접 복사됨 |
| ipAddress, Signon_IP_Address | principal.asset.ip | 비어 있지 않으면 ipAddress에서 병합되고, 비어 있으면 Signon_IP_Address에서 추출된 IP에서 병합됩니다. |
| manager_name, Worker | principal.hostname | 비어 있지 않으면 manager_name의 값, 비어 있지 않으면 Worker의 값 |
| ipAddress, Signon_IP_Address | principal.ip | 비어 있지 않으면 ipAddress에서 병합되고, 비어 있으면 Signon_IP_Address에서 추출된 IP에서 병합됩니다. |
| User_Credentials, Signon_Worker, Supervisory_Organization_of_Signon_Worker, Workday_Account_Sign_On, System_Account | principal.user.attribute.labels | 이 필드에서 생성된 라벨과 병합됨 |
| manager_id | principal.user.userid | 값이 직접 복사됨 |
| Position, Active_Session, Authentication_Failure_Message, Forgotten_Password_Reset_Request, Invalid_Credentials, Is_Device_Managed, MFA_Exempt, Password_Changed | security_result.detection_fields | 이 필드에서 생성된 라벨과 병합됨 |
| taskDisplayName | security_result.summary | 값이 직접 복사됨 |
| 호스트 이름 | target.hostname | 값이 직접 복사됨 |
| CITY_NME, Location | target.location.city | CITY_NME의 값. 비어 있지 않으면 Location으로 설정됩니다. |
| COUNTRY_NME, Location_Country_for_Signon_Worker | target.location.country_or_region | COUNTRY_NME의 값입니다. 비어 있지 않은 경우 Location_Country_for_Signon_Worker로 설정됩니다. |
| STATE_NME, Location_State | target.location.state | STATE_NME의 값. 비어 있지 않으면 Location_State로 설정됩니다. |
| Exempt, Browser_Type, Class_of_Instance | target.resource.attribute.labels | 이 필드에서 생성된 라벨과 병합됨 |
| deviceType, Device_Type | target.resource.resource_subtype | 정규식과 일치하는 경우 deviceType의 값, 그렇지 않은 경우 정규식과 일치하는 경우 Device_Type의 값 |
| deviceType, Device_Type | target.resource.type | deviceType 또는 Device_Type이 정규식과 일치하는 경우 'DEVICE'로 설정됩니다. |
| BUSINESSGROUP_NME | target.user.department | 값이 직접 복사됨 |
| EMAIL_NME | target.user.email_addresses | 값이 직접 복사됨 |
| EMPLOYEE_ID, Employee_ID | target.user.employee_id | EMPLOYEE_ID의 값. 비어 있지 않으면 Employee_ID로 설정됩니다. |
| FIRST_NME | target.user.first_name | 값이 직접 복사됨 |
| EMPLOYEE_TYPE | target.user.group_identifiers | 값이 직접 복사됨 |
| LAST_NME | target.user.last_name | 값이 직접 복사됨 |
| title, JOBTITLE_NME | target.user.title | 타겟.설명자에서 추출된 경우 제목의 값이며 JOBTITLE_NME로 설정됩니다. |
| target.id | target.user.userid | 값이 직접 복사됨 |
requestTime |
event.idm.read_only_udm.metadata.event_timestamp |
변경 로그에서 매핑됨 |
activityAction |
event.idm.read_only_udm.metadata.product_event_type |
변경 로그에서 매핑됨 |
systemAccount |
event.idm.read_only_udm.principal.user.userid |
변경 로그에서 매핑됨 |
taskDisplayName |
event.idm.read_only_udm.target.resource.name |
변경 로그에서 매핑됨 |
target.id |
event.idm.read_only_udm.target.resource.attribute.labels |
변경 로그에서 매핑됨 |
desc |
event.idm.read_only_udm.target.resource.attribute.labels |
변경 로그에서 매핑됨 |
Sign-on_Time |
event.idm.read_only_udm.metadata.event_timestamp |
변경 로그에서 매핑됨 |
Role |
event.idm.read_only_udm.principal.user.user_display_name |
변경 로그에서 매핑됨 |
User_Name |
event.idm.read_only_udm.principal.user.email_addresses |
변경 로그에서 매핑됨 |
awaiting_details |
event.idm.read_only_udm.additional.fields |
변경 로그에서 매핑됨 |
변경 로그
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가에게 문의하여 답변을 받으세요.