どこにでもいる30代SEの学習ブログ

主にプログラミング関連の学習内容。読んだ本の感想や株式投資についても書いてます。

【AWS】Route53でドメインを登録してHTTPS対応のnginxサーバーをCloudFrontとCloudFormationで構築するまで

f:id:predora005:20210101231443p:plain

AWSHTTPS対応のnginxサーバーを構築するまでの手順をまとめました。

HTTPS対応のサーバーを構築する手順は多々あります。今回は「CertificateManager」でサーバ証明書を発行し「CloudFront」でHTTPS対応する構成にしました。

Route53で独自ドメインの登録を行いましたが、別のプロバイダで登録することも可能です。

f:id:predora005:20210101145635p:plain

[0] 前提条件

nginxインストールなどの操作を行うために、EC2へSSHでログインします。SSHでログインするためのキーペアは作成済みとします。

[前提条件]

  • キーペアは作成済み

[1] Route53でドメイン登録

マネージメントコンソールから、Route53で独自ドメインを登録します。

f:id:predora005:20210101150311p:plain

[1-1] ドメインの選択

ドメインの選択欄にドメイン名を入力し[チェック]を押すと、利用可能か否かと料金が表示されます。

f:id:predora005:20210101150625p:plain

[カートに入れる]を押すとカートに料金と登録期間が表示されます。登録期間を過ぎても自動延長できます。長期間使用する予定がなければ1年のままで問題ありません。画面下部の[続行]を押して次に進みます。

f:id:predora005:20210101150838p:plain

[1-2] 連絡先の入力と購入

登録者の連絡先を登録します。電話番号以外は日本語表記で問題ありません。

f:id:predora005:20210101151035p:plain

プライバシーの保護は、個人の場合は[有効化]にしておきます。

f:id:predora005:20210101151234p:plain

[続行]を押すと、次のように表示されますが問題ありませんでした。心配な場合は連絡先に登録したEメールを確認しましょう。AWSからメールが来ているはずです。

f:id:predora005:20210101151325p:plain

ドメインを登録すると自動的にホストゾーンが作成されます。ホストゾーンは12時間以上使用すると、0.5$/月かかります。しばらく使用しない場合は後で手動で削除しましょう。

ドメインの自動更新は個々の目的に応じて選びます。

f:id:predora005:20210101151440p:plain

連絡先に登録したEメールを確認し、メール内のリンクをクリックします。

f:id:predora005:20210101151806p:plain

リンクをクリックした後、[ステータスの更新]を押します。すると、ステータスが検証済みになります。

f:id:predora005:20210101151901p:plain

以上でドメインの登録は完了です。ドメイン登録が完了して使えるようになるまでには多少時間がかかります。私の場合は30分かかりました。

f:id:predora005:20210101154338p:plain

ホストゾーンが自動作成されたかを確認すると、作成されていました。

f:id:predora005:20210101152005p:plain

[2] Certificate Managerで証明書発行

続いて、Certificate Managerでサーバ証明書を発行します。証明書の発行は無料です。

なお、リージョンは「米国東部(バージニア北部)」にします。別のリージョンで発行するとCloudFrontと証明書の関連付けが出来ません。

CloudFront で SSL/TLS 証明書を使用するための要件 - Amazon CloudFront

CloudFront ディストリビューションで使用するために SSL 証明書を米国東部 (バージニア北部) リージョンに移行する

[2-1] 証明書発行

f:id:predora005:20210101152203p:plain

[パブリック証明書のリクエスト]を選択します。

f:id:predora005:20210101152249p:plain

ドメイン名を入力します。登録したドメインexample.comであれば、example.com*.example.comを登録します。ワイルドカードを使用してサブドメインも登録します。

f:id:predora005:20210101152555p:plain

検証方法は[DNSの検証]にします。

f:id:predora005:20210101152627p:plain

証明書を多数発行するなど特別な理由がなければ、タグは空欄のままで問題ありません。

f:id:predora005:20210101152846p:plain

内容を確認し[確認とリクエスト]を押します。

f:id:predora005:20210101152951p:plain

[2-2] 検証

「検証保留中」になっています。これを「発行済み」にしていきます。ドメイン名横のドロップダウンを選択します。

f:id:predora005:20210101153356p:plain

[Route53でのレコードの作成]を押下します。

