1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

簡単Webサイトホスティング①ワンクリックで配信基盤を構築するCloudFormationテンプレート

Last updated at Posted at 2021-01-16

はじめに

Amazon Web Services(AWS)が提供する、Amazon CloudFrontAmazon S3 と呼ばれるサービスを組み合わせることで、 HTMLやJavaScript、画像、ビデオなどで構成される静的Webサイトの配信基盤安価に構築することができます。本記事では、リソースのセットアップを自動で行うことのできる、AWS CloudFormation を用いることで、これらの配信基盤を ミスなく迅速に構築 する手順をご説明します。なお、今回使用する CloudFormation テンプレートは、以下の GitHub リポジトリで公開しています。

TL;DR

以下の CloudFormation テンプレートを実行することで、 静的Webサイトのホスティング基盤迅速かつお手軽に実現 します。下にあるボタンをクリックすると、自身のAWSアカウント(Asia Pacific Tokyo - ap-northeast-1)で、この CloudFormation テンプレートを実行することが可能となります。

cloudformation-launch-stack

作成されるAWSリソースとそのアーキテクチャ図はこちら。

この CloudFormation テンプレートは、 Nested Stack の構成となっており、下記のAWSサービスを単体で作成することも可能です。

作成されるAWSサービス 個別のCloudFormationテンプレート 作成されるサービス
Synthetics cloudformation-launch-stack 作成した運用基盤の 外形監視 を行います
Real-time Dashboard cloudformation-launch-stack CloudFront の アクセスログリアルタイムに解析 します
WAF cloudformation-launch-stack 作成した配信基盤に WAF を導入 します

なお、 上記の Nested Stack のリソースの作成は必須ではなく 、これらが無くても正常に動作します。 静的Webサイトのホスティングに必要となるのは、 Amazon CloudFront と Amazon S3 のみ であり、本記事ではこの部分に焦点を当ててご説明します。

architecture.png

アーキテクチャ

このテンプレートの設定内容は以下の通りです。なお、以下のYAMLコードは、AWSCloudFormationTemplates/static-website-hosting-with-ssl で公開しているCloudFormationテンプレートから、本記事用に一部抜粋したものです。本記事作成用に一部改変を行なっていること、またリポジトリの最新のコードを常に反映している訳ではないことをご了承ください。詳細は、GitHubの当該リポジトリをご覧ください。

Amazon S3

Amazon S3は、ユーザがデータを安全にかつ容量無制限に保存可能な オブジェクトストレージ で、本アーキテクチャでは、 オリジンWebサーバ としての役割を担います。このS3の中に、HTMLやJavaScript、画像、ビデオなどの 静的ファイルが保存 されており、ユーザからのHTTPSリクエストに従って、これらのコンテンツが配信されます。

なお、S3自体に備わっている 静的Webサイトホスティング機能 を有効化することで、S3のみでWebサイトのホスティングを実現することも可能です。しかし、 S3に以下のような制限がある ことから、本テンプレートでは CloudFront と組み合わせて静的Webサイトのホスティングを実現 しています。

S3バケットに関するCloudFormationテンプレートは以下の通りです。

Resource:
  S3ForWebHosting:
    Type: 'AWS::S3::Bucket'
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: AES256
      BucketName: example.com
      LifecycleConfiguration:
        Rules:
          - Id: NonCurrentVersionExpiration
            NoncurrentVersionExpirationInDays: 90
            Status: Enabled
      LoggingConfiguration:
        DestinationBucketName: !Ref S3ForAccessLog
        LogFilePrefix: S3-example.com/
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled

バケット内が空である場合のみS3バケットの削除が可能となるため、S3バケットには、 UpdateReplacePolicy および DeletionPolicy を指定することで、 リソース置換時およびリソース削除時は、リソースを保持する 設定にしています。また、上述のように CloudFront と組み合わせて静的Webサイトホスティングを実現するため、 規定ACLprivate にした上で、 パブリックアクセスを全て禁止 にしています。これにより、 外部から直接S3バケットに対してアクセスされることはなく、ファイルの取得は全てCloudFrontを経由 します。

