Akamai Cloud Monitor 로그 수집
이 문서에서는 AWS S3를 사용하여 Akamai Cloud Monitor (로드 밸런서, 트래픽 셰이퍼, ADC) 로그를 Google Security Operations에 수집하는 방법을 설명합니다. Akamai는 JSON 이벤트를 HTTPS 엔드포인트로 푸시합니다. API 게이트웨이 + Lambda 수신기는 이벤트를 S3 (JSONL, gz)에 씁니다. 파서는 JSON 로그를 UDM으로 변환합니다. JSON 페이로드에서 필드를 추출하고, 데이터 유형 변환을 실행하고, UDM 스키마와 일치하도록 필드 이름을 바꾸고, 맞춤 필드 및 URL 구성을 위한 특정 로직을 처리합니다. 또한 필드 존재 여부에 따라 오류 처리 및 조건부 로직이 통합되어 있습니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스
- Akamai Control Center 및 Property Manager에 대한 권한이 있는 액세스
- AWS*(S3, IAM, Lambda, API Gateway)에 대한 권한 있는 액세스
Google SecOps용 AWS S3 버킷 및 IAM 구성
- 이 사용자 가이드(버킷 만들기)에 따라 Amazon S3 버킷을 만듭니다.
- 나중에 참조할 수 있도록 버킷 이름과 리전을 저장합니다 (예:
akamai-cloud-monitor
). - 이 사용자 가이드(IAM 사용자 만들기)에 따라 사용자를 만듭니다.
- 생성된 사용자를 선택합니다.
- 보안 사용자 인증 정보 탭을 선택합니다.
- 액세스 키 섹션에서 액세스 키 만들기를 클릭합니다.
- 사용 사례로 서드 파티 서비스를 선택합니다.
- 다음을 클릭합니다.
- 선택사항: 설명 태그를 추가합니다.
- 액세스 키 만들기를 클릭합니다.
- CSV 파일 다운로드를 클릭하여 나중에 사용할 수 있도록 액세스 키와 비밀 액세스 키를 저장합니다.
- 완료를 클릭합니다.
- 권한 탭을 선택합니다.
- 권한 정책 섹션에서 권한 추가를 클릭합니다.
- 권한 추가를 선택합니다.
- 정책 직접 연결을 선택합니다.
- AmazonS3FullAccess 정책을 검색하여 선택합니다.
- 다음을 클릭합니다.
- 권한 추가를 클릭합니다.
S3 업로드 (Lambda)를 위한 IAM 정책 및 역할 구성
- AWS 콘솔에서 IAM > 정책 > 정책 만들기 > JSON으로 이동하여 아래 정책을 붙여넣습니다.
JSON 정책 (
akamai-cloud-monitor
을 S3 버킷 이름으로 대체){ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutAkamaiObjects", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::akamai-cloud-monitor/*" } ] }
다음 > 정책 만들기를 클릭합니다.
IAM > 역할 > 역할 생성 > AWS 서비스 > Lambda로 이동합니다.
JSON 정책을 연결합니다.
역할 이름을
WriteAkamaiCMToS3Role
로 지정하고 역할 만들기를 클릭합니다.
Lambda 함수 만들기
설정 | 값 |
---|---|
이름 | akamai_cloud_monitor_to_s3 |
런타임 | Python 3.13 |
아키텍처 | x86_64 |
실행 역할 | WriteAkamaiCMToS3Role |
함수가 생성되면 코드 탭을 열고 스텁을 삭제하고 다음 코드를 입력합니다 (
akamai_cloud_monitor_to_s3.py
).#!/usr/bin/env python3 # Lambda: Receive Akamai Cloud Monitor POST, write JSONL (gz) to S3 import os, json, gzip, io, uuid, base64, datetime as dt import boto3 S3_BUCKET = os.environ["S3_BUCKET_NAME"] S3_PREFIX = os.environ.get("S3_PREFIX", "akamai/cloud-monitor/json/").strip("/") + "/" INGEST_TOKEN = os.environ.get("INGEST_TOKEN") # optional shared secret in URL query (?token=...) s3 = boto3.client("s3") def _write_jsonl_gz(objs: list) -> str: key = f"{dt.datetime.utcnow():%Y/%m/%d}/akamai-cloud-monitor-{uuid.uuid4()}.json.gz" buf = io.BytesIO() with gzip.GzipFile(fileobj=buf, mode="w") as gz: for o in objs: gz.write((json.dumps(o, separators=(",", ":")) + "n").encode()) buf.seek(0) s3.upload_fileobj( buf, S3_BUCKET, f"{S3_PREFIX}{key}", ExtraArgs={ "ContentType": "application/json", "ContentEncoding": "gzip", }, ) return f"s3://{S3_BUCKET}/{S3_PREFIX}{key}" def _parse_records_from_event(event) -> list: # HTTP API (Lambda proxy) event: body is a JSON string body = event.get("body") or "" if event.get("isBase64Encoded"): body = base64.b64decode(body).decode("utf-8", "replace") try: data = json.loads(body) except Exception: # accept line-delimited JSON as pass-through try: return [json.loads(line) for line in body.splitlines() if line.strip()] except Exception: return [] if isinstance(data, list): return data if isinstance(data, dict): return [data] return [] def lambda_handler(event, context=None): # Optional shared-secret verification via query parameter (?token=...) if INGEST_TOKEN: qs = event.get("queryStringParameters") or {} token = qs.get("token") if token != INGEST_TOKEN: return {"statusCode": 403, "body": "forbidden"} records = _parse_records_from_event(event) if not records: return {"statusCode": 204, "body": "no content"} key = _write_jsonl_gz(records) return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"ok": True, "s3_key": key, "count": len(records)}), }
구성 > 환경 변수 > 수정으로 이동합니다.
새 환경 변수 추가를 클릭하고 다음 값을 설정합니다.
환경 변수
키 예 S3_BUCKET_NAME
akamai-cloud-monitor
S3_PREFIX
akamai/cloud-monitor/json/
INGEST_TOKEN
random-shared-secret
구성 > 일반 구성으로 이동합니다.
수정을 클릭하고 시간 제한을 5분 (300초)으로 설정합니다.
저장을 클릭합니다.
Amazon API Gateway 만들기 (Akamai용 HTTPS 엔드포인트)
- AWS 콘솔에서 API Gateway > Create API로 이동합니다.
- HTTP API > 빌드를 선택합니다.
- 다음 구성 세부정보를 제공합니다.
- 통합: Lambda를 선택하고
akamai_cloud_monitor_to_s3
를 선택합니다. - 경로: ANY
/{proxy+}
를 추가하거나 특정 경로를 만듭니다 (예: POST/akamai/cloud-monitor
). - 단계: $default를 만들거나 사용합니다.
- 통합: Lambda를 선택하고
- API를 배포하고 호출 URL (예:
https://abc123.execute-api.<region>.amazonaws.com
)을 복사합니다.
로그를 푸시하도록 Akamai Cloud Monitor 구성
- Akamai Control Center의 Property Manager에서 속성을 엽니다.
- 규칙 추가> 클라우드 관리 선택을 클릭합니다.
- Cloud Monitor Instrumentation을 추가하고 필요한 데이터 세트를 선택합니다.
- Cloud Monitor Data Delivery 추가
- 전송 호스트 이름: API 게이트웨이 호출 URL을 입력합니다 (예:
abc123.execute-api.<region>.amazonaws.com
). - 게재 URL 경로: 경로 더하기 선택적 쿼리 토큰(예:
/akamai/cloud-monitor?token=<INGEST_TOKEN>
)입니다.
- 전송 호스트 이름: API 게이트웨이 호출 URL을 입력합니다 (예:
- 속성 버전을 저장하고 활성화합니다.
Akamai Cloud Monitor (S3 JSON)를 수집하도록 Google SecOps에서 피드 구성
- SIEM 설정> 피드로 이동합니다.
- 새 피드 추가를 클릭합니다.
- 피드 이름 필드에 피드 이름을 입력합니다 (예:
Akamai Cloud Monitor — S3
). - 소스 유형으로 Amazon S3 V2를 선택합니다.
- 로그 유형으로 Akamai Cloud Monitor를 선택합니다.
- 다음을 클릭합니다.
- 다음 입력 파라미터의 값을 지정합니다.
- S3 URI:
s3://akamai-cloud-monitor/akamai/cloud-monitor/json/
- 소스 삭제 옵션: 전송 후 파일이나 디렉터리를 삭제할지 여부입니다.
- 최대 파일 기간: 지난 일수 동안 수정된 파일을 포함합니다. 기본값은 180일입니다.
- 액세스 키 ID: 20자리 영숫자 계정 액세스 키입니다 (예: AKIAIOSFODNN7EXAMPLE)을 사용합니다.
- 보안 비밀 액세스 키: 40자리 영숫자 계정 보안 비밀 액세스 키입니다 (예: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY).
- 애셋 네임스페이스:
akamai.cloud_monitor
- 수집 라벨: 이 피드의 모든 이벤트에 라벨이 추가됩니다 (예:
source=akamai_cloud_monitor
,format=json
).
- S3 URI:
- 다음을 클릭합니다.
- 확정 화면에서 새 피드 구성을 검토한 다음 제출을 클릭합니다.
UDM 매핑 테이블
로그 필드 | UDM 매핑 | 논리 |
---|---|---|
accLang |
network.http.user_agent |
'-' 또는 빈 문자열이 아닌 경우 직접 매핑됩니다. |
city |
principal.location.city |
'-' 또는 빈 문자열이 아닌 경우 직접 매핑됩니다. |
cliIP |
principal.ip |
빈 문자열이 아닌 경우 직접 매핑됩니다. |
country |
principal.location.country_or_region |
'-' 또는 빈 문자열이 아닌 경우 직접 매핑됩니다. |
cp |
additional.fields |
키가 'cp'인 키-값 쌍으로 매핑됩니다. |
customField |
about.ip , about.labels , src.ip |
키-값 쌍으로 파싱됩니다. src.ip 및 about.ip 에 각각 매핑하기 위한 'eIp' 및 'pIp'의 특별 처리 기타 키는 about 내에서 라벨로 매핑됩니다. |
errorCode |
security_result.summary , security_result.severity |
있는 경우 security_result.severity 을 'ERROR'로 설정하고 값을 security_result.summary 에 매핑합니다. |
geo.city |
principal.location.city |
city 이 '-' 또는 빈 문자열인 경우 직접 매핑됩니다. |
geo.country |
principal.location.country_or_region |
country 이 '-' 또는 빈 문자열인 경우 직접 매핑됩니다. |
geo.lat |
principal.location.region_latitude |
직접 매핑되고 부동 소수점으로 변환됩니다. |
geo.long |
principal.location.region_longitude |
직접 매핑되고 부동 소수점으로 변환됩니다. |
geo.region |
principal.location.state |
직접 매핑됩니다. |
id |
metadata.product_log_id |
빈 문자열이 아닌 경우 직접 매핑됩니다. |
message.cliIP |
principal.ip |
cliIP 이 빈 문자열인 경우 직접 매핑됩니다. |
message.fwdHost |
principal.hostname |
직접 매핑됩니다. |
message.reqHost |
target.hostname , target.url |
target.url 를 구성하고 target.hostname 를 추출하는 데 사용됩니다. |
message.reqLen |
network.sent_bytes |
totalBytes 이 비어 있거나 '-'인 경우 직접 매핑되고 부호 없는 정수로 변환됩니다. |
message.reqMethod |
network.http.method |
reqMethod 이 빈 문자열인 경우 직접 매핑됩니다. |
message.reqPath |
target.url |
target.url 에 추가되었습니다. |
message.reqPort |
target.port |
직접 매핑되며, reqPort 이 빈 문자열인 경우 정수로 변환됩니다. |
message.respLen |
network.received_bytes |
직접 매핑되고 부호 없는 정수로 변환됩니다. |
message.sslVer |
network.tls.version |
직접 매핑됩니다. |
message.status |
network.http.response_code |
직접 매핑되며 statusCode 이 비어 있거나 '-'인 경우 정수로 변환됩니다. |
message.UA |
network.http.user_agent |
UA 이 '-' 또는 빈 문자열인 경우 직접 매핑됩니다. |
network.asnum |
additional.fields |
키가 'asnum'인 키-값 쌍으로 매핑됩니다. |
network.edgeIP |
intermediary.ip |
직접 매핑됩니다. |
network.network |
additional.fields |
키가 'network'인 키-값 쌍으로 매핑됩니다. |
network.networkType |
additional.fields |
키가 'networkType'인 키-값 쌍으로 매핑됩니다. |
proto |
network.application_protocol |
network.application_protocol 를 결정하는 데 사용됩니다. |
queryStr |
target.url |
'-' 또는 빈 문자열이 아닌 경우 target.url 에 추가됩니다. |
referer |
network.http.referral_url , about.hostname |
'-'가 아닌 경우 직접 매핑됩니다. 추출된 호스트 이름은 about.hostname 에 매핑됩니다. |
reqHost |
target.hostname , target.url |
target.url 를 구성하고 target.hostname 를 추출하는 데 사용됩니다. |
reqId |
metadata.product_log_id , network.session_id |
id 이 빈 문자열인 경우 직접 매핑됩니다. network.session_id 에도 매핑됩니다. |
reqMethod |
network.http.method |
빈 문자열이 아닌 경우 직접 매핑됩니다. |
reqPath |
target.url |
'-'가 아닌 경우 target.url 에 추가됩니다. |
reqPort |
target.port |
직접 매핑되고 정수로 변환됩니다. |
reqTimeSec |
metadata.event_timestamp , timestamp |
이벤트 타임스탬프를 설정하는 데 사용됩니다. |
start |
metadata.event_timestamp , timestamp |
reqTimeSec 이 빈 문자열인 경우 이벤트 타임스탬프를 설정하는 데 사용됩니다. |
statusCode |
network.http.response_code |
직접 매핑되며, '-' 또는 빈 문자열이 아닌 경우 정수로 변환됩니다. |
tlsVersion |
network.tls.version |
직접 매핑됩니다. |
totalBytes |
network.sent_bytes |
직접 매핑되며 비어 있지 않거나 '-'인 경우 부호 없는 정수로 변환됩니다. |
type |
metadata.product_event_type |
직접 매핑됩니다. |
UA |
network.http.user_agent |
'-' 또는 빈 문자열이 아닌 경우 직접 매핑됩니다. |
version |
metadata.product_version |
직접 매핑됩니다. |
xForwardedFor |
principal.ip |
'-' 또는 빈 문자열이 아닌 경우 직접 매핑됩니다. |
(파서 로직) | metadata.vendor_name |
'Akamai'로 설정됩니다. |
(파서 로직) | metadata.product_name |
'DataStream'으로 설정됩니다. |
(파서 로직) | metadata.event_type |
'NETWORK_HTTP'로 설정됩니다. |
(파서 로직) | metadata.product_version |
version 이 빈 문자열인 경우 '2'로 설정합니다. |
(파서 로직) | metadata.log_type |
'AKAMAI_CLOUD_MONITOR'로 설정됩니다. |
(파서 로직) | network.application_protocol |
proto 또는 message.proto 에서 결정됩니다. 둘 중 하나에 'HTTPS' (대소문자 구분 안 함)가 포함된 경우 'HTTPS'로 설정하고, 그렇지 않은 경우 'HTTP'로 설정합니다. |
(파서 로직) | security_result.severity |
errorCode 이 '-' 또는 빈 문자열인 경우 'INFORMATIONAL'로 설정합니다. |
(파서 로직) | target.url |
protocol , reqHost (또는 message.reqHost ), reqPath (또는 message.reqPath ), queryStr 로 구성됩니다. |
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가로부터 답변을 받으세요.