概要
やりたいこと
前回、AWS WAF ログを S3 バケットに保存した。このログを別アカウントの S3 バケットに保存したい。
クロスアカウントアクセスというらしい。
状況を以下とする。
- アカウント A : ここの S3 バケットに保存したい
- アカウント B : AWS WAF が設定されている
クロスアカウントアクセス
公式より実現方法は 3 つある。今回は1の方法を採用。
- リソースベースのポリシーと AWS Identity and Access Management (IAM) ポリシー (S3 バケットオブジェクトへのプログラムによるアクセス専用)
- リソースベースのアクセスコントロールリスト (ACL) と IAM ポリシー (S3 バケットオブジェクトへのプログラムによるアクセス専用)
- クロスアカウント IAM ロール (S3 バケットオブジェクトへのプログラムおよびコンソールによるアクセス)
リソースベースポリシーと IAM ポリシー
クロスアカウントアクセスしたい場合、リソースベースポリシーと IAM ポリシー両方で許可しなければならない。
- 単一アカウント内アクセス:IAMポリシーかリソースベースポリシーのどちらかの許可が必要
- クロスアカウントアクセス:IAMポリシーとリソースベースポリシーの両方の許可が必要
今回のリソースベースのポリシーは S3 のバケットポリシーとなる。
AWS リソースごとのリソースベースポリシーの対応状況はこちら参照。
CloudFormation
CloudFormation 準備
こちら参照。
アカウント A はコンソールより操作する。
アカウント B は CloudFormation で実行する。
mkdir waf-v2-basepolicy cd waf-v2-basepolicy
スタック名も今回 aws-waf-v2-basepolicy とする。
アカウントA : S3 バケット作成
アカウント B にて、以下の名前で S3 バケットを作成。バケットポリシーは後ほど作成する。
- waf-test-cross-account
アカウントB : Kinesis Data Firehose に IAM ロールを割り当てる
公式より、Kinesis Data Firehose に IAM ロールを割り当てるポリシーは以下。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "firehose.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId":"account-id"
}
}
}
]
}
CloudFormation で作成する。
vi waf_v2.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Create WebACL example
Resources:
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
AssumeRolePolicyDocument: #Kinesis Firehose に IAM を割り当てるため
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- firehose.amazonaws.com
Action:
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Sub ${AWS::AccountId}
スタック作成。
aws cloudformation create-stack --stack-name waf-v2-basepolicy --region ap-northeast-1 --template-body file://waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
アカウントB : アカウント A の S3 バケットアクセス用の IAM ポリシー作成
公式より、Kinesis が S3 バケットへアクセスするのに必要なポリシーは以下。
{
"Version": "2012-10-17",
"Statement":
[
{
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
]
},
{
"Effect": "Allow",
"Action": [
"kinesis:DescribeStream",
"kinesis:GetShardIterator",
"kinesis:GetRecords",
"kinesis:ListShards"
],
"Resource": "arn:aws:kinesis:region:account-id:stream/stream-name"
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": [
"arn:aws:kms:region:account-id:key/key-id"
],
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.region.amazonaws.com"
},
"StringLike": {
"kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::bucket-name/prefix*"
}
}
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:region:account-id:log-group:log-group-name:log-stream:log-stream-name"
]
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction",
"lambda:GetFunctionConfiguration"
],
"Resource": [
"arn:aws:lambda:region:account-id:function:function-name:function-version"
]
}
]
}
ただし、クロスアカウントアクセスの場合、Action に “s3:PutObjectAcl” が必要なる。
上記を考慮した firehose_delivery_policy を作成し、firehose_delivery_policy をアタッチした IAM Role を作成する。
AWSTemplateFormatVersion: 2010-09-09
Description: Create WebACL example
Parameters:
BucketName:
Type: String
Description: The name for the bucket.
Default: waf-test-cross-account
Resources:
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
Policies:
-
PolicyName: firehose_delivery_policy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- s3:AbortMultipartUpload
- s3:GetBucketLocation
- s3:GetObject
- s3:ListBucket
- s3:ListBucketMultipartUploads
- s3:PutObject
- s3:PutObjectAcl
Resource:
- !Sub arn:aws:s3:::${BucketName}
- !Sub arn:aws:s3:::${BucketName}/*
-
Effect: "Allow"
Action:
- kinesis:DescribeStream
- kinesis:GetShardIterator
- kinesis:GetRecords
- kinesis:ListShards
Resource:
- !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME%
-
Effect: "Allow"
Action:
- kms:GenerateDataKey
- kms:Decrypt
Resource:
- !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3
Condition:
StringEquals:
kms:ViaService: !Sub s3.${AWS::Region}.amazonaws.com
StringLike:
kms:EncryptionContext:aws:s3:arn:
- !Sub arn:aws:s3:::${BucketName}/*
- !Sub arn:aws:s3:::${BucketName}/%FIREHOSE_BUCKET_PREFIX%*
-
Effect: "Allow"
Action:
- logs:PutLogEvents
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/aws-waf-logs-${AWS::Region}-${WebACLName}:log-stream:*
-
Effect: "Allow"
Action:
- lambda:InvokeFunction
- lambda:GetFunctionConfiguration
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:%FIREHOSE_DEFAULT_FUNCTION%:%FIREHOSE_DEFAULT_VERSION%
-
Effect: "Allow"
Action:
- kms:Decrypt
Resource:
- !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/%SSE_KEY_ID%
Condition:
StringEquals:
kms:ViaService: kinesis.%REGION_NAME%.amazonaws.com
StringLike:
kms:EncryptionContext:aws:kinesis:arn: !Sub arn:aws:kinesis:%REGION_NAME%:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME%
構文チェックして反映。
aws cloudformation validate-template --template-body file://waf_v2.yaml aws cloudformation deploy --stack-name aws-waf-v2-basepolicy --template-file waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
IAM Role が作成され firehose_delivery_policy がアタッチされていることを確認。
この ARN は アカウントAの S3 バケットポリシーで利用するためメモしておく。
アカウントB : Kinesis Firehose を作成し、IAM Role と WebACL を紐付ける
CloudFormation 全体像。WebACLのルールは省略。
AWSTemplateFormatVersion: 2010-09-09
Description: Create WebACL example
Parameters:
WebACLName:
Description: The Name of WebACL.
Type: String
Default: ExampleWebACL
BucketName:
Type: String
Description: The name for the bucket.
Default: waf-test-cross-account
Resources:
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
AssumeRolePolicyDocument: # Kinesis FirehoseにIAM Roleを割り当てるため
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- firehose.amazonaws.com
Action:
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Sub ${AWS::AccountId}
Policies:
-
PolicyName: firehose_delivery_role
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- s3:AbortMultipartUpload
- s3:GetBucketLocation
- s3:GetObject
- s3:ListBucket
- s3:ListBucketMultipartUploads
- s3:PutObject
- s3:PutObjectAcl
Resource:
- !Sub arn:aws:s3:::${BucketName}
- !Sub arn:aws:s3:::${BucketName}/*
-
Effect: "Allow"
Action:
- kinesis:DescribeStream
- kinesis:GetShardIterator
- kinesis:GetRecords
- kinesis:ListShards
Resource:
- !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME%
-
Effect: "Allow"
Action:
- kms:GenerateDataKey
- kms:Decrypt
Resource:
- !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3
Condition:
StringEquals:
kms:ViaService: !Sub s3.${AWS::Region}.amazonaws.com
StringLike:
kms:EncryptionContext:aws:s3:arn:
- !Sub arn:aws:s3:::${BucketName}/*
- !Sub arn:aws:s3:::${BucketName}/%FIREHOSE_BUCKET_PREFIX%*
-
Effect: "Allow"
Action:
- logs:PutLogEvents
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/aws-waf-logs-${AWS::Region}-${WebACLName}:log-stream:*
-
Effect: "Allow"
Action:
- lambda:InvokeFunction
- lambda:GetFunctionConfiguration
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:%FIREHOSE_DEFAULT_FUNCTION%:%FIREHOSE_DEFAULT_VERSION%
-
Effect: "Allow"
Action:
- kms:Decrypt
Resource:
- !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/%SSE_KEY_ID%
Condition:
StringEquals:
kms:ViaService: kinesis.%REGION_NAME%.amazonaws.com
StringLike:
kms:EncryptionContext:aws:kinesis:arn: !Sub arn:aws:kinesis:%REGION_NAME%:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME%
FirehoseDeliveryStream:
Type: "AWS::KinesisFirehose::DeliveryStream"
Properties:
DeliveryStreamName: !Sub aws-waf-logs-${BucketName}-${AWS::Region}-${WebACLName}
DeliveryStreamType: DirectPut
S3DestinationConfiguration:
BucketARN: !Sub arn:aws:s3:::${BucketName}
BufferingHints:
IntervalInSeconds: 300
SizeInMBs: 5
CompressionFormat: GZIP
RoleARN: !GetAtt FirehoseRole.Arn
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: !Sub ${WebACLName}
Scope: REGIONAL
Description: This is an example WebACL
DefaultAction:
Allow: {} # Allow or Block or Count
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: !Sub ${WebACLName}
構文チェックして反映。
aws cloudformation validate-template --template-body file://waf_v2.yaml aws cloudformation deploy --stack-name aws-waf-v2-basepolicy --template-file waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
Kineis Firehose が作成され、作成した IAM がアタッチされていることを確認。
WebACL のLogging から Kinesis Firehose を有効化する。
アカウントA : バケットポリシー作成
公式より、以下のバケットポリシーを指定する必要があるとのこと。
{
"Version": "2012-10-17",
"Id": "PolicyID",
"Statement": [
{
"Sid": "StmtID",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::accountA-id:role/iam-role-name"
},
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
]
}
]
}
コンソールから操作。
- バケットを選択
- アクセス権限
- ブロックパブリックアクセス : オフ
- バケットポリシーに以下を記入。
- Principal : 上記で確認した Role の ARN
- Resource : S3 バケット名
{
"Version": "2012-10-17",
"Id": "Policy1589030458582",
"Statement": [
{
"Sid": "Stmt1589030372922",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/waf-v2-basepolicy-FirehoseRole-1IZH91LWU3BNE"
},
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::waf-test-cross-account/*",
"arn:aws:s3:::waf-test-cross-account"
]
}
]
}
AWS WAF 側でアラートを発生させて、S3 バケットに保存されていること。
エラー
Invalid principal in policy
「アカウントA : バケットポリシー作成」のところでこのエラーに遭遇。
原因は、既にアカウント A で存在しない arn を指定していたため。CloudFormation で作り直した際に arn の値が変わってしまっていた。
アカウント A 側も CloudFormation か Terraform で管理しないと。。
コメント