CloudFormationでAWS WAFを構築してみた(2022年1月版)
こんにちは、AWS事業本部コンサルティング部の芦沢です。
みなさん、AWS WAF使っていますか?
AWS WAFといえば、WAFからS3へ直接出力することが可能になった2021年11月のアップデートが記憶に新しいですね。
以前はWAFのログをS3へ出力するためにKinesis Data Firehoseを経由する必要がありましたが、WAFからS3へ直接ログ出力が可能になり、とても便利になりました。
アップデート以前のWAF構築方法として、以下の記事を見ていただくとどれだけ簡単になったのかを理解しやすいと思いますので合わせて確認いただければ、と思います。
今回は、CloudFormationを利用してAWS WAF環境を自動構築してみたいと思います。
構成図
本記事で構築するAWS環境は以下の構成です。
背景がピンク色の範囲が本記事で構築するリソース
です。
- AWS WAF(WebAcl)
- S3バケット
- WAFアクセスログ出力用
- Athenaクエリ保存用
背景が水色の範囲は、既存環境のリソース
です。
- ALB
- EC2(Webサーバ)
右上のリソースは、本記事では構築しませんが保存したログを分析する場合に必要なリソース
です。
- Amazon Athena
CloudFormationテンプレートの紹介
(2022/7/25追記)CloudFormationテンプレートを更新しました。
■追記箇所
・ParametersのScopeをAllowedValuesに変更
・WebACLのルールにExcludedRulesを追加
・OverrideActionをNoneに変更
実行するテンプレートはこちらです。
AWSTemplateFormatVersion: '2010-09-09' Parameters: Prefix: Type: String Default: test Description: "Fill in the name of the system name." Env: Type: String Default: develop Description: "Fill in the name of the environment." Scope: Type: String Default: REGIONAL AllowedValues: - REGIONAL - CLOUDFRONT Description: "Select in the scope of waf(REGIONAL or CLOUDFRONT)" 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. Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3BucketForWaflog: Type: AWS::S3::Bucket Properties: BucketName: !Sub aws-waf-logs-${Env}-${Prefix}-${AWS::AccountId} AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True S3BucketForAthenaQuery: Type: AWS::S3::Bucket Properties: BucketName: !Sub athena-query-results-${Env}-${Prefix}-${AWS::AccountId} AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True # ------------------------------------------------------------# # WAF v2 # ------------------------------------------------------------# WebAcl: Type: AWS::WAFv2::WebACL Properties: Name: !Sub ${Env}-${Prefix}-web-acl Scope: !Ref Scope DefaultAction: Allow: {} VisibilityConfig: CloudWatchMetricsEnabled: true SampledRequestsEnabled: true MetricName: !Sub ${Env}-${Prefix}-web-acl Rules: - Name: AWS-AWSManagedRulesCommonRuleSet Priority: 1 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet ExcludedRules: - Name: NoUserAgent_HEADER - Name: UserAgent_BadBots_HEADER - Name: SizeRestrictions_QUERYSTRING - Name: SizeRestrictions_Cookie_HEADER - Name: SizeRestrictions_BODY - Name: SizeRestrictions_URIPATH - Name: EC2MetaDataSSRF_BODY - Name: EC2MetaDataSSRF_COOKIE - Name: EC2MetaDataSSRF_URIPATH - Name: EC2MetaDataSSRF_QUERYARGUMENTS - Name: GenericLFI_QUERYARGUMENTS - Name: GenericLFI_URIPATH - Name: GenericLFI_BODY - Name: RestrictedExtensions_URIPATH - Name: RestrictedExtensions_QUERYARGUMENTS - Name: GenericRFI_QUERYARGUMENTS - Name: GenericRFI_BODY - Name: GenericRFI_URIPATH - Name: CrossSiteScripting_COOKIE - Name: CrossSiteScripting_QUERYARGUMENTS - Name: CrossSiteScripting_BODY - Name: CrossSiteScripting_URIPATH OverrideAction: None: {} VisibilityConfig: CloudWatchMetricsEnabled: true SampledRequestsEnabled: true MetricName: AWS-AWSManagedRulesCommonRuleSet WAFLogConfig: Type: AWS::WAFv2::LoggingConfiguration Properties: LogDestinationConfigs: - !GetAtt S3BucketForWaflog.Arn ResourceArn: !GetAtt WebAcl.Arn WebACLAssociation: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !Ref WebAclAssociationResourceArn WebACLArn: !GetAtt WebAcl.Arn
テンプレート内の定義について簡単に説明します。
Parameters
CloudFormationスタック作成時に入力できるパラメータを定義しています。
詳細については、後述のパラメータ入力画面で説明しています。
## テンプレートから抜粋 Parameters: Prefix: Type: String Default: test Description: "Fill in the name of the system name." Env: Type: String Default: develop Description: "Fill in the name of the environment." Scope: Type: String Default: REGIONAL AllowedValues: - REGIONAL - CLOUDFRONT Description: "Select in the scope of waf(REGIONAL or CLOUDFRONT)" 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.
Resource
作成するリソースを定義します。 今回作成するリソースは以下です。
- S3
- WAFログ保存用バケット
- Athenaクエリ実行結果保存用バケット
- AWS WAF
- Web Acl
- Web Aclに設定するルール
- Web Aclに関連付けするリソースの設定
- WAFログ出力先のS3バケットの設定
S3
S3バケットは2種類作成します。
WAFのアクセスログを出力するためのバケットと、Athenaを使用してWAFログの分析した際のクエリログを出力するためのバケットです。
## テンプレートから抜粋 # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3BucketForWaflog: Type: AWS::S3::Bucket Properties: BucketName: !Sub aws-waf-logs-${Env}-${Prefix}-${AWS::AccountId} AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True S3BucketForAthenaQuery: Type: AWS::S3::Bucket Properties: BucketName: !Sub athena-query-results-${Env}-${Prefix}-${AWS::AccountId} AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True
どちらも外部に公開する必要のないバケットのため、パブリックアクセスは全てブロックしています。
WAFからのログ出力設定は後述のWAF設定の箇所で行います。
WAF
WAFリソースの作成および設定を行なっています。
WebAcl
WAFはWeb Aclという単位でリソースの作成、設定を行います。
Nameでリソース名を入力、DefaultActionで設定したルールに一致しなかった場合のリクエストの振る舞いを定義します。
CloudWatchMetricsEnabledを"Enabled"に設定することで、WAFをCloudWatchメトリクスの監視対象にできます。
SampledRequestsEnabledを"Enabled"に設定することで、AWSコンソールから直近3時間のリクエスト履歴が確認します。
## テンプレートから抜粋 # ------------------------------------------------------------# # WAF v2 # ------------------------------------------------------------# WebAcl: Type: AWS::WAFv2::WebACL Properties: Name: !Sub ${Env}-${Prefix}-web-acl Scope: !Ref Scope DefaultAction: Allow: {} VisibilityConfig: CloudWatchMetricsEnabled: true SampledRequestsEnabled: true MetricName: !Sub ${Env}-${Prefix}-web-acl
Rule
CloudFormationテンプレレート内でルールの設定も可能です。
今回の例では、AWS提供のマネージドルールAWSManagedRulesCommonRuleSetを使用したAWS-AWSManagedRulesCommonRuleSet
をルールとして作成しています。
追加でルールが必要な場合は追記してください。
ExcludedRules以下の記述で「AWSManagedRulesCommonRuleSet」のサブルールすべてをCountモードに設定しています。
マネジメントコンソールだと以下のように、ルールの設定変更画面でRule ActionをCountに切り替えるイメージです。
## テンプレートから抜粋 Rules: - Name: AWS-AWSManagedRulesCommonRuleSet Priority: 1 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet ExcludedRules: - Name: NoUserAgent_HEADER - Name: UserAgent_BadBots_HEADER - Name: SizeRestrictions_QUERYSTRING - Name: SizeRestrictions_Cookie_HEADER - Name: SizeRestrictions_BODY - Name: SizeRestrictions_URIPATH - Name: EC2MetaDataSSRF_BODY - Name: EC2MetaDataSSRF_COOKIE - Name: EC2MetaDataSSRF_URIPATH - Name: EC2MetaDataSSRF_QUERYARGUMENTS - Name: GenericLFI_QUERYARGUMENTS - Name: GenericLFI_URIPATH - Name: GenericLFI_BODY - Name: RestrictedExtensions_URIPATH - Name: RestrictedExtensions_QUERYARGUMENTS - Name: GenericRFI_QUERYARGUMENTS - Name: GenericRFI_BODY - Name: GenericRFI_URIPATH - Name: CrossSiteScripting_COOKIE - Name: CrossSiteScripting_QUERYARGUMENTS - Name: CrossSiteScripting_BODY - Name: CrossSiteScripting_URIPATH OverrideAction: None: {} VisibilityConfig: CloudWatchMetricsEnabled: true SampledRequestsEnabled: true MetricName: AWS-AWSManagedRulesCommonRuleSet
LoggingConfiguration & WebACLAssociation
ログの出力先設定(LoggingConfiguration)やWeb Aclへのリソース関連付け設定(WebACLAssociation)をここで行なっています。
LoggingConfigurationについては割と最近追加(2021年8月24日)されたCloudFormationのプロパティだそうです。1
手動で設定しなくても良いのは便利ですね。
WAFLogConfig: Type: AWS::WAFv2::LoggingConfiguration Properties: LogDestinationConfigs: - !GetAtt S3BucketForWaflog.Arn ResourceArn: !GetAtt WebAcl.Arn WebACLAssociation: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !Ref WebAclAssociationResourceArn WebACLArn: !GetAtt WebAcl.Arn
実際にやってみた
CloudFormationテンプレートの実行
まずはCloudFormationテンプレートを使ってリソースを作成しましょう。
スタックは東京(ap-northeast-1)リージョンで作成します。
パラメータには以下のように記載します。
値 | 説明 |
---|---|
スタックの名前 | 任意の名前 |
Env | AWSリソースの接頭語として付与できる文字列(環境名)を設定できます。 |
Prefix | AWSリソースの接頭語として付与できる文字列(プロジェクト名など)を設定できます。 |
Scope | WebAclに関連付けするAWSリソースの種類を指定します。 関連付けするリソースが、リージョナルリソース(ALBなど)の場合は、 REGIONAL と記載(デフォルトのまま)、Cloudfrontを適用したい場合は、CLOUDFRONT に書き換えてください。 |
WebAclAssociationResourceArn | WebAclに関連付けするAWSリソースのARNを指定します。 AWSリソースの例) ALB,Cloudfront,API Gateway,AppSync |
スタックを作成し、実行結果がCREATE_COMPLETEになることを見届けます。
実行後確認
テンプレートで定義したリソースが正常に作成・設定できているか確認します。
まずはWAFです。
WAF & Shieldコンソール → Web Aclへアクセスします。
今回は東京リージョンでALB用のWAFを作成したので、リージョン選択欄にAsia Pacific (Tokyo)を指定します。
※WAF適用の対象リソースがCloudFrontの場合はGlobal (CloudFront)を指定します。
作成したWeb Aclを選択し、Rules、Associated AWS resources、Logging and metricsを確認しましょう。
S3も確認します。
S3のコンソールから2つのバケット(WAFログ用バケットおよびAthenaクエリ実行用バケット)が作成できていることが確認できます。
WAFログ用バケットの中身を確認すると、以下フォルダ内にWAFログが保存されていることが確認できました
{{S3バケット名}}}}/AWSLogs/{{AWSアカウントID}}/WAFLogs/{{リージョン名}}/{{WebAcl名}}/YYYY/MM/DD/HH(UTC)/MM/ ## HHは、UTCになっているので注意(日本時間であれば+9時間する必要があります)
検知テスト実施
設定したAWSマネージドルールに検知するリクエストをテストで送ってみたいと思います。
検知テストで使用する擬似攻撃リクエストのサンプルは以下のブログが参考になりました。
今回はCore rule setを使用しているので以下のテストリクエストをWAFと関連付けしたALBにリクエストを送信します。
curl -H 'User-Agent: ' http://[test_domain]
実行後、Overview欄でSampled requests欄にAWS#AWSManagedRulesCommonRuleSet#NoUserAgent_HEADER
のルールでCount検知できていることがわかります。
S3バケットにも該当のログが保存されていました。
(ログの中身を見たところ、同時くらいの時刻に保存された別のログも含まれていたことも関係あるのか?ログファイルのの更新時刻は検知した時間から少しずれていました)
なおマネジメントコンソールのOverviewからでは3時間以内のSampled requestsしか確認できません。
以前のログを確認する場合はAthenaを使います。 AthenaでS3に保存したログを分析する方法は以下のブログが参考になりました
以上で完了です、お疲れ様でした。
まとめ
CloudFormationを使ってAWS WAF構築/各種設定(WAFルール設定、関連リソース設定、ログ出力設定)を行いました、
直接S3にログ出力ができなかった以前と比べるとかなりテンプレートが簡単になっており、かつ管理するリソースも減っているのでシンプルな構成でWAFのログ出力設定ができるようになったと実感できました。
さいごに
皆さんもこれでWAF構築が簡単にできますね。
WAFWAFしていきましょう。
以上、AWS事業本部コンサルティング部の芦沢がお送りしました。
- AWSドキュメントGitHubのHistory から確認しました ↩