search
LoginSignup
1

posted at

Route 53 + CloudFront + S3 + WAFの構成をCloudFormationで作る

やりたいこと

  • CloudFront + S3でSPAサイトを公開したい
  • そしてRoute53で独自ドメインでアクセスできるようにする
  • かつCloudFrontにWAFを設定してアクセス制限したい

CloudFormationで一気に作ろうとしたらWAFを作るところで怒られてしまった...

調べてみるとWebACLのScopeをCLOUDFRONTで作成する場合はリージョンをus-east-1で作成する必要がありました

参考↓

ドキュメントにも書いてありました、なるほど

For CLOUDFRONT, you must create your WAFv2 resources in the US East (N. Virginia) Region, us-east-1

解決

ということでus-east-1で作る必要があるものは別で作成してから、
CloudFrontとかS3などは後から別のテンプレートで作成します
(今回は東京リージョンに作りたかったのでそこに)

全体の流れとしては下記のような感じにしました

  1. us-east-1で作成が必要なものを作る
    1. SSL証明書
    2. Route53 ホストゾーン
    3. WAF
  2. ap-northeast-1で他のものを作る
    1. S3
    2. CloudFront
    3. Route53のレコード

us-east-1で作成するもの

まずはus-east-1で作成が必要なものから先に作成します
独自ドメインは取得済みである前提です

WAFは特定のIPからだけアクセスできるようなルールにしています
また、WebACLはブロックした時に404を返すようにしています
(デフォルトは403ですが、403の場合はCloudFrontでルートパスにリダイレクトしたいので404を返すようにしてます)

CloudFormationテンプレートは下記のような感じです

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  DomainName:
    Description: Domain Name
    Type: String

Resources:
  # -----
  # ACM
  # -----
  ACM:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref DomainName
      ValidationMethod: DNS
    DeletionPolicy: Delete

  # -----
  # Route53 Hosted Zone
  # -----
  Route53HostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      Name: !Ref DomainName
    DeletionPolicy: Delete

  # -----
  # WAF
  # -----
  ##### IP Set
  WAFIPSet:
    Type: AWS::WAFv2::IPSet
    Properties:
      NAME: MyIPSet
      Addresses:
        - 2400:2400:2400:2400:2400:2400:2400:2400/128
      IPAddressVersion: IPV6
      Scope: CLOUDFRONT
    DeletionPolicy: Delete

  ##### WAF Web ACL
  WAFWebAcL:
    Type: AWS::WAFv2::WebACL
    Properties:
      DefaultAction:
        Block:
          CustomResponse:
            ResponseCode: 404
      Name: MyWebACL
      Rules:
        - Name: MyRule
          Action:
            Allow: {}
          Priority: 0
          Statement:
            IPSetReferenceStatement:
              Arn: !GetAtt WAFIPSet.Arn
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            MetricName: MyRuleMetricName
            SampledRequestsEnabled: true
      Scope: CLOUDFRONT
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: MyWebACLMetricName
        SampledRequestsEnabled: true
    DeletionPolicy: Delete

これでスタックを作成します(us-east-1で作成することを忘れずに)

ACMのSSL証明書はDNS検証が必要になります
作成したホストゾーンにDNSレコードを追加して検証が完了したらOKです

残りのもの

あとは東京リージョンで作成するものを作ります
CloudFront + S3の設定はOAIを使用して、CloudFrontを経由せずにS3へ直接アクセスすることを制限します
それと独自ドメインをCloudFrontに向かわせるためのRoute53レコードも作ります

パラメータは下記のように上で作成したリソースを指定するようにします

  • CertificateArn
    • 上で作成したSSL証明書のARN
  • DomainName
    • ドメイン
  • WebACLArn
    • 作成したWebACLのARN
  • Route53HostedZone
    • 作成したホストゾーンのID

テンプレートはこんな感じ

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  CertificateArn:
    Description: Certificate Arn
    Type: String
  DomainName:
    Description: Domain Name
    Type: String
  WebACLArn:
    Description: Web ACL Arn for CloudFront
    Type: String
  Route53HostedZone:
    Description: Route53 Hosted Zone Id
    Type: String

Resources:
  # -----
  # S3
  # -----
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: shiotomo-vite-app-bucket
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
    DeletionPolicy: Delete

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub arn:aws:s3:::${S3Bucket}/*
            Principal:
              AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}
    DeletionPolicy: Delete

  # -----
  # CloudFront
  # -----
  CloudFront:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Comment: My Distribution
        Enabled: true
        Origins:
          - Id: S3Bucket
            DomainName: !GetAtt S3Bucket.DomainName
            S3OriginConfig:
              OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          MinimumProtocolVersion: TLSv1.2_2021
          SslSupportMethod: sni-only
        Aliases:
          - !Ref DomainName
        CustomErrorResponses:
          -
            ErrorCode: 403
            ResponsePagePath: /
            ResponseCode: 200
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          TargetOriginId: S3Bucket
          Compress: true
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject: index.html
        IPV6Enabled: true
        WebACLId: !Ref WebACLArn
    DeletionPolicy: Delete

  CloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: MyOAI
    DeletionPolicy: Delete

  # -----
  # Route53 Records
  # -----
  Route53ARecord:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: !Ref DomainName
      Type: A
      AliasTarget:
        DNSName: !GetAtt CloudFront.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref Route53HostedZone
    DeletionPolicy: Delete

  Route53AAAARecord:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: !Ref DomainName
      Type: AAAA
      AliasTarget:
        DNSName: !GetAtt CloudFront.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref Route53HostedZone
    DeletionPolicy: Delete

これでスタック作成して問題なければ
あとはS3にindex.htmlをアップするだけです

独自ドメインにアクセスして表示されれば完了!

参考

CloudFrontのOAIについて↓

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1