この記事の目的は?
CloudFront + AWS WAFは非常に汎用性のある構成であるにも関わらずCloudFormation自動作成の記事が少なかったので、実装方法を記載します
どんなものを実装できるの?
特定のIPのみアクセス可能なCloudFrontをyamlから作成できます
構成
注意することその1
CloudFront+WAFの構成を作る上で気をつけなければいけないのが作成順序です
CloudFrontを作ってからWAFを作って、関連付けるのではなく
WAFを作ってからCloudFrontを作って、関連付けるのが正しいです
理由:CloudFrontディストリビューションはARNでWAFを参照できますが、WAFはCloudFrontを参照する機能が無いからです
注意することその2
WAFをCloudFormationで作成する場合、CloudFormationを実行するリージョンは米国東部 (バージニア北部)us-east-1で実行して下さい
その他リージョンで実行しても失敗してしまいます
参考元:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webacl.html
For
CLOUDFRONT
, you must create your WAFv2 resources in the US East (N. Virginia) Region,us-east-1
.
作ってみた
実現したいことはこの3つ
- アクセスを許可させたいIP群のリストを作成する
- そのリストにあるIPのみアクセス可能なWAFを作成する
- 作成したWAFをCloudFront用コードから参照して関連付けられるよう、WAFのARNの値を出力できるようにしておく
さっそく、CloudFormationのコードをyamlで作成します
CloudFormationをyaml言語で作成
AWSTemplateFormatVersion: '2010-09-09'
Description: WAF by Cloud Front
Parameters:
Resources:
WebACL: # WAF本体はこの WebACL で作成します
Type: AWS::WAFv2::WebACL
Properties:
Name: WebACL_test
Scope: CLOUDFRONT # CloudFront用のWAFなのでScopeは CLOUDFRONT にしましょう
DefaultAction:
Block: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: webaclcloudfront
Rules: # WAFのアクセス制御の具体的な方法はこの Rules で設定します
-
Name: rules-proxy-test
Priority: 0
Action:
Allow: {}
Statement:
IPSetReferenceStatement:
Arn: !GetAtt WAFIPSet.Arn # ここでは後述の WAFIPSet で作成したIP情報のARNを参照するようにしています
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: rules-proxy-test
WAFIPSet: # アクセスを許可したいIPはこの WAFIPSet で設定します
Type: AWS::WAFv2::IPSet
Properties:
Name: wafipset-test
Scope: CLOUDFRONT # WebACLと同じくここもCLOUDFRONTと指定します
IPAddressVersion: IPV4
Addresses: # 許可させたいIPをこの Addresses に記載します
- xxx.xxx.xxx.xxx/32
- yyy.yyy.yyy.yyy/24
- zzz.zzz.zzz.zzz/28
Outputs:
CloudFrontWAF:
Value: !GetAtt WebACL.Arn # WAFのARNを CloudFrontWAF という論理名で出力して、CloudFrontから参照できるようにします
CloudFormationで読み込んだらリソースが作成されました
1および2を実現できました
- アクセスさせたいIPのリストを作成する (WAFIPSet)
- そのIPリストを元にIPでアクセス制御できるWAFを作る**(WebACL)**
3を実現できました
- 作成したWAFをCloudFront用コードから参照して関連付けられるよう、WAFのARNの値を出力できるようにしておく**(CloudFrontWAF)**
実装までにつまずいたところ
最初はStatementの設定を以下のようにしていました
Statement:
IPSetReferenceStatement:
!Ref WAFIPSet
CloudFormationにインポートしたら、以下のようなモデル検証エラーが発生
原文
Resource handler returned message: "Model validation failed (
#/Rules/0/Priority: expected type: Number, found: String
#/Rules/0/Statement/IPSetReferenceStatement: expected type: JSONObject, found: String
#/Rules/0/VisibilityConfig/SampledRequestsEnabled: expected type: Boolean, found: String
#/Rules/0/VisibilityConfig/CloudWatchMetricsEnabled: expected type: Boolean, found: String
#/VisibilityConfig/SampledRequestsEnabled: expected type: Boolean, found: String
#/VisibilityConfig/CloudWatchMetricsEnabled: expected type: Boolean, found: String)" (RequestToken: f1cbb0f3-5861-59ab-ffbc-36c56447b5b0, HandlerErrorCode: InvalidRequest)
和訳
リソースハンドラーが返すメッセージ: "モデルの検証に失敗しました(
#/ Rules / 0 /優先度:期待されるタイプ:数値、検出されました:文字列
#/ Rules / 0 / Statement / IPSetReferenceStatement:期待されるタイプ:JSONObject、検出されました:文字列
#/ Rules / 0 / VisibilityConfig / SampledRequestsEnabled:期待されるタイプ:ブール値、検出:文字列
#/ Rules / 0 / VisibilityConfig / CloudWatchMetricsEnabled:期待されるタイプ:ブール値、検出:文字列
#/ VisibilityConfig / SampledRequestsEnabled:期待されるタイプ:ブール値、検出:文字列
#/ VisibilityConfig / CloudWatchMetricsEnabled:予期されるタイプ:ブール値、検出:文字列)
エラーの原因は戻り値の指定の仕方
ちゃんとAWS公式リファレンスに書かれていました
参考元:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webacl.html
参照
Refリソース名、物理ID、および範囲を含むリソースのため、次のようにフォーマットされました:name|id|scope
例:my-webacl-name|xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx|REGIONAL
Fn :: GetAtt
Arn
Web ACLのAmazonリソース名(ARN)
戻りの値は**!Refではなく!GetAtt**で指定する必要があるようです
コードを以下のように書き直して再度CloudFormationで実行したところ、成功しました!
Statement:
IPSetReferenceStatement:
Arn: !GetAtt WAFIPSet.Arn
終わりに
CloudFront+WAFというありふれた構成であるにも関わらず、その構成をCloudFormationで実現する情報がウェブにはほぼ皆無だったため、非常に難儀しました
なので今後似たような状況で苦しんでいる方々のために、なるべく最小構成でコードを作っております
今回の調査のため色々なblogを巡回しましたが、答えは最初からAWS公式リファレンスにすべて書かれており、改めてAWSドキュメントの完成度の高さに感動しました
補足
参考になったページ