f:id:predora005:20210101153945p:plain

[作成]を押します。

f:id:predora005:20210101154036p:plain

もう一方も同様にDNSレコードを作成します。

f:id:predora005:20210101154109p:plain

以上で証明書発行の手続きは完了です。「検証保留中」になっていますが、数分経つと「発行済み」に変わります。

f:id:predora005:20210101154816p:plain

[3] EC2インスタンス構築

EC2インスタンス構築に当たり、次のものを作成する必要があります。

  • VPC
  • サブネット
  • ルートテーブル
  • インターネットゲートウェイ
  • セキュリティグループ
  • EC2

これらを毎回手動で構築するのは面倒です。なので、CloudFormationで自動化します。

[3-1] CloudFormationのテンプレート

[3-1-1] テンプレートバージョン

テンプレートバージョンは固定です。Descriptionは任意です。

AWSTemplateFormatVersion: 2010-09-09
Description: >-
  AWS CloudFormation template for the nginx server.

[3-1-2] パラメータ

次の内容はユーザーが決められるようにします。Defaultを指定しておけば毎回入力する必要がなくて楽です。

  • SSHのキーペア (コンボボックスから選択できるようになる)
  • インスタンスタイプ
  • EBSの容量
  • CIDR
  • プライベートIP
Parameters:
  # SSHのキー名称
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: Can contain only ASCII characters.
  
  # インスタンスタイプをリストから選択
  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.nano
      - t3.micro
      - t3.small
      - t3.medium
    ConstraintDescription: must be a valid EC2 instance type.

  # EBSの容量
  VolumeSize:
    Description: 'Volume size of EBS'
    Type: Number
    Default: 8
    ConstraintDescription: expect size >= 8GB.

  # CIDR
  VpcCidr:
    Description: 'VPC CIDR'
    Type: String
    Default: 10.0.0.0/16
  SubnetCidr:
    Description: 'Subnet CIDR'
    Type: String
    Default: 10.0.0.0/24
  
  # プライベートIPアドレス
  PrivateIpAddress:
    Description: 'Private IP Address.'
    Type: String
    Default: '10.0.0.10'

[3-1-3] ネットワーク周り

VPC, サブネット, ルートテーブル, インターネットゲートウェイの設定です。特殊なのはVPCEnableDnsHostnamesEnableDnsSupportをtrueにすることです。デフォルトはfalseですが、CloudFrontの設定時に必要なためtrueにします。

Resources:
  # VPC
  Vpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCidr
      # DNSはCloudFrontのOrigin Domain Name指定のために必要
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-vpc'
  
  # サブネット
  Subnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Ref SubnetCidr
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-sb'
  
  # インターネットゲートウェイ
  IGW:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-igw'
  IGW2Vpc:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref Vpc
  
  # ルートテーブル
  RouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-rt'
  RT2Subnet:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref Subnet
      RouteTableId: !Ref RouteTable
  Route:
    Type: 'AWS::EC2::Route'
    Properties:
      GatewayId: !Ref IGW
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0

[3-1-4] セキュリティグループ

セキュリティグループは少々複雑です。4つセキュリティグループを作成するのには理由があります。

セキュリティグループ1は、EC2へのSSHアクセスと、EC2からnginxインストール等の作業を可能にするための設定です。これは普通の設定です。

セキュリティグループ2, 3, 4は、CloudFrontからのHTTPアクセスのみ受け付けるためです。CloudFrontは世界中にエッジサーバを持っており、どのサーバからアクセスされるか分かりません。そのため、全エッジサーバのIPアドレスをセキュリティグループに登録しておく必要があります。

セキュリティグループへの登録は、EC2構築後にシェルスクリプトで行います。CloudFormationでは中身が空のセキュリティグループを準備しておきます。3つ用意するのは、セキュリティグループのルール数に制限があるからです。デフォルトでは60ルールまでですが、CloudFrontのIPアドレスは100以上有るため、複数用意しておきます。

  # セキュリティグループ1
  SecurityGroup1:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: !Sub '${AWS::StackName}-ec2-sg1'
      GroupDescription: !Sub '${AWS::StackName}-ec2-sg1'
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-sg1'
      
      # インバウンド: SSH許可
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

      # アウトバウンド: HTTPとHTTPS許可
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

  # セキュリティグループ2 (for CloudFront)
  SecurityGroup2:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: !Sub '${AWS::StackName}-ec2-sg2'
      GroupDescription: !Sub '${AWS::StackName}-ec2-sg2'
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-sg2'
      
  # セキュリティグループ3 (for CloudFront)
  SecurityGroup3:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: !Sub '${AWS::StackName}-ec2-sg3'
      GroupDescription: !Sub '${AWS::StackName}-ec2-sg3'
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-sg3'
      
  # セキュリティグループ4 (for CloudFront)
  SecurityGroup4:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: !Sub '${AWS::StackName}-ec2-sg4'
      GroupDescription: !Sub '${AWS::StackName}-ec2-sg4'
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2-sg4'