なお、のちほどご説明する Origin Access Identity が、 AES256によるバケット暗号化のみをサポート していること、AWS の基本的なセキュリティのベストプラクティス標準 にて S3バケットのサーバサイド暗号化を求められている ことから、 AES256によるサーバサイド暗号化を有効化 しています。

Parameters:
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: The CNAME attached to CloudFront [required]

Resources:
  S3BucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties: 
      Bucket: !Ref S3ForWebHosting
      PolicyDocument:
        Version: 2012-10-17
        Id: !Ref S3ForWebHosting
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}
            Action:
              - 's3:GetObject'
            Resource: !Sub arn:aws:s3:::${S3ForWebHosting}/*
          - Effect: Deny
            Principal: '*'
            Action: 's3:*'
            Resource: 
              - !GetAtt S3ForWebHosting.Arn
              - !Join
                - ''
                - - !GetAtt S3ForWebHosting.Arn
                  - /*
            Condition:
              Bool: 
                aws:SecureTransport: false
  CloudFrontOriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    Properties:
      CloudFrontOriginAccessIdentityConfig: 
        Comment: !Sub access-identity-${DomainName}

S3のバケットポリシーでは、 Origin Access Identity を用いて CloudFrontユーザからの GetObject 操作のみを許可 しています。また、AWS の基本的なセキュリティのベストプラクティス標準 にて Secure Socket Layer を使用するためのリクエストが必要 とされているため、 SSLを用いないS3バケットへのアクセスを明示的に禁止 しています。

また、上述のS3バケットとは別に、 ログを蓄積するためのS3バケットを作成 します。このバケットの規定ACLには、 LogDeliveryWrite を指定しています。

Resources:
  S3ForAccessLog:
    Type: 'AWS::S3::Bucket'
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      AccessControl: LogDeliveryWrite
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: AES256
      BucketName: !Sub logs-${AWS::Region}-${AWS::AccountId}
      LifecycleConfiguration:
        Rules:
          - Id: ExpirationInDays
            ExpirationInDays: 60
            Status: Enabled
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
  S3BucketPolicyForAccessLog:
    Condition: CustomLogBucketName
    Type: 'AWS::S3::BucketPolicy'
    Properties: 
      Bucket: !Ref S3ForAccessLog
      PolicyDocument:
        Version: 2012-10-17
        Id: !Ref S3ForAccessLog
        Statement:
          - Effect: Deny
            Principal: '*'
            Action: 's3:*'
            Resource: 
              - !GetAtt S3ForAccessLog.Arn
              - !Join
                - ''
                - - !GetAtt S3ForAccessLog.Arn
                  - /*
            Condition:
              Bool: 
                aws:SecureTransport: false

本テンプレートで作成するS3バケットは、AWS の基本的なセキュリティのベストプラクティス標準 の以下の項目に準拠しています。

No. ルール 実行内容
S3.1 S3 バケットでは、パブリックアクセスブロック設定を有効にする必要があります パブリックアクセスを禁止します
S3.2 S3 バケットでは、パブリック読み取りアクセスを禁止する必要があります パブリックアクセスを禁止します
S3.3 S3 バケットでは、パブリック書き込みアクセスを禁止する必要があります パブリックアクセスを禁止します
S3.4 S3 バケットでは、サーバー側の暗号化を有効にする必要があります AES256によるサーバ側暗号化を行います
S3.5 S3 バケットでは、Secure Socket Layer を使用するためのリクエストが必要です バケットボリシーを用いて、SSL通信以外のリクエストを拒否します

Amazon CloudFront

CloudFrontは、コンテンツを低遅延、高速かつ安全に配信することが可能な **コンテンツ配信ネットワーク(CDN)**で、AWS Certificate Manager(ACM)で作成/インポートされた SSL/TLS証明書を簡単に紐付ける ことができます。

ユーザからのHTTPリクエストはまず最初にこのCloudFrontが受信し、 CloudFrontがキャッシュしていないコンテンツのみをオリジンWebサーバの役割を担うS3バケットに問い合わせるため、 S3バケットへのトラフィックを大幅に低減することが可能 です。

また、 GET/HEAD リクエストのみ許可 され、 かつS3バケットへは Origin Access Identity (OAI)と呼ばれる、特別なCloudFrontユーザからのリクエストのみが許可 されるため、 AWSリソースおよびS3バケット上のコンテンツは安全に保護 されます。

Parameters:
  CertificateManagerARN:
    Default: ''
    Type: String
    Description: The ARN of an SSL Certifiation attached to CloudFront
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: The CNAME attached to CloudFront [required]
  CloudFrontDefaultTTL:
    Default: 86400
    MinValue: 0
    Type: Number
    Description: CloudFront Default TTL [required]
  CloudFrontMinimumTTL:
    Default: 0
    MinValue: 0
    Type: Number
    Description: CloudFront Minimum TTL [required]
  CloudFrontMaximumTTL:
    Default: 31536000
    MinValue: 0
    Type: Number
    Description: CloudFront Maximum TTL [required]

Resources:
  CloudFront:
    Type: 'AWS::CloudFront::Distribution'
    Properties:
      DistributionConfig:
        Aliases:
         - !Ref DomainName
        Comment: !Sub CDN for ${DomainName}
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
          DefaultTTL: !Ref CloudFrontDefaultTTL
          ForwardedValues:
            QueryString: false
          MaxTTL: !Ref CloudFrontMaximumTTL
          MinTTL: !Ref CloudFrontMinimumTTL
          SmoothStreaming: false
          TargetOriginId: !Ref S3ForWebHosting
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject : index.html
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: false
        Logging:
          Bucket: !GetAtt S3ForAccessLog.DomainName
          Prefix: !Sub CloudFront-${DomainName}/
        Origins:
          - DomainName: !GetAtt S3ForWebHosting.DomainName
            Id: !Ref S3ForWebHosting
            S3OriginConfig:
              OriginAccessIdentity: !Join
               - ''
               - - origin-access-identity/cloudfront/
                 - !Ref CloudFrontOriginAccessIdentity
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateManagerARN
          MinimumProtocolVersion: TLSv1.1_2016
          SslSupportMethod: sni-only

本テンプレートでは、 任意のカスタムドメイン とこれに 関連するSSL/TLS証明書 をCloudFrontディストリビューションに紐づけます。また、ディストリビューションが コンテンツをキャッシュする時間(TTL)を自由に指定 することが可能です。

以上で、Amazon S3 バケットと Amazon CloudFront ディストリビューションを用いた、 静的Webサイトの配信基盤を構築 することができました。 トラフィックの大部分はCloudFrontで処理 され、またCloudFrontとS3との間の通信に Origin Access Identity が使用されるために、 オリジンWebサーバの役割を担うS3バケットと、その中に保存されている静的コンテンツは安全に保護 されます。また、任意のカスタムドメインを紐付けて、このドメインに対して HTTPS通信 を行うこともできます。これらのトラフィックの ログは、指定したS3バケットに一定期間保存 されます。

関連リンク

  1. ワンクリックで配信基盤を構築 - CloudFormation を用いて簡単Webサイトホスティング
  2. CloudFrontにWAFをアタッチ - CloudFormation を用いて簡単Webサイトホスティング
  3. 特定のURLを定期的にモニタリングする - CloudFormation を用いて簡単Webサイトホスティング
  4. CloudFrontのリアルタイムログをKibanaで可視化する - CloudFormation を用いて簡単Webサイトホスティング
1
2
0

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
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?