Help us understand the problem. What is going on with this article?

CloudFormationからのWAFの設定(CloudFront/APIGateway)

はじめに

AWSでサーバレスなアーキテクチャを採用していて、 各エンドポイント(CloudFront や API Gateway)にIP制限をかけたい場合 AWS WAF を用いることが多いかと思います。
この記事では、CloudFormation から各リソースと WAF のリソースを生成する際にハマりポイントが多かったのでまとめてみました。

AWS::WAF と AWS::WAFRegional

まずハマりやすいのが、この2つのリソースタイプがあること。
設定できるリソースやパラメータにあまり違いはないのですが、使い分けとしては、リージョンを持つサービスに紐づけるか、リージョンを持たないサービス(CloudFront)に紐づけるか、でどちらを選択するか決まります。

  • AWS::WAFRegional → API Gateway, ALB, etc...
  • AWS::WAF → CloudFront

と考えておけばOKだと思います。(これを逆にすると権限のエラーで失敗します。)

AWS WAF と AWS WAF Classic

この2つの違いも初見の人は注意が必要です。
AWS WAF は2019年11月にアップデートされていて、コンソールからサービスを利用する場合、はじめに新バージョンのサービスが表示されます。
旧バージョンの AWS WAF を利用する場合、左部メニューにこっそりある Switch to AWS WAF Classic をクリックすると切り替わります。
image.png

ここでハマりどころなのが、 CloudFormation のリソースタイプの AWS::WAFAWS::WAFRegional は、どちらも旧バージョン(AWS WAF Classic)のリソースを作成するものであるということです。
AWSサポートに確認したわけではないので確かではありませんが、現時点(2019年12月時点)では、CloudFormation から新バージョンの AWS WAF のリソースを生成することはできなそうです。(今後対応されそう)

template ファイル

今回、実際に CloudFront と API Gateway それぞれに紐づいた AWS WAF (Classic) を CloudFormation から生成しました。
その際、template の記述に関しても注意すべき点があったので、 コメントを追記した template.yaml を載せておきます。

共通

IP制限を行う際に、 WebACL + Rule + IPSet のリソースをセットで使用するのは、 AWS::WAFAWS::WAFRegional も同様。

AWS::CloudFront + AWS::WAF パターン

AWS::WAF の場合は、 CloudFront のリソース側から紐付けを行います。

template.yaml(CloudFront)
Parameters:
  AppName:
    Type: String
  AllowedIP:
    Type: String

Resources:
  MyDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      # 〜その他のプロパティは省略〜
      DistributionConfig:
        WebACLId: !Ref MyWebACL #紐付けはここで行う

  # AWS::WAF の場合、リソースの紐付けに WebACLAssociation は使用しない

  MyWebACL:
    Type: AWS::WAF::WebACL
    Properties:
      Name: !Sub ${AppName}WebACL #ハイフンやアンスコは使用不可
      DefaultAction:
        Type: BLOCK
      MetricName: !Sub ${AppName}WebACL
      Rules:
        -
          Action:
            Type: ALLOW
          Priority: 1
          RuleId: !Ref MyRule

  MyRule:
    Type: AWS::WAF::Rule
    Properties:
      Name: !Sub ${AppName}AllowedRule #ハイフンやアンスコは使用不可
      MetricName: !Sub ${AppName}AllowedRule
      Predicates:
        -
          DataId: !Ref MyIPSet
          Negated: false
          Type: IPMatch

  MyIPSet:
    Type: AWS::WAF::IPSet
    Properties:
      Name: !Sub ${AppName}AllowedIPSet #ハイフンやアンスコは使用不可
      IPSetDescriptors:
        -
          Type: IPV4
          Value: !Sub ${AllowedIP}/32 #IPは末尾に「/32」を付与

AWS::Serverless::Api + AWS::WAFRegional パターン

AWS::WAFRegional の場合、 WebACLAssociation というリソースを利用して紐付けを行います。
公式ドキュメントでは、ロードバランサーとの紐付けの例しかありませんが、API Gateway と紐付けを行う場合は以下のように記述します。

template.yaml(+APIGateway)
Parameters:
  AppName:
    Type: String
  Stage:
    Type: String
  AllowedIP:
    Type: String

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      # 〜その他のプロパティは省略〜
      StageName: !Ref Stage

  MyWebACLAssociation: #紐付けはこのリソースで行う
    Type: "AWS::WAFRegional::WebACLAssociation"
    Properties:
      ResourceArn: !Sub arn:aws:apigateway:${AWS::Region}::/restapis/${MyApi}/stages/${Stage}
      # ResourceArn は !Ref MyApi ではないので注意
      WebACLId: !Ref MyWebACL

  MyWebACL:
    Type: AWS::WAFRegional::WebACL
    Properties:
      Name: !Sub ${AppName}APIWebACL #ハイフンやアンスコは使用不可
      DefaultAction:
        Type: BLOCK
      MetricName: !Sub ${AppName}APIWebACL
      Rules:
        -
          Action:
            Type: ALLOW
          Priority: 1
          RuleId: !Ref MyRule

  MyRule:
    Type: AWS::WAFRegional::Rule
    Properties:
      Name: !Sub ${AppName}APIAllowedRule #ハイフンやアンスコは使用不可
      MetricName: !Sub ${AppName}APIAllowedRule
      Predicates:
        -
          DataId: !Ref MyIPSet
          Negated: false
          Type: IPMatch

  MyIPSet:
    Type: AWS::WAFRegional::IPSet
    Properties:
      Name: !Sub ${AppName}APIAllowedIPSet #ハイフンやアンスコは使用不可
      IPSetDescriptors:
        -
          Type: IPV4
          Value: !Sub ${AllowedIP}/32 #IPは末尾に「/32」を付与

最後に

今回は、サーバレスアーキテクチャでの CloudFormation を用いた WAF の設定のハマりポイントを解説しました。
AWSの公式ドキュメントは本当にわかりづらいので、同じようなことでハマっている人は参考にしていただければ幸いです。

noyoikw
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした