概要
CloudTrail 単体でも S3 にログは残るが、IAM 系イベントだけを任意のS3に決まったパーティション構造で流したい という要望があった。EventBridge を介して Kinesis Data Firehose に渡すことで、フィルタとプレフィックス整形を行える
CFnでテンプレートを作成したのでまとめておく
なぜ CloudTrail の S3 出力をそのまま使わないのか
CloudTrail を有効化すれば、管理イベントは arn:aws:s3:::aws-cloudtrail-logs-... 形式のバケットに自動で蓄積される。これでも調査自体はできるが、運用してみると以下が気になる。
| 観点 | CloudTrail 既定の S3 出力 | EventBridge → Firehose → S3 |
|---|---|---|
| バケット | CloudTrail が管理する命名のバケット | 任意の自バケットを指定可 |
| フィルタ | 「管理イベント全部」など粗粒度 |
source: aws.iam などイベント単位で絞れる |
| パーティション |
AWSLogs/<account>/CloudTrail/<region>/yyyy/MM/dd/ 固定 |
自分で設計できる(後段の Athena 射影と整合させる) |
| レコード形式 | gzip 圧縮された JSON 配列(Records[]) |
JSON Lines(1 行 1 イベント) に整形可能 |
| 後段の Athena テーブル | CloudTrail 形式に合わせた SerDe が必要 | EventBridge イベント構造そのまま |
保管期間/検索の自由度が必要だったため、EventBridge + Firehose で S3に保管し Athena で検索することにした。
アーキテクチャ
事前条件: CloudTrail の EventBridge 配信を有効化
CloudFormation だけ流してもイベントは出ない。
CloudTrail のトレイル設定画面で:
- 管理イベント有効
- 「データイベントに加えて、EventBridge にイベントを送信」を ON
これをやらないと、aws.iam ソースのイベントは EventBridge デフォルトバスに届かない。スタックは成功するのに S3 に何も出ない、という現象になる。
CloudFormation テンプレート
1. EventBridge → Firehose の IAM ロール
events.amazonaws.com を Principal にして、firehose:PutRecord* を許可。
EventBridgeToFirehoseRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: InvokeFirehose
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- firehose:PutRecord
- firehose:PutRecordBatch
Resource: !GetAtt IamEventsDeliveryStream.Arn
EventBridge ルールの Target に RoleArn を渡す点を忘れない(Lambda などと違い、EventBridge → Firehose は実行ロールが必須)。
2. Firehose → S3 / CloudWatch Logs のロール
firehose.amazonaws.com を Principal に、出力先 S3 と配信ログ用 CloudWatch Logs への権限を付与。
FirehoseToS3Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: firehose.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: S3Delivery
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:AbortMultipartUpload
- s3:GetBucketLocation
- s3:GetObject
- s3:ListBucket
- s3:ListBucketMultipartUploads
- s3:PutObject
Resource:
- !Sub 'arn:aws:s3:::${ExistingS3BucketName}'
- !Sub 'arn:aws:s3:::${ExistingS3BucketName}/*'
S3 側の Resource は バケット本体(List 用)とオブジェクト(Put 用)の両方が要る。片方だけだと配信が失敗する。
3. Kinesis Data Firehose 本体
IamEventsDeliveryStream:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: !Ref DeliveryStreamName
DeliveryStreamType: DirectPut
ExtendedS3DestinationConfiguration:
RoleARN: !GetAtt FirehoseToS3Role.Arn
BucketARN: !Sub 'arn:aws:s3:::${ExistingS3BucketName}'
Prefix: '!{timestamp:yyyy}/!{timestamp:MM}/!{timestamp:dd}/'
ErrorOutputPrefix: 'errors/!{firehose:error-output-type}/!{timestamp:yyyy}/!{timestamp:MM}/!{timestamp:dd}/'
BufferingHints:
SizeInMBs: 5
IntervalInSeconds: 300
CompressionFormat: UNCOMPRESSED
ProcessingConfiguration:
Enabled: true
Processors:
- Type: AppendDelimiterToRecord
Parameters:
- ParameterName: Delimiter
ParameterValue: "\\n"
4. EventBridge ルール
CloudTrailIamEventRule:
Type: AWS::Events::Rule
Properties:
State: ENABLED
EventPattern:
source:
- aws.iam
detail-type:
- AWS API Call via CloudTrail
Targets:
- Id: FirehoseStream
Arn: !GetAtt IamEventsDeliveryStream.Arn
RoleArn: !GetAtt EventBridgeToFirehoseRole.Arn