Amazon VPC でセキュリティグループルールの制限を増やす

[3-1-5] IAMロール

IAMロールは通常不要です。わざわざ設定するのは、EC2からCloudFrontのIPアドレスをセキュリティグループに登録するためです。セキュリティグループの変更は、EC2構築後にEC2から行います。EC2にセキュリティグループ操作の権限を付与します。

  # IAMロール
  SecurityGroupAccessRole:
    Type: "AWS::IAM::Role"
    Properties: 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "ec2.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"

  # IAMポリシー
  SecurityGroupAccessPolicies:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: SecurityGroupAccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          # 自リージョン・自アカウントのセキュリティグループの変更を許可
          Action:
            - 'ec2:AuthorizeSecurityGroupEgress'
            - 'ec2:AuthorizeSecurityGroupIngress'
            - 'ec2:DeleteSecurityGroup'
            - 'ec2:RevokeSecurityGroupEgress'
            - 'ec2:RevokeSecurityGroupIngress'
          Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*'
          # 作成するVPCのセキュリティグループのみに限定する
          Condition:
            ArnEquals:
              'ec2:Vpc': !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${Vpc}'
        - Effect: Allow
          Action:
            - 'ec2:DescribeSecurityGroups'
            - 'ec2:DescribeSecurityGroupReferences'
            - 'ec2:DescribeStaleSecurityGroups'
            - 'ec2:DescribeVpcs'
          Resource: '*'
      Roles:
      - !Ref SecurityGroupAccessRole

  # IAMインスタンスプロファイル
  SecurityGroupAccessInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - !Ref SecurityGroupAccessRole

Amazon EC2: 特定の VPC に関連付けられた EC2 セキュリティグループの管理をプログラムによりコンソールで許可する - AWS Identity and Access Management

[3-1-6] EC2 (前半)

ここでは、リージョンは東京(ap-northeast-1)、OSはAmazon Linux2で構築する設定にしています。UserDataはEC2構築後にjq, curlを自動インストールし、シェルスクリプトを自動生成するための設定です。

  # インスタンス
  Instance:
    Type: 'AWS::EC2::Instance'
    
    # プロパティ
    Properties:
      # ap-northeast-1のAmazon Linux2
      ImageId: 'ami-01748a72bed07727c'
      InstanceType: !Ref InstanceType
      
      # ネットワーク周りの設定
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          GroupSet: 
            - Ref: SecurityGroup1
            - Ref: SecurityGroup2
            - Ref: SecurityGroup3
            - Ref: SecurityGroup4
          SubnetId: 
            Ref: Subnet
          PrivateIpAddress: !Ref PrivateIpAddress

      # SSHのキー名称
      KeyName: !Ref KeyName

      # EBSの設定
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs: 
            VolumeSize: !Ref VolumeSize
            VolumeType: gp2
            DeleteOnTermination: true
      
      # IAM
      IamInstanceProfile:
        !Ref SecurityGroupAccessInstanceProfile
      
      # タグ
      Tags: 
        - Key: Application
          Value: String
        - Key: Name
          Value: !Sub '${AWS::StackName}-inst'

      # ユーザーデータ
      UserData: !Base64
        Fn::Sub: |
          #!/bin/bash
          yum -y update
          yum -y install aws-cfn-bootstrap
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Instance --region ${AWS::Region}
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource Instance --region ${AWS::Region} 
  

[3-1-7] EC2 (後半)

EC2構築後にjq, curlを自動インストールし、シェルスクリプトを自動生成するための設定です。スクリプトの詳細は後述しますが下記記事を参考にさせていただいています。

