概要
やりたいこと
AWS WAF の検知ログを S3 に保存したい。
AWS WAF v2 リソースは既に作成されてるものとする。
Kinesis Firehose の IAM Role
AWS WAF ログを S3 に保存するには Kinesis Firehose を利用する必要がある。
Kinesis Firehose には S3 へのアクセス許可を付与する必要がある。
その方法が2種類ある。今回は 1 を採用。
- Amazon S3 の送信先へのアクセス権を Kinesis Data Firehose に付与する
- ウェブ ACL トラフィック情報のログ記録
iam:CreateServiceLinkedRole
firehose:ListDeliveryStreams
wafv2:PutLoggingConfiguration
1 の方で行う場合、2 種類の Role を付与する。
CloudFormation でデプロイ
準備
こちら参照。
S3 バケット
最初にログを貯める S3 バケットを作成しておく。
AWSTemplateFormatVersion: 2010-09-09
Description: Create WebACL example
Parameters:
BucketName:
Type: String
Description: The name for the bucket.
Default: waf-logs
Resources:
S3BacketForWAF:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub waf-logs-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
IAM Role 作成
S3 バケットを保存するための IAM Role を作成する。内容は
- Kinesis Firehose に Role を付与するための sts:AssumeRole
- S3 にアクセス許可を与える firehose_delivery_role
sts:AssumeRole
sts:AssumeRole を付与。
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-logs
Resources:
S3BacketForWAF:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub ${BucketName}-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- firehose.amazonaws.com
Action:
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Sub ${AWS::AccountId}
S3アクセス用ポリシー firehose_delivery_role
以下のポリシーを作成している。
- S3 バケットへのアクセス許可
- Kinesis へのアクセス許可
- S3 のデータ暗号化
- Kinesis によるログ保存イベントの許可
- Lambda Function の許可(CloudWatch用?)
- AWS KMS キー使用のポリシー
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-logs
Resources:
S3BacketForWAF:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub ${BucketName}-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
AssumeRolePolicyDocument:
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
Resource:
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/*
- arn:aws:s3:::%FIREHOSE_BUCKET_NAME%
- arn:aws:s3:::%FIREHOSE_BUCKET_NAME%/*
-
Effect: "Allow"
Action:
- kinesis:DescribeStream
- kinesis:GetShardIterator
- kinesis:GetRecords
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}-${AWS::AccountId}/*
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/%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%
Kinesis Firehose
AWS::KinesisFirehose::DeliveryStream を利用し、ログの保存先とログ記録を有効にする WebACL を指定する。
注意として、 DeliveryStreamName は「aws-waf-logs-」をプレフィックスとして付けなければならない。後述の WAF Logging 設定時に、選択肢に作成した Kinesis が表示されないため。公式で紹介されていた。
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-logs
Resources:
S3BacketForWAF:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub ${BucketName}-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
FirehoseRole:
Type: AWS::IAM::Role
Properties:
Path: '/'
AssumeRolePolicyDocument:
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
Resource:
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/*
- arn:aws:s3:::%FIREHOSE_BUCKET_NAME%
- arn:aws:s3:::%FIREHOSE_BUCKET_NAME%/*
-
Effect: "Allow"
Action:
- kinesis:DescribeStream
- kinesis:GetShardIterator
- kinesis:GetRecords
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}-${AWS::AccountId}/*
- !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/%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%
FirehoseDeliveryStream:
Type: "AWS::KinesisFirehose::DeliveryStream"
Properties:
DeliveryStreamName: !Sub aws-waf-logs-${BucketName}-${AWS::Region}-${WebACLName}
DeliveryStreamType: DirectPut
S3DestinationConfiguration:
BucketARN: !GetAtt S3BacketForWAF.Arn
BufferingHints:
IntervalInSeconds: 300
SizeInMBs: 5
CompressionFormat: GZIP
RoleARN: !GetAtt FirehoseRole.Arn
構文チェック後に反映。
aws cloudformation validate-template --template-body file://waf_v2.yaml aws cloudformation deploy --stack-name aws-waf-v2-test --template-file waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
Kinesis Data Firehose コンソールより作成されていることを確認。
AWS WAF 設定
AWS::WAFv2::WebACL を確認してもロギング設定をするパラメータが見当たらない。。AWS WAFコンソールから手動で設定しないといけないようだ。。
- 作成された WebACL を選択
- Logging and metrics
- Enable logging
- Amazon Kinesis Data Firehose Delivery Stream
- 上記で作成したもの
- Redacted fields
- お好みのもの
- Enable Logging
設定完了後、上手く動いていれば S3 バケットにログが保存されている。
中身は巨大な json。
{"timestamp":1589003189188,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:657885203613:regional/webacl/ExampleWebACL/08f08fc4-8cd1-4f7c-af58-c21b181d93e6","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"657885203613-app/webserver-alb/3c8d60490de0199e","ruleGroupList":[{"ruleGroupId":"AWS#AWSManagedRulesCommonRuleSet","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesKnownBadInputsRuleSet","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesAmazonIpReputationList","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesAnonymousIpList","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesSQLiRuleSet","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesLinuxRuleSet","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null},{"ruleGroupId":"AWS#AWSManagedRulesUnixRuleSet","terminatingRule":null,"nonTerminatingMatchingRules":[],"excludedRules":null}],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"111.108.92.1","country":"JP","headers":[{"name":"Host","value":"test-security-aws.net"},{"name":"Pragma","value":"no-cache"},{"name":"Cache-Control","value":"no-cache"},{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"},{"name":"Origin","value":"https://test-security-aws.net"},{"name":"Sec-WebSocket-Version","value":"13"},{"name":"Accept-Encoding","value":"gzip, deflate, br"},{"name":"Accept-Language","value":"ja,en-US;q=0.9,en;q=0.8"},{"name":"Cookie","value":"language=en; welcomebanner_status=dismiss; io=qD01R6-qBXefEj1bAAAP"},{"name":"Sec-WebSocket-Key","value":"gjOonPgUgqiZPcPKW1EZkg=="},{"name":"Sec-WebSocket-Extensions","value":"permessage-deflate; client_max_window_bits"}],"uri":"REDACTED","args":"REDACTED","httpVersion":"HTTP/1.1","httpMethod":"REDACTED","requestId":null}}
コードは github にまとめてある。
An error occurred (InsufficientCapabilitiesException) when calling the CreateChangeSet operation: Requires capabilities : [CAPABILITY_IAM]
Kinesis と IAM の設定反映の際にエラーが出てしまった。
$ aws cloudformation deploy --stack-name aws-waf-v2-test --template-file waf_v2.yaml An error occurred (InsufficientCapabilitiesException) when calling the CreateChangeSet operation: Requires capabilities : [CAPABILITY_IAM]
CloudFormation で「AWS::IAM::Role」Type を利用する場合、--capabilities
パラメータに CAPABILITY_IAM
または CAPABILITY_NAMED_IAM
値を指定する必要がある。
CAPABILITY_IAM
または CAPABILITY_NAMED_IAM
の使い分けは以下。
- CAPABILITY_IAM : テンプレートの IAM リソースにカスタム名がない場合
- CAPABILITY_NAMED_IAM : テンプレートの IAM リソースにカスタム名がある場合
今回はKinesis 用の IAM ポリシー(firehose_delivery_role)を作成するため、CAPABILITY_NAMED_IAM を利用する。
スタック作成時に
aws cloudformation create-stack --stack-name aws-waf-v2-test --region ap-northeast-1 --template-body file://waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
または、デプロイ時に--capabilities
パラメータを指定する。
aws cloudformation deploy --stack-name aws-waf-v2-test --template-file waf_v2.yaml --capabilities CAPABILITY_NAMED_IAM
参考
AWS WAFのLogを保存するKinesis FirehoseをCloudFormationでさくっと作ってみた
AWS Managed Rules for AWS WAF を構成するCloudFormationテンプレートを作ってみた
Terraform で AWS WAF のログを Kinesis Firehose 経由で logging する
when calling the CreateStack operation: Requires capabilities : [CAPABILITY_IAM]エラーの対応
コメント