非Organizations環境でCloudTrail証跡をS3レプリケーションで集約管理してみた
こんにちは!クラウド事業本部のおつまみです。
今回は非Organizations環境にて、CloudTrailを1つの証跡でログアーカイブアカウントにまとめつつ、メンバーアカウントにログを残す方法をご紹介します。
アーキテクチャ
実装するアーキテクチャはこちらです。
本手順の前提条件
- 非Organizations環境であること。
- Organizations環境をご利用の方はこちらのブログをご参考ください。
- CloudTrailが設定済であること。
- 未設定な場合はこちらのブログを参考にし、有効化してください。
- CloudTrailログが保存されているS3バケットの暗号化はSSE-S3を使用していること。
- SSE-KMSを利用している場合には、こちらのブログを参考にしてください。
構築手順
1. 集約アカウント側の設定
まずはログアーカイブアカウント側でレプリケーションが可能なS3バケットを用意します。
<アカウントID>
は集約アカウントのAWSアカウントIDに変換してください。
S3バケットの作成
aws s3api create-bucket \
--bucket aggregation-cloudtrail-logs-<アカウントID> \
--region ap-northeast-1 \
--create-bucket-configuration LocationConstraint=ap-northeast-1 \
--object-lock-enabled-for-bucket
バケットのバージョニング有効化
aws s3api put-bucket-versioning \
--bucket aggregation-cloudtrail-logs-<アカウントID> \
--versioning-configuration Status=Enabled
2. ソースアカウント側の設定
弊社請求代行サービスの「メンバーズ」をご利用のお客様はアカウント払出し時にCloudTrailが設定された状態で払い出されます。
以下は既存のCloudTrail証跡「Members」とS3バケット「cm-members-cloudtrail-<アカウントID>」を使用する場合の手順となります。
既存S3バケットの確認
以下コマンドを実行し、バケットが存在します
と表示されればOKです。
# アカウントIDを取得
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
# バケット名の確認
BUCKET_NAME="cm-members-cloudtrail-${ACCOUNT_ID}"
echo "対象バケット: ${BUCKET_NAME}"
# バケットの存在確認
aws s3api head-bucket --bucket ${BUCKET_NAME} --output text > /dev/null 2>&1 && echo "バケットが存在します" || echo "バケットが存在しません"
バージョニングの有効化(未設定の場合)
# 現在のバージョニング状態を確認
aws s3api get-bucket-versioning --bucket ${BUCKET_NAME}
上記コマンドを実行し、"Status": "Disbled"
が表示された場合に以下のコマンドを実行し、バージョニングを有効化します。
# バージョニングが無効な場合は有効化
aws s3api put-bucket-versioning \
--bucket ${BUCKET_NAME} \
--versioning-configuration Status=Enabled
レプリケーション用IAMロールの作成
以下のyamlファイルを準備します。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudTrail S3 Replication IAM Role for cross-account log aggregation'
Parameters:
DestinationAccountId:
Type: String
Description: 'Destination account ID for log aggregation'
AllowedPattern: '[0-9]{12}'
ConstraintDescription: 'Must be a valid 12-digit AWS account ID'
Resources:
S3ReplicationRole:
Type: AWS::IAM::Role
Properties:
RoleName: S3ReplicationRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: s3.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: S3ReplicationPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetReplicationConfiguration
- s3:ListBucket
Resource: !Sub 'arn:aws:s3:::cm-members-cloudtrail-${AWS::AccountId}'
- Effect: Allow
Action:
- s3:GetObjectVersionForReplication
- s3:GetObjectVersionAcl
- s3:GetObjectVersionTagging
Resource: !Sub 'arn:aws:s3:::cm-members-cloudtrail-${AWS::AccountId}/*'
- Effect: Allow
Action:
- s3:ReplicateObject
- s3:ReplicateTags
- s3:ReplicateDelete
- s3:ObjectOwnerOverrideToBucketOwner
Resource: !Sub 'arn:aws:s3:::aggregation-cloudtrail-logs-${DestinationAccountId}/*'
Outputs:
ReplicationRoleArn:
Description: 'ARN of the S3 Replication Role'
Value: !GetAtt S3ReplicationRole.Arn
Export:
Name: !Sub '${AWS::StackName}-ReplicationRoleArn'
ReplicationRoleName:
Description: 'Name of the S3 Replication Role'
Value: !Ref S3ReplicationRole
Export:
Name: !Sub '${AWS::StackName}-ReplicationRoleName'
以下の<集約先アカウントID>を置換し、コマンドを実行します。
# 集約先アカウントIDを指定してスタックを作成
aws cloudformation create-stack \
--stack-name cloudtrail-replication-role \
--template-body file://s3-replication-role.yaml \
--parameters ParameterKey=DestinationAccountId,ParameterValue=<集約先アカウントID> \
--capabilities CAPABILITY_NAMED_IAM
# スタック作成の完了を待つ
aws cloudformation wait stack-create-complete \
--stack-name cloudtrail-replication-role
既存バケットにレプリケーション設定を追加
以下のファイルを準備します。${DESTINATION_ACCOUNT_ID}
は集約先アカウントのIDに置き換えてください。
{
"Role": "arn:aws:iam::${ACCOUNT_ID}:role/S3ReplicationRole",
"Rules": [
{
"ID": "ReplicateCloudTrailLogs",
"Priority": 1,
"Status": "Enabled",
"Filter": {
"Prefix": "AWSLogs/"
},
"Destination": {
"Bucket": "arn:aws:s3:::aggregation-cloudtrail-logs-${DESTINATION_ACCOUNT_ID}",
"Account": "${DESTINATION_ACCOUNT_ID}",
"StorageClass": "STANDARD_IA",
"AccessControlTranslation": {
"Owner": "Destination"
},
"ReplicationTime": {
"Status": "Enabled",
"Time": {
"Minutes": 15
}
},
"Metrics": {
"Status": "Enabled",
"EventThreshold": {
"Minutes": 15
}
}
},
"DeleteMarkerReplication": {
"Status": "Disabled"
}
}
]
}
以下の<集約先アカウントID>を置換し、コマンドを実行します。
# 現在のアカウントIDと集約先アカウントIDを設定
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
DESTINATION_ACCOUNT_ID=<集約先アカウントID> # ここに実際の集約先アカウントIDを入力
以下コマンドを実行し、Enabled
が表示されることを確認します。
# アカウントIDを置換してレプリケーション設定を適用
sed -e "s/\${ACCOUNT_ID}/${ACCOUNT_ID}/g" \
-e "s/\${DESTINATION_ACCOUNT_ID}/${DESTINATION_ACCOUNT_ID}/g" \
replication-config.json > replication-config-filled.json
# レプリケーション設定を適用
aws s3api put-bucket-replication \
--bucket cm-members-cloudtrail-${ACCOUNT_ID} \
--replication-configuration file://replication-config-filled.json
# 設定が適用されたか確認
echo "レプリケーション設定の確認:"
aws s3api get-bucket-replication --bucket cm-members-cloudtrail-${ACCOUNT_ID} --query 'ReplicationConfiguration.Rules[0].Status' --output text
3. 集約アカウント側でバケットポリシーの設定
ソースアカウント側の設定が完了したら、集約アカウント側でバケットポリシーを設定します。
これにより、ソースアカウントからのレプリケーションが許可されます。
以下のファイルを準備します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReplicationFromSourceAccount",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ソースアカウントID>:role/S3ReplicationRole"
},
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
],
"Resource": "arn:aws:s3:::aggregation-cloudtrail-logs-<集約アカウントID>/*"
},
{
"Sid": "AllowReplicationPolicyCheck",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ソースアカウントID>:role/S3ReplicationRole"
},
"Action": [
"s3:GetBucketVersioning",
"s3:PutBucketVersioning",
"s3:List*"
],
"Resource": "arn:aws:s3:::aggregation-cloudtrail-logs-<集約アカウントID>"
}
]
}
以下の<ソースアカウントID>を置換し、コマンドを実行します。
# アカウントIDを設定
DESTINATION_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) # 集約先アカウントID
SOURCE_ACCOUNT_ID=<ソースアカウントID> # ここに実際のソースアカウントIDを入力
以下コマンドを実行し、設定されたバケットポリシーが表示されることを確認します。
# バケットポリシーファイルの作成(sedで置換)
sed -e "s/<集約アカウントID>/${DESTINATION_ACCOUNT_ID}/g" \
-e "s/<ソースアカウントID>/${SOURCE_ACCOUNT_ID}/g" \
bucket-policy.json > bucket-policy-filled.json
# バケットポリシーを適用
aws s3api put-bucket-policy \
--bucket aggregation-cloudtrail-logs-${DESTINATION_ACCOUNT_ID} \
--policy file://bucket-policy-filled.json
# ポリシーが適用されたか確認
echo "適用されたバケットポリシー:"
aws s3api get-bucket-policy \
--bucket aggregation-cloudtrail-logs-${DESTINATION_ACCOUNT_ID} \
--query Policy --output text | jq .
以上で設定は完了です。
ソースアカウントを追加する場合
既存の構成に新たなソースアカウントを追加する場合は、以下の手順で実施します。
1. 新規ソースアカウント側の設定
新規ソースアカウントで、上記「2. ソースアカウント側の設定」の手順を実施します。
主な作業:
- CloudTrailバケットのバージョニング有効化
- レプリケーション用IAMロールの作成
- レプリケーション設定の追加
2. 集約アカウント側のバケットポリシー更新
既存のバケットポリシーに新規ソースアカウントからのレプリケーションを許可する設定を追加します。
新規ソースアカウント用のPrincipal追加
既存のStatementのPrincipalに新しいソースアカウントを追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReplicationFromSourceAccounts",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<既存ソースアカウントID>:role/S3ReplicationRole",
"arn:aws:iam::<新規ソースアカウントID>:role/S3ReplicationRole"
]
},
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
],
"Resource": "arn:aws:s3:::aggregation-cloudtrail-logs-<集約アカウントID>/*"
},
{
"Sid": "AllowReplicationPolicyCheck",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<既存ソースアカウントID>:role/S3ReplicationRole",
"arn:aws:iam::<新規ソースアカウントID>:role/S3ReplicationRole"
]
},
"Action": [
"s3:GetBucketVersioning",
"s3:PutBucketVersioning",
"s3:List*"
],
"Resource": "arn:aws:s3:::aggregation-cloudtrail-logs-<集約アカウントID>"
}
]
}
動作確認
設定完了後、以下の点を確認しました。
- 新規ソースアカウント側
- CloudTrailログが生成されていることを確認
- レプリケーションステータスが
COMPLETED
になっていることを確認
- 集約アカウント側
- 新規ソースアカウントのログが集約バケットに表示されることを確認
- レプリケーションステータスが
REPLICA
になっていることを確認
まとめ
今回は、Organizations環境を使わずにS3レプリケーションでCloudTrailログを集約する方法についてご紹介しました。
この方法を使えば、非Organizations環境でも複数AWSアカウントのログを効率的かつコスト効果的に管理でき、セキュリティ監査やコンプライアンス対応が大幅に改善されます。
最後までお読みいただきありがとうございました!
以上、おつまみ(@AWS11077)でした!