EC2のセキュリティグループにCloudFrontからしかアクセスを許可しない設定を追加する(改良版) | 綺麗に死ぬITエンジニア

    # メタデータ
    Metadata:
      AWS::CloudFormation::Init:
        config:
          # jq, curlをインストール
          packages:
            yum:
              jq: []
              curl: []
          files: 
            # セキュリティグループ変更用のスクリプト
           '/home/ec2-user/set_security_group.sh':
              content:
                Fn::Sub: |
                  #!/bin/bash
                  IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json`
                  IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'`
                  
                  count=0
                  for IP in $IP_LIST; do
                    if [ $count -lt 60 ]; then
                      SG=${SecurityGroup2}
                    elif [ $count -lt 120 ]; then
                      SG=${SecurityGroup3}
                    else
                      SG=${SecurityGroup4}
                    fi
                    
                    aws ec2 authorize-security-group-ingress --region ${AWS::Region} --group-id $SG --protocol tcp --port 80 --cidr $IP
                    echo $IP
                    (( count++ ))
                  done
                  
              mode: '000644'
              owner: 'ec2-user'
              group: 'ec2-user'

            # nginxインストール用のスクリプト
            '/home/ec2-user/install_nginx.sh':
              content:
                Fn::Sub: |
                  #!/bin/bash
                  sudo amazon-linux-extras install nginx1 -y
                  sudo systemctl start nginx
                  sudo systemctl enable nginx

              mode: '000644'
              owner: 'ec2-user'
              group: 'ec2-user'

[3-1-8] Elastic IP

サーバーを再起動するたびにパブリックIPが変わると面倒なので、Elastic IPを設定しておきます。

  # Elastic IP
  ElasticIp:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref Instance
      Domain: vpc

[3-2] スタックの作成

テンプレートが準備できたらスタックを作成します。

f:id:predora005:20210101182916p:plain

テンプレートファイルはローカルからのアップロードを選択しました。S3にアップロードしたものを参照することも可能です。

f:id:predora005:20210101183214p:plain

スタックの名前は任意の名称を入力します。今回は'stack1'としました。

f:id:predora005:20210101183022p:plain

パラメータはキーペアの名称(KeyName)以外はデフォルトのままにしました。キーペアのみコンボボックスから作成済みのキーペアを選択します。

f:id:predora005:20210101183557p:plain

「スタックオプションの設定」は変更なしで次に進みます。

f:id:predora005:20210101183718p:plain

「レビュー」では末尾の機能のみ注意が必要です。今回作成したテンプレートではIAMロールをEC2に付与しています。IAMリソースを作成しても問題ないか問われるので、チェックボックスにチェックして[スタックの作成]を押下します。

f:id:predora005:20210101183925p:plain

数分待つとスタックの作成が完了します。

f:id:predora005:20210101184514p:plain

インスタンスの状態を確認すると、設定通りに作成されていることが分かります。

f:id:predora005:20210101185154p:plain

[4] nginxインストール

EC2にsshでログインして、install_nginx.shを実行します。

sh install_nginx.sh

スクリプトの中身は次の通りです。nginxはAmazon Linux 2のextrasレポジトリで配布されているものをインストールします。

#!/bin/bash
sudo amazon-linux-extras install nginx1 -y
sudo systemctl start nginx
sudo systemctl enable nginx

インストール後にバージョンを確認すると無事インストールされたことが分かります。

nginx -v
# nginx version: nginx/1.18.0

[5] セキュリティグループの設定

EC2でset_security_group.shを実行します。

sh set_security_group.sh

実行後にマネージメントコンソールで確認すると、インバウンドルールが追加されています。

f:id:predora005:20210101185537p:plain

スクリプトの中身は次の通りです。セキュリティグループのIDとリージョン名は、スタック作成時に自動で設定されます。

#!/bin/bash
IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json`
IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'`

count=0
for IP in $IP_LIST; do
  if [ $count -lt 60 ]; then
    SG={セキュリティグループ2のID}
  elif [ $count -lt 120 ]; then
    SG={セキュリティグループ3のID}
  else
    SG={セキュリティグループ4のID}
  fi

  aws ec2 authorize-security-group-ingress --region {リージョン名} --group-id $SG --protocol tcp --port 80 --cidr $IP
  echo $IP
  (( count++ ))
done

[5-1] シェルスクリプトの解説

