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のディストリビューションでバケットポリシーをコピー
変更されたバケットポリシーの例
{
"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 で先ほど作成した証明書を選択
- 「一般」タブ > 設定:編集 > 代替ドメイン名 (CNAME) の「項目を追加」
-
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分経過してもログは作成されない
使用条件
- ログ保存用の 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ウェブサイトエンドポイント推奨してくる(下図参照)
なお公式ではS3静的サイトとCloudfrontの連携には以下の方法が言及されています
- オリジン:REST API エンドポイント+OAC(またはOAI)
- オリジン:S3ウェブサイト + パブリックアクセス許可
- オリジン:S3ウェブサイト + Referer ヘッダーでアクセスを制限する
- 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の違い: