LoginSignup
1
2

CloudFrontを用いたS3静的サイトの構築

Last updated at Posted at 2024-05-20

TL;DR

  • CloudFront + S3 で静的サイトを構築できる
  • OAC;Origin Access Control を用いてS3のオブジェクトへのアクセスをCloudFront経由に限定する事ができる
  • OAC を用いる場合は CloudFront のオリジンは オブジェクトストレージの S3 エンドポイント(URL) を指定する
  • TTL関連はキャッシュポリシーで設定する

はじめに

CloudFront + S3 で静的サイトを構築してみましたが、意外と苦労をしたので作業手順をまとめました。

作業手順

  • S3静的サイトを構築
    • バケット選択 > プロパティ > 静的ウェブサイトホスティング
    • アクセス許可 > パブリックアクセスをすべてブロック のチェックを外す
    • バケットポリシーを変更して s3:GetObject をAllowする。
      参考:公式(バケットポリシー)

⇒ S3(website)単体で動作確認


  • CloudFront の OACを作成
    • ナビゲーションペイン:セキュリティ:オリジンアクセス > コントロール設定を作成
      • 「署名動作」は「署名リクエスト(推奨)」
      • 「オリジンタイプ」は「S3」

  • Cloudfront のディストリビューションを作成
    • オリジンドメイン:S3バケットのエンドポイントを指定(後述)
    • オリジンアクセスでOACを選択。先ほど作成したOACを指定する
    • デフォルトルートオブジェクトに index.html を指定
      (実際のS3のオブジェクトに合わせる)

s3エンドポイントの補足
〇 mybucket.s3.ap-northeast-1.amazonaws.com
× mybucket.s3-website-ap-northeast-1.amazonaws.com

⇒ 他はデフォルトのままでもよい。(WAFだけ選択必須)
⇒ ディストリビューションを作成


  • S3バケットの設定を修正
    • CloudFrontディストリビューション > ディストリビューションを選択 > 「オリジン」タブ >オリジンを選択 > 編集 > 「オリジンアクセス」セクションの「ポリシーをコピー」をクリック
    • S3へ移動 > バケット選択 > アクセス許可 > バケットポリシー > 編集 > 先ほどコピーしたポリシーを張り付ける
    • バケット選択 > プロパティ > 静的ウェブサイトホスティングを無効に
    • アクセス許可 > パブリックアクセスをすべてブロック のチェックを入れる

補足:CloudFrontのディストリビューションでバケットポリシーをコピー
image.png

変更されたバケットポリシーの例
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PolicyForCloudFrontPrivateContent",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::mybucket/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/YOUR_DISTRIBUTION"
                }
            }
        }
    ]
}

⇒ CloudFrontのドメイン名(*********.cloudfront.net)で静的サイトにアクセスできる事を確認する。

追加設定例

Route 53 のドメイン名を適用する

Route 53 を利用して作成した独自ドメインを静的サイト用のURLにする方法。

前提条件

  • Route53でドメイン、ホストゾーンが作成済である
  • ACMでドメインの証明書を発行
    補足:*.mydomainname.com といったドメイン全体の証明書

  • CloudFrontディストリビューションの設定を修正

    • 「一般」タブ > 設定:編集 > 代替ドメイン名 (CNAME) の「項目を追加」
      (ここではsample.mydomainname.com とする)
    • Custom SSL certificate で先ほど作成した証明書を選択
  • Route 53でレコードを作成

    • Route 53でホストゾーンmydomainname.com を選択
    • レコードを作成
      • レコード名:sample(mydomainname.com)
      • エイリアス:オンにする
      • エンドポイント:CloudFront
      • ディストリビューション:先ほど作成したディストリビューションのCNAME

キャッシュポリシー

概要

Default TTL, Max TTL, Min TTL などを設定する為のもの。

操作(ポリシーの割り当て):CloudFront > ディストリビューションを選択 > ビヘイビア > 編集 > キャッシュキーとオリジンリクエスト > Cache policy and origin request policy (recommended)

操作(ポリシーの作成):CloudFront > ナビゲーションペイン:ポリシー > キャッシュポリシーを作成

以前は「Legacy cache settings」の追加設定で直接指定していたようですが、2024年5月現在非推奨との事。

Amazonマネージドポリシー

以下のポリシーが用意されているようです(詳細は割愛):

  • Amplify
  • CachingDisabled
  • CachingOptimized
  • CachingOptimizedForUncompressedObjects
  • Elemental-MediaPackage

情報元(公式):https://go.aws/3bJid3k

カスタムポリシー

文字通り、自分で作成するキャッシュポリシー。
これを説明する公式資料は無い...かも?こことか?

自分はCloudFormationの資料を見ましたが。

Cloudfrontテンプレート例(キャッシュポリシー)

参考までに、Cloudfrontテンプレート例です。但し、正直あんまり理解できてない部分があります。(2024.05.22現在)

テンプレート例(Amazon 管理の CachingOptimized 同等)
  StaticSiteBucketCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: my-cache-policy
        DefaultTTL: 86400
        MaxTTL: 31536000
        MinTTL: 1
        ParametersInCacheKeyAndForwardedToOrigin:
          EnableAcceptEncodingGzip: true
          EnableAcceptEncodingBrotli: true
          CookiesConfig: 
            CookieBehavior: none
          HeadersConfig: 
            HeaderBehavior: none
          QueryStringsConfig: 
            QueryStringBehavior: none
            
  # 参照の仕方
  StaticSiteBucketDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        DefaultCacheBehavior:
          CachePolicyId: !Ref StaticSiteBucketCachePolicy

ロギング

概要

操作:CloudFront > ナビゲーションペイン:ログ