シェルスクリプトは下記記事を参考にさせていただきました。

EC2のセキュリティグループにCloudFrontからしかアクセスを許可しない設定を追加する(改良版) | 綺麗に死ぬITエンジニア

[5-1-1] CloudFrontのIPアドレス取得

CloudFrontエッジサーバのIPアドレスip-ranges.jsonから取得します。ip-ranges.jsonから必要な情報をjqコマンドで取り出します。

ip-ranges.jsonはCloudFront以外の情報も含んでいます。service == "CLOUDFRONT"でCloudFrontの情報を抽出します。また、抽出した情報はIPアドレス以外の情報も含んでいるため.ip_prefixIPアドレスを抽出します。

IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json`
IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'`

[5-1-2] セキュリティグループのルール追加

抽出したIPアドレスからのHTTPアクセス(TCPプロトコルのポート80)を許可するルールを、セキュリティグループに追加します。

countIPアドレスの個数をカウントします。1セキュリティグループあたり、60ルール以内に収まるようにしています。

セキュリティグループへのルール追加はaws ec2 authorize-security-group-ingressで行います。

count=0
for IP in $IP_LIST; do
  if [ $count -lt 60 ]; then
    SG={セキュリティグループ2のID}
  elif [ $count -lt 120 ]; then
    SG={セキュリティグループ3のID}
  else
    SG={セキュリティグループ4のID}
  fi

  aws ec2 authorize-security-group-ingress --region {リージョン名} --group-id $SG --protocol tcp --port 80 --cidr $IP
  echo $IP
  (( count++ ))
done

[6] CloudFrontの設定

外部からnginxサーバーにアクセスする際は、'test.example.com'のようにドメイン形式でアクセスできるようにします。そして、外部からのアクセスはCloudFrontがHTTPSのみ受け付けて、CloudFrontからEC2へHTTPへ流します。それぞれの関係を表すと次の通りです。

サブドメイン名(test.exmaple.com) <---> CloudFront <---> EC2のパブリック IPv4 DNS

[6-1] CloudFormationのテンプレート

[6-1-1] パラメータ

次の内容はユーザーが入力します。調べ方は後述します。

パラメータ 備考
EC2インスタンスのパブリック IPv4 DNS CloudFrontのOrigin Domain Nameに指定。
Certificate ManagerのARN CloudFrontのSSL Certificateに指定。
ホストゾーン ID ドメイン登録時に作成されたホストゾーン。
サブドメイン 任意。Route53で登録したドメインサブドメイン
AWSTemplateFormatVersion: 2010-09-09
Description: >-
  AWS CloudFormation template for the nginx server.

Parameters:
  # Instance
  InstancePublicDns:
    Description: Instance Public DNS
    Type: String
    Default: 'ec2-xxx'
  
  # Certificate ManagerのARN
  AcmArn:
    Description: AWS Certificate Manager ARN
    Type: String
    Default: 'arn:aws:acm:xxx'

  # ホストゾーンのID
  HostedZoneId:
    Description: FQDN of the hosted zone
    Type: String
    Default: 'Z0xxx'
  
  # サブドメイン名
  SubDomain:
    Description: Sub Domain Name
    Type: String
    Default: 'xxx.example.com'

[6-1-2] CloudFront

CloudFrontは次の設定にします。

  • Origin側(EC2)はHTTP Only
  • Target側(CloudFront)はHTTPからHTTPSへリダイレクト
  • Certificate Managerで発行した証明書を関連付け
  • サブドメインを使用する
Resources:

  # CloudFront Distribution
  CloudFrontDistribution:
    Type: 'AWS::CloudFront::Distribution'
    Properties:
      DistributionConfig:
        Aliases:
        - !Ref SubDomain
        # インスタンス側はHTTP Onlyに設定
        Origins:
        - CustomOriginConfig:
            OriginProtocolPolicy: http-only
          DomainName: !Ref InstancePublicDns
          Id: !Sub '${AWS::StackName}-cloudfront-distribution'
        DefaultCacheBehavior:
          # TargetOriginIdは Origins Idと一致していないとNG
          TargetOriginId: !Sub '${AWS::StackName}-cloudfront-distribution'
          ViewerProtocolPolicy: redirect-to-https
          # ForwardedValuesは公式非推奨(CachePolicyが推奨だが面倒)
          ForwardedValues:
            QueryString: false
        # SSL Certificate
        ViewerCertificate:
          SslSupportMethod: sni-only
          AcmCertificateArn: !Ref AcmArn
          MinimumProtocolVersion: TLSv1.2_2019
        HttpVersion: http2
        IPV6Enabled: true
        # Enabledは必須
        Enabled: true
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-cloudfront-dns'

