Keycloak 로그 수집
이 문서에서는 웹훅을 사용하여 로그를 Google Security Operations로 푸시하도록 Keycloak을 구성하는 방법을 설명합니다.
Keycloak은 싱글 사인온 (SSO), 사용자 페더레이션, ID 브로커링, 소셜 로그인 기능을 제공하는 오픈소스 ID 및 액세스 관리 (IAM) 솔루션입니다. OpenID Connect, OAuth 2.0, SAML 2.0 프로토콜을 지원하며 보안 감사를 위해 사용자 이벤트 (로그인, 로그아웃, 등록, 비밀번호 변경) 및 관리자 이벤트 (사용자, 클라이언트, 영역, 역할 관리 작업)를 추적합니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스
- 실행 중인 Keycloak 인스턴스 (버전 20 이상 권장)
- Keycloak 관리 콘솔에 대한 관리자 액세스 권한
- 확장 프로그램을 배포하기 위한 Keycloak 서버 파일 시스템 또는 컨테이너 액세스
- Google Cloud 콘솔 액세스 (API 키 생성용)
Google SecOps에서 웹훅 피드 만들기
피드 만들기
- SIEM 설정> 피드로 이동합니다.
- 새 피드 추가를 클릭합니다.
- 다음 페이지에서 단일 피드 구성을 클릭합니다.
- 피드 이름 필드에 피드 이름을 입력합니다(예:
Keycloak Events). - 소스 유형으로 웹훅을 선택합니다.
- 로그 유형으로 Keycloak을 선택합니다.
- 다음을 클릭합니다.
- 다음 입력 파라미터의 값을 지정합니다.
- 분할 구분자 (선택사항):
\n를 입력하여 여러 줄 이벤트를 분할합니다 (각 웹훅 POST에는 단일 이벤트가 포함되므로 비워 둘 수 있음). - 애셋 네임스페이스: 애셋 네임스페이스
- 수집 라벨: 이 피드의 이벤트에 적용할 라벨입니다.
- 분할 구분자 (선택사항):
- 다음을 클릭합니다.
- 확정 화면에서 새 피드 구성을 검토한 다음 제출을 클릭합니다.
보안 비밀 키 생성 및 저장
피드를 만든 후 인증을 위한 보안 비밀 키를 생성해야 합니다.
- 피드 세부정보 페이지에서 보안 비밀 키 생성을 클릭합니다.
- 대화상자에 보안 비밀 키가 표시됩니다.
- 보안 비밀번호를 안전하게 복사하여 저장합니다.
중요: 비밀 키는 한 번만 표시되며 나중에 검색할 수 없습니다. 분실할 경우 새 비밀번호 키를 생성해야 합니다.
피드 엔드포인트 URL 가져오기
- 피드의 세부정보 탭으로 이동합니다.
- 엔드포인트 정보 섹션에서 피드 엔드포인트 URL을 복사합니다.
URL 형식은 다음과 같습니다.
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate또는
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate다음 단계를 위해 이 URL을 저장합니다.
완료를 클릭합니다.
Google Cloud API 키 만들기
Chronicle에는 인증을 위한 API 키가 필요합니다. Google Cloud 콘솔에서 제한된 API 키를 만듭니다.
API 키 만들기
- Google Cloud 콘솔 사용자 인증 정보 페이지로 이동합니다.
- 프로젝트 (Chronicle 인스턴스와 연결된 프로젝트)를 선택합니다.
- 사용자 인증 정보 만들기 API 키를 클릭합니다.
- API 키가 생성되어 대화상자에 표시됩니다.
- API 키 수정을 클릭하여 키를 제한합니다.
API 키 제한
- API 키 설정 페이지에서 다음을 수행합니다.
- 이름: 설명이 포함된 이름을 입력합니다 (예:
Chronicle Webhook API Key).
- 이름: 설명이 포함된 이름을 입력합니다 (예:
- API 제한사항에서 다음을 수행합니다.
- 키 제한을 선택합니다.
- API 선택 드롭다운에서 Google SecOps API (또는 Chronicle API)를 검색하여 선택합니다.
- 저장을 클릭합니다.
- 페이지 상단의 API 키 필드에서 API 키 값을 복사합니다.
- API 키를 안전하게 저장합니다.
Keycloak에서 이벤트 저장소 사용 설정
웹훅 확장 프로그램을 구성하기 전에 Keycloak에서 이벤트 스토리지를 사용 설정하여 이벤트가 생성되고 전달할 수 있도록 합니다.
사용자 이벤트 사용 설정
- Keycloak 관리 콘솔에 로그인합니다.
- 왼쪽 상단의 영역 드롭다운에서 모니터링할 영역을 선택합니다.
- Realm Settings(렐름 설정) > Events(이벤트)로 이동합니다.
- 사용자 이벤트 설정 하위 탭을 선택합니다.
- 일정 저장 전환 버튼을 사용 설정합니다.
- 만료 기간을 설정합니다 (최소 권장 기간: 7일).
- 저장을 클릭합니다.
관리 이벤트 사용 설정
- 동일한 이벤트 탭에서 관리 이벤트 설정 하위 탭을 선택합니다.
- 일정 저장 전환 버튼을 사용 설정합니다.
- 표현 포함 전환 버튼을 사용 설정하여 변경된 객체의 전체 세부정보를 캡처합니다.
- 만료 기간을 설정합니다 (최소 권장 기간: 7일).
- 저장을 클릭합니다.
웹훅 이벤트 리스너 확장 프로그램 설치
Keycloak에는 기본 웹훅 이벤트 리스너가 포함되어 있지 않습니다. 2단계 (p2-inc)에서 keycloak-events 확장 프로그램을 설치하여 웹훅 전송을 사용 설정합니다.
확장 프로그램 다운로드 및 배포
Maven Central의 keycloak-events 출시 페이지에서 최신 출시 JAR를 다운로드하거나 소스에서 빌드합니다.
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean install결과로 생성된 fat JAR 파일을 Keycloak
providers디렉터리에 복사합니다.cp target/keycloak-events-*.jar /opt/keycloak/providers/Keycloak을 다시 빌드하고 다시 시작합니다.
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
웹훅 이벤트 리스너 사용 설정
- Keycloak 관리 콘솔에 로그인합니다.
- 영역 드롭다운에서 타겟 영역을 선택합니다.
- Realm Settings(렐름 설정) > Events(이벤트)로 이동합니다.
- 이벤트 리스너 드롭다운에서 ext-event-webhook을 선택합니다.
- 저장을 클릭합니다.
Keycloak 웹훅 구성
웹훅 URL 구성
Chronicle 엔드포인트 URL과 API 키를 결합합니다.
<ENDPOINT_URL>?key=<API_KEY>예:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Keycloak REST API를 통해 웹훅 구독 만들기
keycloak-events 확장 프로그램은 웹훅 구독을 관리하기 위한 REST 엔드포인트를 제공합니다. Keycloak Admin REST API를 사용하여 웹훅을 만듭니다.
1단계: 액세스 토큰 가져오기
관리자 계정을 사용하여 Keycloak에서 액세스 토큰을 요청합니다.
TOKEN=$(curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/master/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=password" \ --data-urlencode "client_id=admin-cli" \ --data-urlencode "username=<ADMIN_USERNAME>" \ --data-urlencode "password=<ADMIN_PASSWORD>" \ | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
다음을 바꿉니다.
<KEYCLOAK_HOST>: Keycloak 서버 호스트 이름 및 포트 (예:keycloak.example.com:8443)<ADMIN_USERNAME>: Keycloak 관리자 사용자 이름<ADMIN_PASSWORD>: Keycloak 관리자 비밀번호
2단계: 웹훅 만들기
POST 요청을 보내 타겟 영역의 웹훅 구독을 만듭니다.
curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "enabled": "true", "url": "<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>", "secret": "<WEBHOOK_HMAC_SECRET>", "eventTypes": ["*"] }'
다음을 바꿉니다.
<KEYCLOAK_HOST>: Keycloak 서버 호스트 이름<REALM_NAME>: 모니터링할 렐름의 이름입니다 (예:master또는my-realm).<ENDPOINT_URL>: 이전에 복사한 Chronicle 피드 엔드포인트 URL<API_KEY>: 이전에 만든 Google Cloud API 키<SECRET_KEY>: 이전에 생성된 Chronicle 웹훅 보안 비밀 키<WEBHOOK_HMAC_SECRET>: 웹훅 페이로드의 HMAC 서명을 위한 임의의 보안 비밀 문자열입니다 (예:mySecretKey123).
3단계: 웹훅 확인
영역의 모든 웹훅을 나열하여 웹훅이 생성되었는지 확인합니다.
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
이 응답은 웹훅 객체 목록을 반환합니다. 웹훅이 "enabled": "true" 및 올바른 URL과 함께 표시되는지 확인합니다.
웹훅 이벤트 유형
eventTypes 필드는 전송되는 이벤트를 필터링하는 표현식 배열을 허용합니다.
*- 모든 이벤트 전송 (SIEM 통합에 권장)access.*- 모든 액세스 이벤트 전송admin.*- 모든 관리 이벤트 전송admin.USER-*- 사용자와 관련된 모든 관리자 이벤트 전송admin-USER-CREATE- 사용자 생성 관리 이벤트만 전송
웹훅 페이로드 형식
웹훅은 JSON 페이로드가 포함된 HTTP POST 요청으로 이벤트를 전송합니다. 사용자 이벤트 페이로드의 예:
{ "id": "987865-1a2b-3c4d-9876-654321abc", "time": 1767799710612, "type": "LOGIN", "realmId": "12345abcde-1a2b-4d3c-9876-abcd456", "clientId": "account-console", "userId": "abcd456-1234-5678-abc9-987gfed654", "sessionId": "efghij-9876-abcd-456-11223344", "ipAddress": "203.0.113.45", "details": { "auth_method": "openid-connect", "auth_type": "code", "redirect_uri": "https://app.example.com/callback", "consent": "no_consent_required", "username": "jdoe" } }
웹훅 재시도 동작
확장 프로그램은 2xx가 아닌 응답이 수신될 때 재시도를 위해 자동 지수 백오프를 사용합니다.
| 매개변수 | 기본값 | 설명 |
|---|---|---|
| backoffInitialInterval | 500 ms | 초기 재시도 간격 |
| backoffMaxElapsedTime | 900,000ms (15분) | 최대 총 재시도 시간 |
| backoffMaxInterval | 180,000ms (3분) | 재시도 간 최대 간격 |
| backoffMultiplier | 5 | 각 재시도 간격의 승수 |
| backoffRandomizationFactor | 0.5 | 지터의 무작위화 요소 |
인증 방법 참조
Chronicle 웹훅 피드는 여러 인증 방법을 지원합니다. 공급업체에서 지원하는 방법을 선택합니다.
방법 1: 맞춤 헤더 (권장)
공급업체에서 맞춤 HTTP 헤더를 지원하는 경우 이 방법을 사용하여 보안을 강화하세요.
요청 형식:
POST <ENDPOINT_URL> HTTP/1.1 Content-Type: application/json x-goog-chronicle-auth: <API_KEY> x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
장점:
- API 키와 보안 비밀이 URL에 표시되지 않음
- 더 안전함 (헤더가 웹 서버 액세스 로그에 기록되지 않음)
- 공급업체에서 지원하는 경우 선호되는 방법
방법 2: 쿼리 매개변수
공급업체에서 맞춤 헤더를 지원하지 않는 경우 사용자 인증 정보를 URL에 추가하세요.
URL 형식:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>예:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...요청 형식:
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
단점:
- URL에 사용자 인증 정보가 표시됨
- 웹 서버 액세스 로그에 기록될 수 있음
- 헤더보다 보안 수준이 낮음
방법 3: 하이브리드 (URL + 헤더)
일부 구성에서는 URL에 API 키를 사용하고 헤더에 비밀 키를 사용합니다.
요청 형식:
POST <ENDPOINT_URL>?key=<API_KEY> HTTP/1.1 Content-Type: application/json x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
인증 헤더 이름
Chronicle은 인증을 위해 다음 헤더 이름을 허용합니다.
API 키:
x-goog-chronicle-auth(권장)X-Goog-Chronicle-Auth(대소문자 구분 안 함)
보안 비밀 키:
x-chronicle-auth(권장)X-Chronicle-Auth(대소문자 구분 안 함)
웹훅 한도 및 권장사항
요청 한도
| 한도 | 값 |
|---|---|
| 최대 요청 크기 | 4MB |
| 최대 QPS (초당 쿼리 수) | 15,000 |
| 요청 제한 시간 | 30초 |
| 재시도 동작 | 지수 백오프를 사용한 자동 |
UDM 매핑 테이블
| 로그 필드 | UDM 매핑 | 논리 |
|---|---|---|
| payload.client_id | additional.fields | payload.client_id, payload.realm_id에서 생성된 필드와 병합됨 |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | ISO8601 및 yyyy-MM-dd'T'HH:mm:ss.SSSZ 패턴의 날짜 필터를 사용하여 파싱됨 |
| payload.ip_address | metadata.event_type | payload.ip_address가 비어 있지 않으면 'STATUS_UPDATE'로 설정되고, uuid가 비어 있지 않으면 'USER_UNCATEGORIZED'로 설정되고, 그 외에는 'GENERIC_EVENT'로 설정됩니다. |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | 값이 직접 복사됨 |
| payload.session_id | network.session_id | 값이 직접 복사됨 |
| payload.ip_address | principal.ip | 값이 직접 복사됨 |
| source_metadata.schema | principal.resource.attribute.labels | source_metadata.schema, source_metadata.table, source_metadata.is_deleted (문자열로 변환됨), source_metadata.change_type, source_metadata.tx_id, source_metadata.lsn에서 생성된 라벨과 병합됨 |
| source_metadata.table | principal.resource.attribute.labels | |
| source_metadata.is_deleted | principal.resource.attribute.labels | |
| source_metadata.change_type | principal.resource.attribute.labels | |
| source_metadata.tx_id | principal.resource.attribute.labels | |
| source_metadata.lsn | principal.resource.attribute.labels | |
| uuid | principal.user.userid | 값이 직접 복사됨 |
| 객체 | security_result.detection_fields | 객체, read_method, payload.id에서 생성된 라벨과 병합됨 |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | 값이 직접 복사됨 |
| 사용자 이름 | target.user.userid | 값이 직접 복사됨 |
| metadata.product_name | metadata.product_name | 'KEYCLOAK'으로 설정 |
| metadata.vendor_name | metadata.vendor_name | 'KEYCLOAK'으로 설정 |
username" from "details_json |
target.user.userid |
변경 로그에서 매핑됨 |
redirect_uri" from "details_json |
target.url |
변경 로그에서 매핑됨 |
realm_id" and "client_id |
additional.fields |
변경 로그에서 매핑됨 |
변경 로그
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가에게 문의하여 답변을 받으세요.