CloudFrontからS3コンテンツを配信する際に、OAIでオリジンへのアクセスを制限する
以下のページで、S3バケットのコンテンツをCloudFrontから配信する際に、Refererヘッダーを使用することで、オリジンへのアクセスを制限する方法をご紹介しました。
ただし上記の構成には1つの課題があります。
それはRefererヘッダーに適切な値を設定されてしまうと、直接オリジンにアクセスすることができてしまうという点です。
これを解決するために、今回はOAI(Origin Access Identity)を使用します。
オリジンアクセスアイデンティティ (OAI) と呼ばれる特別な CloudFront ユーザーを作成し、ディストリビューションに関連付けます。
CloudFront が OAI を使用してバケット内のファイルにアクセスしてユーザーに提供できるように、S3 バケットのアクセス許可を設定します。
オリジンアクセスアイデンティティ (OAI) を使用して Amazon S3 コンテンツへのアクセスを制限する
OAIを使用する方法と、Refererヘッダーを使用する方法を簡単に比較します。
項目 | OAI | Refererヘッダー |
静的ウェブサイトホスティング | 無効 | 有効 |
オリジンに指定するエンドポイント | REST APIエンドポイント | ウェブサイトエンドポイント |
ドキュメントルートオブジェクト | 必須 | 不要 |
アクセス制限方法(バケットポリシー) | プリンシパルにOAIユーザーを設定 | Refererヘッダーの値をチェックするように設定 |
構成する環境
全体的な構成は以前と同様です。
CloudFrontにはOAIを作成し、S3バケットポリシーにはOAIユーザーからのアクセスを許可する設定を行います。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/050
テンプレートファイルのポイント解説
本ページでは、OAIを使用したオリジンへのアクセス制限方法を中心に取り上げます。
それ以外の内容については、冒頭でご紹介したページをご確認ください。
静的ウェブサイトホスティング機能は不要
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref Prefix
AccessControl: Private
#WebsiteConfiguration:
# IndexDocument: index.html
Code language: YAML (yaml)
OAIを使用するためには、静的ウェブサイトホスティング機能を有効化する必要はありません。
この設定では、バケットで静的ウェブサイトホスティングを有効にする必要はありません。
CloudFront を使用して、Amazon S3 でホストされた静的ウェブサイトを公開するにはどうすればよいですか?
ですから該当のコードをコメントアウトしています。
S3バケットのエンドポイント整理
ここでS3バケットの2つのエンドポイントの特徴を確認しておきます。
項目 | REST APIエンドポイント | ウェブサイトエンドポイント |
フォーマット | DOC-EXAMPLE-BUCKET.s3.region.amazonaws.comDOC-EXAMPLE-BUCKET.s3.amazonaws.com | DOC-EXAMPLE-BUCKET.s3-website-region.amazonaws.com |
HTTP/HTTPS | HTTPS | HTTP |
CFNでの取得方法 | !GetAtt Bucket.RegionalDomainName!GetAtt Bucket.DomainName | !GetAtt Bucket.WebsiteURL |
冒頭で述べた通り、OAIを使用する場合は、CloudFrontのオリジンとしてREST APIエンドポイントを指定します。
この設定では、静的ウェブサイトホスティング機能のウェブサイトエンドポイントではなく、バケットの REST API エンドポイントを使用します。
CloudFront を使用して、Amazon S3 でホストされた静的ウェブサイトを公開するにはどうすればよいですか?
次にREST APIエンドポイントに注目します。
REST APIエンドポイントには2つのフォーマットが用意されていますが、リージョン情報が含まれているものの方が望ましいとされています。
CloudFront のオリジンとして Amazon S3 バケットを指定する場合は、次の形式を使用することをお勧めします。
bucket-name.s3.region.amazonaws.com
CloudFront ディストリビューションでのさまざまなオリジンの使用
上記に従い、今回はこのフォーマットを使用することにします。
このフォーマットの値を取得する方法を確認します。
この値はCloudFormationの組み込み関数Fn::GetAttを使用して、「!GetAtt Bucket.RegionalDomainName」で取得することができます。
OAI
Resources:
OAI:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Ref Prefix
Code language: YAML (yaml)
OAIを定義します。
設定項目は限られており、Commentプロパティのみです。
ここでOAIの概念について確認しておきます。
OAIは一種のIAMユーザーと捉えることができます。
CloudFrontディストリビューションに紐づく特殊なユーザーであり、エンドユーザーからリクエストを受けると、OAIがユーザーの代理でS3バケットにアクセスするという流れになります。
CloudFrontはドキュメントルートオブジェクトの設定が必須
Resources:
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
Compress: true
ForwardedValues:
Cookies:
Forward: none
QueryString: false
TargetOriginId: !Ref BucketName
ViewerProtocolPolicy: allow-all
DefaultRootObject: index.html
Enabled: true
Origins:
- DomainName: !Ref BucketRegionalDomainName
Id: !Ref BucketName
S3OriginConfig:
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${OAI}"
#CustomOriginConfig:
PriceClass: PriceClass_All
Code language: YAML (yaml)
ポイントは2点です。
1点目はデフォルトルートオブジェクトです。
この設定は必須です。
ディストリビューションにデフォルトのルートオブジェクトが定義されておらず、リクエスタが s3:ListBucket アクセス許可を持っていない場合、リクエスタはアクセス拒否エラーを受け取ります。
CloudFront ディストリビューションのオリジンとして S3 REST API エンドポイントを使用しています。403 Access Denied エラーが発生するのはなぜですか?
上記に従い、index.htmlというファイルをルートオブジェクトとして設定します。
なおこのファイルはCloudFormationカスタムリソースでLambda関数を実行し、自動生成します。
詳細は以下のページをご確認ください。
2点目はオリジンに関する設定です。
まずオリジンの指定ですが、Originsプロパティの内部で行います。
DomainNameプロパティで、先述のリージョン情報を含むフォーマットのREST APIエンドポイントを指定します。
次にオリジンの詳細設定として、S3OriginConfigプロパティに設定を行います。
Refererヘッダーを使用する際は、CustomOriginConfigプロパティに設定を行いましたが、今回はそうではありません。
Use S3OriginConfig to specify an Amazon S3 bucket that is not configured with static website hosting.
Use CustomOriginConfig to specify all other kinds of origins
AWS::CloudFront::Distribution Origin
上記に従い、S3OriginConfigプロパティを選択します。
このプロパティには、OriginAccessIdentityプロパティというOAIを設定するプロパティが用意されています。
記載方法は以下の通りに説明されています。
The format of the value is:
origin-access-identity/cloudfront/ID-of-origin-access-identity
AWS::CloudFront::Distribution S3OriginConfig
上記に従い、組み込み関数Fn::Subを使用して、文字列を作成します。
バケットポリシーのプリンシパルにOAIを指定する
Resources:
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref BucketName
PolicyDocument:
Statement:
Action:
- s3:GetObject
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${OAI}"
Resource: !Sub "arn:aws:s3:::${BucketName}/*"
#Condition:
Code language: YAML (yaml)
バケットポリシーにて、バケットにアクセスできるユーザーを制限します。
今回はConditionではなく、Principalプロパティを使用します。
このプロパティにユーザーとしてOAIを設定することになります。
具体的には以下の通りに設定を行います。
arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity [OAI-id]
オリジンアクセスアイデンティティ (OAI) を使用して Amazon S3 コンテンツへのアクセスを制限する
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- S3バケット:fa-050
- CloudFrontディストリビューションのドメイン:https://d38ixdp3esygfv.cloudfront.net/
- OAIの名前:fa-050
- OAIのID:E3A4FYJVN7KUC8
AWS Management Consoleからもリソースを確認します。
まずCloudFrontです。
S3バケットがオリジンとして設定されています。
リージョンを含むタイプのREST APIエンドポイントを指定しています。
またバケットへのアクセス時は、OAI(fa-050)を使用する設定となっていることも確認できます。
デフォルトルートオブジェクトにindex.htmlを設定しています。
つまり「https://d38ixdp3esygfv.cloudfront.net/」にアクセスすると、「https://d38ixdp3esygfv.cloudfront.net/index.html」にアクセスしたものとしてして動作するという意味です。
次にS3バケットポリシーを確認します。
先ほどとOAIのIDを指定しています。
このOAIからのアクセスの場合は、オブジェクトへのアクセスを許可するという内容です。
ちなみに静的ウェブサイトホスティング機能は有効化していません。
動作確認
準備が整いましたので、CloudFrontディストリビューションにアクセスします。
S3バケットのコンテンツが表示されました。
CloudFrontを経由して、オリジンであるS3バケットのREST APIエンドポイントにアクセスしているということです。
当然、S3バケットに直接アクセスすることはできません。
今回のOAIを使用する方法は、ヘッダー情報に依存した方法ではないため、よりセキュアな方法と言えます。
まとめ
S3バケットのコンテンツを配信する際に、OAIを使用することによって、CloudFront経由でアクセスすることを強制する方法をご紹介しました。