CloudFrontのログは以下の2つの方法があるようです。
ここでは標準ログについて書きます。(リアルタイムログは試してません)

タイプ 転送先 備考
標準ログ S3 
リアルタイムログ  Kinesis Data Streams

特徴

  • ログの書き方は「W3C 拡張形式」
  • .gz アーカイブが作成される
  • 初回アクセスの5分後にその間のアクセス分が書き込まれ、以後 5分毎に同様の動作をするっぽい
  • アクセスがなかった場合は 5分経過してもログは作成されない

参考:https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#AccessLogsOverview

使用条件

  • ログ保存用の S3 バケット(以後 LogBucketとする)を用意
    • 静的サイトのバケットと別にする事が推奨
  • LogBucket は「オブジェクト所有者」で ACL 有効にする必要がある
    • 2023 年 4 月以降の変更
    • 「オブジェクト所有者」はオブジェクトライター、希望するバケット所有者 のどちらでも良い
      操作:バケット選択 > アクセス許可 > オブジェクト所有者 > 編集
  • LogBucket のバケットポリシーで対象のCloudFrontディストリビューションへ s3:PutObject を Allow する必要がある
  • LogBucket のブロックパブリックアクセスの無効化は必要ない

補足

公式資料の一部がまだ理解できていない(当然達成できていない)にも関わらずログは出力されているので少し不安はあります。

理解できていない部分:

  • FULL_CONTROL を付与
  • s3:GetBucketAcl と s3:PutBucketAcl を付与

Cloudfrontテンプレート例(ロギング)

テンプレート例(抜粋)
  LoggingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${ProjectName}-logging-bucket
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
  LoggingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref LoggingBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: DistributionLoggingPermission
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:PutObject
            Resource:
              - !Sub ${LoggingBucket.Arn}/*
            Condition:
              ArnLike:
                AWS:SourceArn: arn:aws:cloudfront::${AWS::AccountId}:distribution/*
  StaticSiteBucketDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Logging:
          Bucket: !GetAtt LoggingBucket.RegionalDomainName
          Prefix: cloudfront/

資料

自分がつまづいたポイント

以下のミスマッチにより苦労しました。

  • CloudfrontのオリジンにS3ウェブサイトエンドポイントを指定した場合、OACによるアクセスコントロールはできない
  • Webで S3サイトのCloudfront経由制限の情報はOACを用いたものが多かった
  • CloudfrontでS3静的サイトをオリジンとする場合、マネージメントコンソールはS3ウェブサイトエンドポイント推奨してくる(下図参照)

image.png


なお公式ではS3静的サイトとCloudfrontの連携には以下の方法が言及されています

  1. オリジン:REST API エンドポイント+OAC(またはOAI)
  2. オリジン:S3ウェブサイト + パブリックアクセス許可
  3. オリジン:S3ウェブサイト + Referer ヘッダーでアクセスを制限する
  4. AWS CloudFormation を使用して、CloudFront を指すカスタムドメインをデプロイする

今回の方法は1. に近い方法です。
3 は試してませんがなんとなく有力な気がします。

2 はS3に直接アクセスできるので却下(自分の理解が正しければ)
4 はよく分からない 笑

S3静的サイトの特徴

  • HTTP エンドポイントのみをサポート

Cloudfront の特徴

独断と偏見で役に立つと感じた順に:

  • HTTPS(TLS1.3) をサポート
  • クライアントからのリクエストを受けたとき、レスポンスを返す時に関数を実行できる
    • 認証認可やレスポンスの生成に役立つ
    • CloudFront Functions(js)とLambda@Edgeがある
  • オリジンとなるAWSリソースへのアクセス回数を減らす
  • WAFの利用が可能
     補足:レイヤー3,4 への DDos は AWS Sheildによって守られる
  • コンテンツ読み込みの速度を上げる(通常のCDNに期待する機能)
  • キャッシュヒット率の設定ができる(ビヘイビア)

参考(CloudFrontを使う理由):https://zenn.dev/funteractiveinc/articles/d2a0d96f82bd2f

料金

  • オリジン⇔CloudFront間は無料
  • CloudFrontからインターネットへデータ転送 0.114USD/GB
    (※東京リージョン、最初の10TB/月まで)
  • CloudFront Functions  100 万件の呼び出しあたり 0.10 USD

公式(料金):https://aws.amazon.com/jp/cloudfront/pricing/

Cloudformationのテンプレート例

今回の S3 + Cloudfront を構築するテンプレート例です。

テンプレート例
AWSTemplateFormatVersion: "2010-09-09"
Description: my-static-site-with-cloudfront
Parameters:
  ProjectName:
    Type: String
    Default: my-static-site-with-cloudfront
Resources:
  StaticSiteBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${ProjectName}-static-site
  StaticSiteBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref StaticSiteBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: PolicyForCloudFrontPrivateContent
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource:
              - !Sub ${StaticSiteBucket.Arn}/*
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${StaticSiteBucketDistribution}
  StaticSiteBucketOAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig: 
        Name: !Sub ${ProjectName}-oac
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
  StaticSiteBucketDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - Id: S3Origin
            DomainName: !GetAtt StaticSiteBucket.RegionalDomainName
            OriginAccessControlId: !GetAtt StaticSiteBucketOAC.Id
            S3OriginConfig:
              OriginAccessIdentity: ""
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          AllowedMethods:
           - HEAD
           - GET
          ForwardedValues: 
            QueryString: true
          ViewerProtocolPolicy: redirect-to-https
        HttpVersion: http2 
        Enabled: true
        DefaultRootObject: index.html      

参考資料

基本概念から主要機能まで説明(わかりやすい):

AWS WAFとAWS Shieldの違い:

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