こんにちは!AWS事業本部のおつまみです。
みなさん!AWS WAFのWebACLトラフィックログは有効化していますか?
2023年5月時点でログは以下のサービスに出力することができるようになっています。
- CloudWatch Logs
- S3
- Kinesis
2021年11月より、CloudWatch Logs、S3に直接がログが出力できるようになっています。
詳細はこちらのブログをご確認ください。
今回はCloudFormationを利用し、AWS WAFのログをCloudWatchLogsに出力する環境を構築してみたいと思います。
いくつかハマった箇所もあったので、その点についても合わせてご紹介します。
S3に出力する環境方法を知りたい方はこちらのブログをご確認ください。
構成図
本記事で構築するAWS環境は以下の構成です。
背景が赤色の範囲が本記事で構築するリソースです。
- AWS WAF(WebAcl)
- CloudWatch Logs log group
背景が灰色の範囲は、既存環境のリソースです。
※作成方法は割愛します。
- ALB
- EC2
やってみた
実行するCloudFormationテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: "create waf Resources"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
PJPrefix:
Type: String
Default: "demo"
Description: "Fill in the name of the system name."
Env:
Type: String
Default: "test"
Description: "Fill in the name of the environment."
WebAclAssociationResourceArn:
Type: String
Default: "arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:loadbalancer/app/XXXXXXXXXXXX"
Description: Enter RegionalResource(ALB,APIGateway,AppSync) ARN or CloudFront ARN to associate with WEBACL.
# ------------------------------------------------------------#
# create Resources
# ------------------------------------------------------------#
Resources:
WebACL:
Type: "AWS::WAFv2::WebACL"
Properties:
Name: !Sub ${PJPrefix}-${Env}-alb-acl
Scope: "REGIONAL"
DefaultAction:
Allow: {}
Description: "Web ACL for InternetALB"
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
Rules:
- Name: "AWS-AWSManagedRulesCommonRuleSet"
Priority: 1
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: "AWS-AWSManagedRulesCommonRuleSet"
Statement:
ManagedRuleGroupStatement:
Name: "AWSManagedRulesCommonRuleSet"
VendorName: "AWS"
WAFLogConfig:
Type: AWS::WAFv2::LoggingConfiguration
Properties:
LogDestinationConfigs:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:aws-waf-logs-${PJPrefix}-${Env}-alb-acl
ResourceArn: !GetAtt WebACL.Arn
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
ResourceArn: !Ref WebAclAssociationResourceArn
WebACLArn: !GetAtt WebACL.Arn
WAFLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
RetentionInDays: 365
Tags:
- Key: Name
Value: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
WAFToCWLogsPolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: WAFToCWLogsPolicy
PolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "${CloudWatchLogsLogGroupArn}",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ${AWS::AccountId}
},
"ArnLike": {
"aws:SourceArn": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
}
}
}
]
}
- CloudWatchLogsLogGroupArn: !GetAtt WAFLogGroup.Arn
今回テンプレート内で作成するリソースは以下です。
- AWS WAF
- WebACL
- LoggingConfiguration(ログ出力先の設定)
- WebACLAssociation(Web Aclに関連付けするリソースの設定)
- CloudWatchLogs
- LogGroup
- ResourcePolicy
各リソースについて、簡単に解説します。
AWS WAF
WebAcl
WAFはWeb Aclという単位でリソースの作成、設定を行います。
ルールは今回AWS提供のマネージドルールAWS-AWSManagedRulesCommonRuleSet
を設定しました。要件に応じて変更してください。
WebACL:
Type: "AWS::WAFv2::WebACL"
Properties:
Name: !Sub ${PJPrefix}-${Env}-alb-acl
Scope: "REGIONAL"
DefaultAction:
Allow: {}
Description: "Web ACL for InternetALB"
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
Rules:
- Name: "AWS-AWSManagedRulesCommonRuleSet"
Priority: 1
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: "AWS-AWSManagedRulesCommonRuleSet"
Statement:
ManagedRuleGroupStatement:
Name: "AWSManagedRulesCommonRuleSet"
VendorName: "AWS"
CloudFormationの記述方法は下記を参考にしています。
AWS::WAFv2::WebACL - AWS CloudFormation
AWS::WAFv2::WebACL Rule - AWS CloudFormation
LoggingConfiguration
WAFのWebACLトラフィックログ出力先の設定を行います。
LogDestinationConfigs
の記述方法で1ハマりしたので、後半で解説します。
WAFLogConfig:
Type: AWS::WAFv2::LoggingConfiguration
Properties:
LogDestinationConfigs:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:aws-waf-logs-${PJPrefix}-${Env}-alb-acl
ResourceArn: !GetAtt WebACL.Arn
CloudFormatinonの記述方法は下記を参考にしています。
AWS::WAFv2::LoggingConfiguration - AWS CloudFormation
WebACLAssociation
Web Aclに関連付けするリソースの設定を行います。
今回は既に構築済のALBのARNをParameterで指定し、ResourceArn
で関連付けています。
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
ResourceArn: !Ref WebAclAssociationResourceArn
WebACLArn: !GetAtt WebACL.Arn
CloudFormatinonの記述方法は下記を参考にしています。
AWS::WAFv2::WebACLAssociation - AWS CloudFormation
CloudWatchLogs
LogGroup
WebACLトラフィックログ出力先となるCloudWatchLogs Loggroupを作成します。
RetentionInDays
で保持期間を設定しています。 今回は365(1年)
で設定していますが、コストに関係する部分であるため、必要に応じて設定してください。
WAFLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
RetentionInDays: 365
Tags:
- Key: Name
Value: !Sub aws-waf-logs-${PJPrefix}-${Env}-alb-acl
CloudFormatinonの記述方法は下記を参考にしています。
AWS::Logs::LogGroup - AWS CloudFormation
ResourcePolicy
WAFのWebACLトラフィックログを出力するために、CloudWatchLogsに与えるリソースベースポリシーの作成を行います。
当初はこのポリシーを作成しておらず、ハマりしました 。。(2ハマり目)
詳細は後半で後半で解説します。
WAFToCWLogsPolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: WAFToCWLogsPolicy
PolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "${CloudWatchLogsLogGroupArn}",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ${AWS::AccountId}
},
"ArnLike": {
"aws:SourceArn": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
}
}
}
]
}
- CloudWatchLogsLogGroupArn: !GetAtt WAFLogGroup.Arn
CloudFormatinonの記述方法は下記を参考にしています。
AWS::Logs::ResourcePolicy - AWS CloudFormation
CloudFormation実行
今回はマネジメントコンソール経由でCloudFormationテンプレートを実行します。
リージョンは東京リージョン
を指定し、テンプレートをアップロードします。
パラメータは以下のように設定します。
値 | 説明 |
---|---|
スタックの名前 | 任意の名前 |
Env | AWSリソースの接頭語として付与できる文字列(環境名)を設定します。 |
Prefix | AWSリソースの接頭語として付与できる文字列(プロジェクト名など)を設定します。 |
WebAclAssociationResourceArn | WebAclに関連付けするAWSリソースのARNを指定します。AWSリソースの例) ALB,Cloudfront,API Gateway,AppSync |
スタックの作成を実行し、実行結果がCREATE_COMPLETE
になっていることを確認します。
リソースの確認
テンプレートで定義したリソースが正常に作成・設定できているか確認します。
WAF
WAF & Shield コンソール で Web Acl
を選択します。
今回は東京リージョンで作成したので、リージョン選択欄にAsia Pacific (Tokyo)
を指定します。
作成されているWAFを選択します。
各タブを確認し、設定が適切であることを確認します。
Rules
Associated AWS resources
Logging and metrics
CloudWatchLogs
WAFのログ設定で設定されているLogGroupを選択します。
リソースベースポリシーのアクセス許可が正しく設定されていれば、ログストリームが出力されています。
設定は以上となりますが、今回ハマった箇所についてもお伝えします。
ハマりどころ
LogDestinationConfigsの指定方法
AWS WAFのログの出力先設定LoggingConfiguration
では、Web ACLに関連付けたいログ設定としてLogDestinationConfigs
を設定しています。
当初は下記のように設定していました。
WAFLogConfig:
Type: AWS::WAFv2::LoggingConfiguration
Properties:
LogDestinationConfigs:
- !GetAtt WAFLogGroup.Arn
ResourceArn: !GetAtt WebACL.Arn
この状態でテンプレートをを実行すると、LogGroupが*
となり、関連付けできませんでした。
*
を選択すると、下記のページにとびます。
エラーメッセージ(訳)
どうやらLogDestinationConfigs:
で直接LogGroupのARNを指定すると末尾に:*
が付いてしまうことが原因でした。
というのもLogGroupのARNはarn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:aws-waf-logs-log-group:*
となっています。
そのため、LogGroupのARNは下記のように文字列結合する必要があります。
WAFLogConfig:
Type: AWS::WAFv2::LoggingConfiguration
Properties:
LogDestinationConfigs:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:aws-waf-logs-${PJPrefix}-${Env}-alb-acl
ResourceArn: !GetAtt WebACL.Arn
リソースベースポリシーの設定漏れ
AWS WAFのログをCloudWatachLogsに出力するためには、リソースベースポリシーが必要となります。
リソースベースポリシーとは、その名の通り操作対象のリソースに対して設定するポリシーです。
当初はこのポリシーを設定していなかったため、設定したCloudWatachLogsにログストリームが出力されませんでした。
そのため、リソースポリシーの設定を追加しました。
なおPolicyDocument
はStringで記述する必要があるため、書き方に注意してください。
WAFToCWLogsPolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: WAFToCWLogsPolicy
PolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "${CloudWatchLogsLogGroupArn}",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ${AWS::AccountId}
},
"ArnLike": {
"aws:SourceArn": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
}
}
}
]
}
- CloudWatchLogsLogGroupArn: !GetAtt WAFLogGroup.Arn
なおCloudFormationでリソースベースポリシーが記述できるようになったのは、2021年7月ごろからのようです。
注意点として、CloudFormationで作成できるリソースベースポリシーはAWSアカウントの各リージョンにつき10個までという制限があります。
下記CloudFormationガイドより引用
AWS::Logs::ResourcePolicy - AWS CloudFormation
An account can have up to 10 resource policies per AWS Region.(アカウントには、AWS リージョンごとに最大 10 個のリソース ポリシーを含めることができます。)
そのため、リソースベースポリシーを量産する場合はResource
でワイルドカード(*)を利用したり、List形式で列挙するなど工夫が必要となります。
なお設定したリソースベースポリシーは下記のコマンドで確認できます。
aws logs describe-resource-policies --region ap-northeast-1
最後に
今回はAWS WAFのログをCloudWatchLogsに出力するCloudFormationで構築してみました。
手動で設定すると上手くいくCloudWatchLogとの関連付けや自動で作成されるリソースベースポリシーなどにハマりましたが、なんとか構築することができました。
同じようにハマってしまった方の参考になれば嬉しいです。
最後までお読みいただきありがとうございました!
以上、おつまみ(@AWS11077)でした!
参考
[アップデート] AWS WAFのログを直接CloudWatch LogsおよびS3に出力可能になりました | DevelopersIO