Slack 감사 로그 수집
이 문서에서는 Google Cloud Run 함수를 사용하여 Slack 감사 로그를 Google Security Operations에 수집하는 방법을 설명합니다. 파서는 두 가지 형식의 Slack 감사 로그를 처리합니다. 먼저 불리언 값을 정규화하고 사전 정의된 필드를 지웁니다. 그런 다음 'message' 필드를 JSON으로 파싱하고 JSON이 아닌 메시지는 삭제하여 처리합니다. 특정 필드 (date_create
및 user_id
)의 존재 여부에 따라 파서는 메타데이터, 주체, 네트워크, 타겟, 정보 등의 원시 로그 필드를 UDM에 매핑하고 보안 결과를 구성하는 데 다른 로직을 적용합니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스
- Slack Enterprise Grid 테넌트 및 관리 콘솔에 대한 권한 있는 액세스
- GCP Cloud Run 함수 및 Cloud Scheduler에 대한 액세스 권한 관리
Slack 감사 로그 수집 필수사항 (앱 ID, OAuth 토큰, 조직 ID)
- Enterprise Grid 조직의 Slack 관리 콘솔에 로그인합니다.
- https://api.slack.com/apps로 이동하여 새 앱 만들기 > 처음부터를 클릭합니다.
- 앱 이름을 입력하고 개발 Slack 작업공간을 선택합니다.
- 앱 만들기를 클릭합니다.
- 왼쪽 사이드바에서 OAuth & Permissions(OAuth 및 권한)로 이동합니다.
- 범위 섹션으로 이동하여 다음 사용자 토큰 범위를 추가합니다.
- auditlogs:read
- Workspace에 설치 > 허용을 클릭합니다.
- 설치가 완료되면 조직 수준 앱 > 조직에 설치로 이동합니다.
- 조직 소유자/관리자 계정으로 앱을 승인합니다.
xoxp-
로 시작하는 사용자 OAuth 토큰을 복사하여 안전하게 저장합니다 (SLACK_ADMIN_TOKEN임).- Slack 관리 콘솔의 설정 및 권한 > 조직 설정에서 확인할 수 있는 조직 ID를 복사합니다.
디렉터리 설정
- 로컬 머신에 Cloud Run 함수 배포를 위한 새 디렉터리를 만듭니다.
- Chronicle 수집 스크립트 GitHub 저장소에서 다음 파일을 다운로드합니다.
- slack 폴더에서 다음을 다운로드합니다.
.env.yml
main.py
requirements.txt
- 저장소의 루트에서 모든 파일과 함께 전체 common 디렉터리를 다운로드합니다.
common/__init__.py
common/auth.py
common/env_constants.py
common/ingest.py
common/status.py
common/utils.py
- slack 폴더에서 다음을 다운로드합니다.
- 다운로드한 모든 파일을 배포 디렉터리에 배치합니다.
디렉터리 구조는 다음과 같아야 합니다.
deployment_directory/
├─common/
│ ├─__init__.py
│ ├─auth.py
│ ├─env_constants.py
│ ├─ingest.py
│ ├─status.py
│ └─utils.py
├─.env.yml
├─main.py
└─requirements.txt
Google Secret Manager에서 보안 비밀 만들기
- Google Cloud 콘솔에서 보안 > Secret Manager로 이동합니다.
- 보안 비밀 만들기를 클릭합니다.
- Chronicle 서비스 계정에 다음 구성 세부정보를 제공합니다.
- 이름:
chronicle-service-account
를 입력합니다. - 보안 비밀 값: Google SecOps 수집 인증 JSON 파일의 콘텐츠를 붙여넣습니다.
- 이름:
- 보안 비밀 만들기를 클릭합니다.
다음 형식으로 보안 비밀 리소스 이름을 복사합니다.
projects/<PROJECT_ID>/secrets/chronicle-service-account/versions/latest
보안 비밀 만들기를 다시 클릭하여 두 번째 보안 비밀을 만듭니다.
Slack 토큰에 대해 다음 구성 세부정보를 제공합니다.
- 이름:
slack-admin-token
를 입력합니다. - 보안 비밀 값: Slack 사용자 OAuth 토큰 (
xoxp-
로 시작)을 붙여넣습니다.
- 이름:
보안 비밀 만들기를 클릭합니다.
다음 형식으로 보안 비밀 리소스 이름을 복사합니다.
projects/<PROJECT_ID>/secrets/slack-admin-token/versions/latest
필수 런타임 환경 변수 설정
- 배포 디렉터리에서
.env.yml
파일을 엽니다. 환경 변수를 값으로 구성합니다.
CHRONICLE_CUSTOMER_ID: "<your-chronicle-customer-id>" CHRONICLE_REGION: us CHRONICLE_SERVICE_ACCOUNT: "projects/<PROJECT_ID>/secrets/chronicle-service-account/versions/latest" CHRONICLE_NAMESPACE: "" POLL_INTERVAL: "5" SLACK_ADMIN_TOKEN: "projects/<PROJECT_ID>/secrets/slack-admin-token/versions/latest"
- 다음을 바꿉니다.
<your-chronicle-customer-id>
: Google SecOps 고객 ID입니다.<PROJECT_ID>
: Google Cloud 프로젝트 ID입니다.- CHRONICLE_REGION: Google SecOps 리전으로 설정합니다. 유효한 값:
us
,asia-northeast1
,asia-south1
,asia-southeast1
,australia-southeast1
,europe
,europe-west2
,europe-west3
,europe-west6
,europe-west9
,europe-west12
,me-central1
,me-central2
,me-west1
,northamerica-northeast2
,southamerica-east1
. - POLL_INTERVAL: 함수가 실행되는 빈도 간격 (분)입니다. 이 기간은 Cloud Scheduler 작업 간격과 동일해야 합니다.
- 다음을 바꿉니다.
.env.yml
파일을 저장합니다.
Cloud Run 함수 배포
- Google Cloud 콘솔에서 터미널 또는 Cloud Shell을 엽니다.
배포 디렉터리로 이동합니다.
cd /path/to/deployment_directory
다음 명령어를 실행하여 Cloud Run 함수를 배포합니다.
gcloud functions deploy slack-audit-to-chronicle \ --entry-point main \ --trigger-http \ --runtime python39 \ --env-vars-file .env.yml \ --timeout 300s \ --memory 512MB \ --service-account <SERVICE_ACCOUNT_EMAIL>
<SERVICE_ACCOUNT_EMAIL>
을 Cloud Run 함수에서 사용할 서비스 계정의 이메일 주소로 바꿉니다.
배포가 완료될 때까지 기다립니다.
배포가 완료되면 출력에서 함수 URL을 기록해 둡니다.
Cloud Scheduler 설정
- Google Cloud 콘솔에서 Cloud Scheduler > 작업 만들기로 이동합니다.
- 다음 구성 세부정보를 제공합니다.
- 이름:
slack-audit-scheduler
를 입력합니다. - 리전: Cloud Run 함수를 배포한 동일한 리전을 선택합니다.
- 빈도:
*/5 * * * *
를 입력합니다 (POLL_INTERVAL
값과 일치하는 5분마다 실행됨). - 시간대: UTC를 선택합니다.
- 대상 유형: HTTP를 선택합니다.
- URL: 배포 출력에서 Cloud Run 함수 URL을 입력합니다.
- HTTP 메서드: POST를 선택합니다.
- 인증 헤더: OIDC 토큰 추가를 선택합니다.
- 서비스 계정: Cloud Run 함수에 사용된 동일한 서비스 계정을 선택합니다.
- 이름:
- 만들기를 클릭합니다.
UDM 매핑 테이블
로그 필드 | UDM 매핑 | 논리 |
---|---|---|
action |
metadata.product_event_type |
원시 로그의 action 필드에서 직접 매핑됩니다. |
actor.type |
principal.labels.value |
actor.type 필드에서 직접 매핑되며 키 actor.type 가 추가됩니다. |
actor.user.email |
principal.user.email_addresses |
actor.user.email 필드에서 직접 매핑됩니다. |
actor.user.id |
principal.user.product_object_id |
actor.user.id 필드에서 직접 매핑됩니다. |
actor.user.id |
principal.user.userid |
actor.user.id 필드에서 직접 매핑됩니다. |
actor.user.name |
principal.user.user_display_name |
actor.user.name 필드에서 직접 매핑됩니다. |
actor.user.team |
principal.user.group_identifiers |
actor.user.team 필드에서 직접 매핑됩니다. |
context.ip_address |
principal.ip |
context.ip_address 필드에서 직접 매핑됩니다. |
context.location.domain |
about.resource.attribute.labels.value |
context.location.domain 필드에서 직접 매핑되며 키 context.location.domain 가 추가됩니다. |
context.location.id |
about.resource.id |
context.location.id 필드에서 직접 매핑됩니다. |
context.location.name |
about.resource.name |
context.location.name 필드에서 직접 매핑됩니다. |
context.location.name |
about.resource.attribute.labels.value |
context.location.name 필드에서 직접 매핑되며 키 context.location.name 가 추가됩니다. |
context.location.type |
about.resource.resource_subtype |
context.location.type 필드에서 직접 매핑됩니다. |
context.session_id |
network.session_id |
context.session_id 필드에서 직접 매핑됩니다. |
context.ua |
network.http.user_agent |
context.ua 필드에서 직접 매핑됩니다. |
context.ua |
network.http.parsed_user_agent |
parseduseragent 필터를 사용하여 context.ua 필드에서 파싱된 사용자 에이전트 정보입니다. |
country |
principal.location.country_or_region |
country 필드에서 직접 매핑됩니다. |
date_create |
metadata.event_timestamp.seconds |
date_create 필드의 에포크 타임스탬프가 타임스탬프 객체로 변환됩니다. |
details.inviter.email |
target.user.email_addresses |
details.inviter.email 필드에서 직접 매핑됩니다. |
details.inviter.id |
target.user.product_object_id |
details.inviter.id 필드에서 직접 매핑됩니다. |
details.inviter.name |
target.user.user_display_name |
details.inviter.name 필드에서 직접 매핑됩니다. |
details.inviter.team |
target.user.group_identifiers |
details.inviter.team 필드에서 직접 매핑됩니다. |
details.reason |
security_result.description |
details.reason 필드에서 직접 매핑되거나 배열인 경우 쉼표로 연결됩니다. |
details.type |
about.resource.attribute.labels.value |
details.type 필드에서 직접 매핑되며 키 details.type 가 추가됩니다. |
details.type |
security_result.summary |
details.type 필드에서 직접 매핑됩니다. |
entity.app.id |
target.resource.id |
entity.app.id 필드에서 직접 매핑됩니다. |
entity.app.name |
target.resource.name |
entity.app.name 필드에서 직접 매핑됩니다. |
entity.channel.id |
target.resource.id |
entity.channel.id 필드에서 직접 매핑됩니다. |
entity.channel.name |
target.resource.name |
entity.channel.name 필드에서 직접 매핑됩니다. |
entity.channel.privacy |
target.resource.attribute.labels.value |
entity.channel.privacy 필드에서 직접 매핑되며 키 entity.channel.privacy 가 추가됩니다. |
entity.file.filetype |
target.resource.attribute.labels.value |
entity.file.filetype 필드에서 직접 매핑되며 키 entity.file.filetype 가 추가됩니다. |
entity.file.id |
target.resource.id |
entity.file.id 필드에서 직접 매핑됩니다. |
entity.file.name |
target.resource.name |
entity.file.name 필드에서 직접 매핑됩니다. |
entity.file.title |
target.resource.attribute.labels.value |
entity.file.title 필드에서 직접 매핑되며 키 entity.file.title 가 추가됩니다. |
entity.huddle.date_end |
about.resource.attribute.labels.value |
entity.huddle.date_end 필드에서 직접 매핑되며 키 entity.huddle.date_end 가 추가됩니다. |
entity.huddle.date_start |
about.resource.attribute.labels.value |
entity.huddle.date_start 필드에서 직접 매핑되며 키 entity.huddle.date_start 가 추가됩니다. |
entity.huddle.id |
about.resource.attribute.labels.value |
entity.huddle.id 필드에서 직접 매핑되며 키 entity.huddle.id 가 추가됩니다. |
entity.huddle.participants.0 |
about.resource.attribute.labels.value |
entity.huddle.participants.0 필드에서 직접 매핑되며 키 entity.huddle.participants.0 가 추가됩니다. |
entity.huddle.participants.1 |
about.resource.attribute.labels.value |
entity.huddle.participants.1 필드에서 직접 매핑되며 키 entity.huddle.participants.1 가 추가됩니다. |
entity.type |
target.resource.resource_subtype |
entity.type 필드에서 직접 매핑됩니다. |
entity.user.email |
target.user.email_addresses |
entity.user.email 필드에서 직접 매핑됩니다. |
entity.user.id |
target.user.product_object_id |
entity.user.id 필드에서 직접 매핑됩니다. |
entity.user.name |
target.user.user_display_name |
entity.user.name 필드에서 직접 매핑됩니다. |
entity.user.team |
target.user.group_identifiers |
entity.user.team 필드에서 직접 매핑됩니다. |
entity.workflow.id |
target.resource.id |
entity.workflow.id 필드에서 직접 매핑됩니다. |
entity.workflow.name |
target.resource.name |
entity.workflow.name 필드에서 직접 매핑됩니다. |
id |
metadata.product_log_id |
id 필드에서 직접 매핑됩니다. |
ip |
principal.ip |
ip 필드에서 직접 매핑됩니다. action 필드를 기반으로 한 로직에 따라 결정됩니다. 기본값은 USER_COMMUNICATION 이지만 action 값에 따라 USER_CREATION , USER_LOGIN , USER_LOGOUT , USER_RESOURCE_ACCESS , USER_RESOURCE_UPDATE_PERMISSIONS , USER_CHANGE_PERMISSIONS 와 같은 다른 값으로 변경됩니다. 'SLACK_AUDIT'으로 하드코딩됩니다. date_create 가 있는 경우 'Enterprise Grid'로 설정하고, 그렇지 않고 user_id 가 있는 경우 '감사 로그'로 설정합니다. 'Slack'으로 하드코딩됩니다. 'REMOTE'로 하드코딩됩니다. action 에 'user_login' 또는 'user_logout'이 포함된 경우 'SSO'로 설정됩니다. 그 외의 경우 'MACHINE'으로 설정합니다. 제공된 예시에서 매핑되지 않았습니다. 기본값은 'ALLOW'이지만 action 이 'user_login_failed'인 경우 'BLOCK'으로 설정됩니다. date_create 이 있으면 'Slack'으로 설정하고, 그렇지 않고 user_id 이 있으면 'SLACK'으로 설정합니다. |
user_agent |
network.http.user_agent |
user_agent 필드에서 직접 매핑됩니다. |
user_id |
principal.user.product_object_id |
user_id 필드에서 직접 매핑됩니다. |
username |
principal.user.product_object_id |
username 필드에서 직접 매핑됩니다. |
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가로부터 답변을 받으세요.