MinimumProtocolVersionHttpVersion, IPV6Enabledは任意のため指定しなくても問題はありません。

代替ドメイン名 (CNAME) を追加してカスタム URL を使用する - Amazon CloudFront

[6-1-3] Route53のエイリアスレコード

サブドメイン名とCloudFrontを関連付けします。CloudFront自体のドメイン名は'*.cloudfront.net'ですが、自分が決めたサブドメイン(www.example.com等)でCloudFrontにアクセスできるようにします。

  # DNS Record
  DnsRecord:
    Type: 'AWS::Route53::RecordSet'
    Properties:
      HostedZoneId: !Ref HostedZoneId
      Comment: 'DNS name for CloudFront'
      Name: !Ref SubDomain
      Type: A
      AliasTarget:
        # AliasTargetのHostedZoneIdはホストゾーンのIDとは別
        # CloudFrontの場合は固定
        HostedZoneId: Z2FDTNDATAQYW2
        DNSName: !GetAtt CloudFrontDistribution.DomainName

注意点はAliasTargetHostedZoneIdです。CloudFrontを使用する場合は「Z2FDTNDATAQYW2」固定です。

AWS::Route53::RecordSetGroup AliasTarget - AWS CloudFormation

[6-2] スタックの作成

先程と同様にマネージメントコンソールから作成します。

[6-2-1] パラメータの調べ方

サブドメイン名は任意に決めます。それ以外のパラメータはマネージメントコンソールから確認します。

EC2インスタンスのパブリック IPv4 DNS

f:id:predora005:20210101195157p:plain

Certificate ManagerのARN

f:id:predora005:20210101195225p:plain

ホストゾーン ID

f:id:predora005:20210101195242p:plain

[6-2-2] スタックの作成

調べたパラメータを入力します。

f:id:predora005:20210101211706p:plain

以降の項目はデフォルトのまま進めます。作成を開始して5分程度で作成が完了しました。

f:id:predora005:20210101212230p:plain

[7] 動作確認

設定したサブドメイン名(www.example.com等)でアクセスできるのかを確認します。

ブラウザのURL欄にサブドメイン名を入力します。正常に動作していれば、nginxのテストページが表示されます。

f:id:predora005:20210101212807p:plain

サーバ証明書を確認すると、有効になっていることが分かります。

f:id:predora005:20210101213004p:plain

EC2のIPアドレスを直に入力するとアクセスできないことが確認できます。セキュリティグループでCloudFrontからのアクセスのみ許可したため、EC2に直接アクセスできなくなっています。

f:id:predora005:20210101213951p:plain

[8] スタックの削除

作成したスタックを削除します。削除は新しいスタックから順に行います。

f:id:predora005:20210101214256p:plain

'CloudFrontDistribution'の削除は作成時と同様に5分程度かかります。

終わりに

複雑な構成では無いので手動で行えば、それほど時間はかかりません。しかし、CloudFormationでやろうとすると思いのほか時間がかかりました。

CloudFrontを使うことによって、CloudFormationのテンプレートが大分複雑になった感はあります。CloudFrontのIPをセキュリティグループに設定するのは、もう少し簡単になるといいなと思いました。

ただ、テンプレートを1回作成してしまえば何度でも使い回せるのはメリットです。だいぶ時間はかかりましたが、やってみて良かったなと思います。

ちなみに、HTTPS対応はCloudFrontだけが選択肢ではありません。ALBやNLBで代替可能な場合は検討してみるのもよいでしょう。また、EC2にSSHでログインして操作を行いましたが、Session Managerを使用する選択肢もアリです。

参考資料

AWSでWebサイトをHTTPS化 全パターンを整理してみました – ナレコムAWSレシピ

CloudFormationを使ってS3とCloudFrontの構成で静的Webサイトを構築する(Origin Access Identityは使わない) - Qiita

CloudFormation で OAI を使った CloudFront + S3 の静的コンテンツ配信インフラを作る | DevelopersIO