Tháng 9/2025, RubyGems.org: attacker giành được root password AWS account. Trong 4 phút, họ thay đổi root password, enumerate toàn bộ IAM roles, lấy credential từ EC2 instance. Phát hiện 11 ngày sau — khi một contributor tình cờ check billing thấy spike lạ.

Tại sao 11 ngày? Không có CloudTrail alert cho root activity. Nếu họ có metric filter { $.userIdentity.type = "Root" } → CloudWatch Alarm → SNS notification, họ đã biết trong vài phút.

CloudTrail là “camera an ninh” của AWS — ghi lại mọi API call trong account: ai, làm gì, khi nào, từ IP nào, thành công hay thất bại. Bài này setup CloudTrail đúng cách, dùng nó để điều tra incident, và alert real-time khi có dấu hiệu tấn công.


  flowchart LR
    API["Every API Call<br/>Console · CLI · SDK · IaC"]
    CT["CloudTrail"]
    S3["S3 Bucket<br/>gzip JSON logs"]
    CW["CloudWatch Logs<br/>real-time stream"]
    Lake["CloudTrail Lake<br/>SQL query, 7yr retention"]
    Athena["Athena<br/>SQL query on S3"]
    Filter["Metric Filters<br/>→ CloudWatch Alarms"]
    CT --> S3
    CT --> CW
    CT --> Lake
    S3 --> Athena
    CW --> Filter

3 loại event: chọn đúng, trả đúng giá

Event TypeGhi những gìMặc định?Giá
ManagementControl plane: Create/Delete/Update resourceCó (bản copy đầu miễn phí/account)Miễn phí (copy đầu)
DataData plane: GetObject, PutItem, InvokeKhông$0.10/100K events
InsightML phát hiện bất thường (API spike, unusual pattern)Không$0.35/100K events
Data events có thể rất đắt. S3 bucket 10M GET/ngày → Data events = $10/ngày = $300/tháng cho một bucket. Chỉ bật Data events cho bucket chứa PII/payment data. Dùng AdvancedEventSelectors để filter chính xác bucket và prefix — không dùng wildcard *.

Trail setup production-grade

# 1. S3 bucket cho log (với lifecycle policy)
aws s3api create-bucket --bucket aws-cloudtrail-logs-123456789012 --region ap-southeast-1

# 2. Bucket policy cho CloudTrail
aws s3api put-bucket-policy --bucket aws-cloudtrail-logs-123456789012 --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {"Effect":"Allow","Principal":{"Service":"cloudtrail.amazonaws.com"},"Action":"s3:GetBucketAcl","Resource":"arn:aws:s3:::aws-cloudtrail-logs-123456789012"},
    {"Effect":"Allow","Principal":{"Service":"cloudtrail.amazonaws.com"},"Action":"s3:PutObject","Resource":"arn:aws:s3:::aws-cloudtrail-logs-123456789012/AWSLogs/123456789012/*","Condition":{"StringEquals":{"s3:x-amz-acl":"bucket-owner-full-control"}}}
  ]
}'

# 3. Tạo trail: multi-region + global services + log validation + organization
aws cloudtrail create-trail \
  --name org-trail \
  --s3-bucket-name aws-cloudtrail-logs-123456789012 \
  --is-multi-region-trail \
  --include-global-service-events \
  --enable-log-file-validation \
  --is-organization-trail \
  --cloud-watch-logs-log-group-arn arn:aws:logs:ap-southeast-1:123456789012:log-group:cloudtrail:*

aws cloudtrail start-logging --name org-trail

4 flags mandatory:

  • --is-multi-region-trail: attacker thường nhắm region ít dùng → cần log tất cả region
  • --include-global-service-events: IAM, CloudFront, Route 53, STS
  • --enable-log-file-validation: digest SHA-256 mỗi giờ → phát hiện log bị chỉnh sửa
  • --is-organization-trail: một trail cho toàn organization → member account không thể tắt

Incident investigation với Athena

CREATE EXTERNAL TABLE cloudtrail_logs (
  eventVersion STRING,
  userIdentity STRUCT<type:STRING,arn:STRING,userName:STRING>,
  eventTime STRING, eventName STRING, sourceIPAddress STRING,
  userAgent STRING, errorCode STRING, requestParameters STRING
)
PARTITIONED BY (region STRING, year STRING, month STRING, day STRING)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://aws-cloudtrail-logs-123456789012/AWSLogs/123456789012/';

-- Ai xóa RDS instance?
SELECT userIdentity.userName, eventTime, sourceIPAddress
FROM cloudtrail_logs WHERE eventName = 'DeleteDBInstance'
  AND year='2026' AND month='06' ORDER BY eventTime DESC;

-- API nào bị deny nhiều nhất? (potential attack scan)
SELECT eventName, COUNT(*) AS denied, userIdentity.userName
FROM cloudtrail_logs WHERE errorCode IS NOT NULL
  AND year='2026' AND month='06'
GROUP BY eventName, userIdentity.userName ORDER BY denied DESC LIMIT 20;

-- IP lạ gọi API? (ngoài office range)
SELECT sourceIPAddress, userIdentity.userName, COUNT(*) AS calls
FROM cloudtrail_logs WHERE sourceIPAddress NOT LIKE '203.0.113.%'
  AND year='2026' AND month='06'
GROUP BY sourceIPAddress, userIdentity.userName ORDER BY calls DESC;

-- Root user activity (phải CỰC HIẾM)
SELECT eventTime, eventName, sourceIPAddress
FROM cloudtrail_logs WHERE userIdentity.type = 'Root'
  AND year='2026' ORDER BY eventTime DESC;

Metric filters: real-time security alert

Top 10 metric filter nên có:

#Filter PatternÝ nghĩa
1{ $.userIdentity.type = "Root" }Root activity
2{ $.eventName = "CreateUser" }IAM user được tạo (backdoor?)
3{ $.eventName = "AuthorizeSecurityGroupIngress" }SG rule thay đổi
4{ $.eventName = "StopLogging" }CloudTrail bị tắt
5{ $.eventName = "DeleteTrail" }Trail bị xóa
6{ $.eventName = "PutBucketPolicy" }Bucket policy thay đổi
7{ $.eventName = "DisableKey" }KMS key bị disable (ransomware)
8{ $.eventName = "DeleteBucket" }S3 bucket bị xóa
9{ $.eventName = "DetachPolicy" }Organization policy bị gỡ
10{ $.errorCode = "AccessDenied" }Spike deny request
aws logs put-metric-filter --log-group-name cloudtrail \
  --filter-name "RootUserActivity" \
  --filter-pattern '{ $.userIdentity.type = "Root" }' \
  --metric-transformations metricName=RootUserActivity,metricNamespace=CloudTrailMetrics,metricValue=1

Mình đã từng cấu hình thiếu metric filter Root activity cho một project, may phát hiện kịp trước audit. Từ đó mình không bao giờ bỏ qua bước này.


CloudTrail Lake vs Athena

LakeAthena on S3
SetupEnable → chọn retentionTạo table + partition
SQLCơ bản (không JOIN)Presto SQL đầy đủ
Cost$2.50/GB ingested + $0.005/GB scannedChỉ Athena scan (~$5/TB)
PartitionTheo ngày (auto)Tự chọn schema
Dùng khiSetup nhanh, query đơn giảnQuery phức tạp, JOIN, cost-sensitive

CloudTrail là camera an ninh, nhưng camera không có người xem thì cũng vô dụng. Metric filters + alert là người bảo vệ 24/7.

Bài sau: Phần 6: S3 cơ bản — bucket, object, encryption, static hosting