S3とCloudFrontでwebサイトを公開する際のバケット設定はどうすべきか?

公式のナレッジセンターの記事「CloudFront を使用して Amazon S3 でホストされた静的ウェブサイトを公開する」 で紹介されているwebサイト公開手法、 複数あるけど結局どれを採用すれば良いのか?という判断基準を整理したく、纏めてみました。

外部・viewer -> CloudFront -> S3 バケット(公開物はここに配置) という通信経路を想定しています。

論点

  • S3 × CloudFront という組み合わせでwebサイトを公開する場合の、バケットの作成方法の選択肢2通り
    1. webサイトホスティング
    2. 非・webサイトホスティング
  • 上記2通りのバケット作成方法のメリット・デメリットと、作成方法選択時の判断基準は何なのか?
    • 機能要件的な観点=外部・viewerからサイトにアクセスするユーザー観点では、上記2通りでどんな違いが生じうるのか?
    • 非機能要件的な観点では、上記2通りでどんな違いが生じうるのか?
    • webサイト公開が目的でも「非・webサイトホスティング を選ぶべき場合」があるなら、それはどういう場合なのか?

先に結論まとめ

検証内容と前提条件

検証環境の構成

検証環境構築にはterraformを利用しました。※動作確認アプリ&検証環境構築用terraformはこちらのリポジトリにupしておきました。

S3(webサイトホスティング) × CloudFront の設定

tfファイル

S3は下記の通りwebサイトホスティング前提の内容でbucket、public_access_block、bucket_policyを定義しておきます。

  1. acl = "public-read" 設定
  2. website を定義
    • ※定義はするものの、バケットの前段にCloudFrontを配置する構成だとバケット直アクセスは想定しなくなり、この設定が実際に利用される場面はなくなる
  3. public_access_blockクラスメソッドさんのこちらの記事を参考に、ACLに対して設定
  4. バケットポリシーは "aws:UserAgent" = "Amazon CloudFront" に対するAllowを設定
  5. server_side_encryption_configurationAES256サーバー側暗号化を有効にしておく
    • ※サーバーサイド暗号化したS3をwebサイトホスティングで公開するとアクセス時にエラーになるという言及を見掛けましたが、Principal: "*" に対して s3:GetObject を許可するバケットポリシーを設定した上で http://該当バケットのwebサイトエンドポイント/存在するhtmlファイル というURLでアクセスしても、手元ではエラーにはならず正常表示されました
  6. これら以外は任意で設定
    • CORS設定、アクセスログ設定、etc

そして、このバケットの前段に配置するCloudFrontを定義します。

  1. default_root_object を定義して https://cloudfrontドメイン へのアクセス時はindex.htmlを表示する
  2. origin の domain_namebucket_domain_name でも bucket_regional_domain_name でもなく website_endpoint を指定する。これにより https://cloudfrontドメイン/subdir/ のようなフォルダへのアクセスでも https://cloudfrontドメイン/subdir/index.html というindexドキュメントの表示が可能となる
  3. origin のconfigは custom_origin_config として定義し、origin_protocol_policy は(httpのみが利用可能なwebサイトホスティングなので) "http-only" を指定
  4. custom_error_response により、エラー時にS3が(デフォルトで)返してくるxmlエラーをhtmlに変換するようにしておく
  5. これら以外は任意で設定
    • cache behavior
    • 証明書
    • 地域制限設定
    • アクセスログ設定
    • etc

そして対象バケット(の直下)に以下を配置しておきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.
├── 404.html
├── 50x.html
├── assets
│   ├── index.xxxxxxxx.css
│   ├── index.xxxxxxxx.js
│   ├── logo.xxxxxxxx.png
│   └── vendor.xxxxxxxx.js
├── favicon.ico
├── index.html
└── subdir
    └── index.html

S3(非・webサイトホスティング) × CloudFront

tfファイル

S3バケットは、webサイトホスティングとは異なりパブリックアクセスは許可しない前提の内容で定義しておきます。

  1. acl = "private" 設定
  2. website を定義 しない
  3. public_access_block は全てtrue
  4. バケットポリシーは Origin Access Identityに対するAllowと、https以外(=http)を拒否するDenyを設定
    • Allow側ポリシーで許可するAction, Resourceはwebサイトホスティング用と同様
  5. server_side_encryption_configurationサーバー側暗号化を有効にしておく
    • webサイトホスティング用と同様
  6. これら以外は任意で設定(webサイトホスティング用と合わせておけばOK)

そして、CloudFrontをS3 Origin&Origin Access Identityの組み合わせで定義します。

  1. Origin Access Identity を定義
  2. origin のconfigは s3_origin_config として定義し、1 の origin_access_identity を指定
  3. これら以外は任意で設定(webサイトホスティング用と合わせておけばOK)

検証してみた

正常系 or 異常系でアクセス時の挙動が揃っているか?揃っていない場合はそれは何か?を検証します。

1. https://自動生成ドメイン文字列.cloudfront.net/存在するファイル へのアクセス

これは(当然ですが)webサイト or 非・webサイト共に同じ結果が表示されます。

01

初回以降のアクセスではCloudFrontでのキャッシュが効いてhttp statusは304になります。

2. https://自動生成ドメイン文字列.cloudfront.net/ へのアクセス

webサイト or 非・webサイトに関わらず、ドメイン名のみのアクセス時はCloudFrontの default_root_object 設定が有効であることを確認したい、という意図でしたが、これも想定通りでした。

02

3. https://自動生成ドメイン文字列.cloudfront.net/subdir/存在するファイル へのアクセス

これもwebサイト or 非・webサイト共に同じ結果が表示されます。フォルダ配下であっても存在するファイルのフルパスURLを指定すれば挙動に違いはありません。

03

4. https://自動生成ドメイン文字列.cloudfront.net/subdir/ へのアクセス

フォルダへのアクセスは、webサイトの場合のみ、フォルダ配下のindexドキュメント表示が機能します。これはCloudFrontのorigin設定で domain_name にS3バケットの website_endpoint プロパティを設定する事で実現できています。

04-web

逆に 非・webサイトではエラー(= 403)となり、webサイトの場合と挙動に差異が発生します。

04-notweb

5. https://自動生成ドメイン文字列.cloudfront.net/存在しないファイル名 でアクセス

存在しないファイルへのアクセスは、バケットポリシーで s3:ListBucket を許可していないと403エラーとなります。 デフォルトではその結果はhtmlではなくxmlで返ってくるので、CloudFrontの custom_error_response 設定でhtmlを返すよう設定してあります。

デフォルトの挙動とcustom_error_response によるエラーのカスタマイズについては、webサイト or 非・webサイトに関わらず同様です。つまり 存在しないファイルへアクセスされた際のエラー処理も、webサイトホスティング or 非・webサイトホスティングで同じ機能を実現できる という事です。

05

補足: custom_error_response の設定が無いとエラー表示はどうなるのか?

webサイトホスティング or 非・webサイトホスティングどちらのケースでも、custom_error_response 設定がない状態で存在しないページにアクセスした場合、s3:ListBucket バケットポリシーの有無でエラー表示の挙動が変わります

  • custom_error_response 設定がない、且つ、バケットポリシーで s3:ListBucket が許可されていない
    • 「S3がデフォルトで返す、AccessDenied を表すxml」が表示されます。http statusは 403
  • custom_error_response 設定がない、且つ、バケットポリシーで s3:ListBucket が許可されている
    • 「S3がデフォルトで返す、NoSuchKey を表すxml」が表示されます。http statusは 404
      • つまり、エラーの内容から「ファイルが無い」という事に気付く事が出来る

一方で、「webサイトホスティングで s3:ListBucket アクセスを許可する」事には注意が必要です。公式では、

パブリック s3:ListBucket アクセスを有効にすることは、セキュリティ上のベストプラクティスではありません。 パブリック s3:ListBucket アクセスを有効にすると、ユーザーはバケット内のすべてのオブジェクトを表示およびリスト化できます。 これにより、ユーザーがオブジェクトをダウンロードするアクセス許可を持っていない場合でも、オブジェクトのメタデータの詳細 (キーやサイズなど) がユーザーに公開されます

と言及されており、オブジェクトの存在を推測されるようなエラー表示を抑止したければ、webサイトホスティングに於いて s3:ListBucket を許可するのはプラクティスとしてはよろしくなく、避けた方が無難です。

※ちなみに、「custom_error_response 設定がある、且つ、バケットポリシーで s3:ListBucket が許可されている」という場合であれば、custom_error_response で404エラーを独自エラーに変換(して詳細を隠蔽する)という手段も可能と言えば可能です。

補足: webサイトホスティングで http://バケットのエンドポイント/存在するファイル名 でバケットへの直アクセスを試みるとどうなるか?

このパターンのアクセスを試してみると、“S3がデフォルトで返す、403を表すHTML” が表示され、アクセス拒否自体は成功している事が分かります。

05-hosoku-bucketerror

今回構築したwebサイトホスティング用のバケットでもカスタムエラードキュメントを設定していたものの、バケットポリシーでCloudFrontからのアクセスだけを許可していると、バケットへの直アクセス時はカスタムエラードキュメントへのアクセスも拒否される事になり、結果としてS3のデフォルトエラー画面が表示される という挙動になります。

改めて結論まとめ

  • webサイトホスティング or 非・webサイトホスティングどちらを選んでも差が無い観点
    • S3バケットへの直接アクセスを抑止し、CloudFrontからのみアクセス可能にする
      • webサイトホスティング:User-AgentがCloudFrontの場合だけAllowするバケットポリシーを設定
      • 非・webサイトホスティング: Origin Access Identityの活用
    • 外部(viewer) -> CloudFront 間の通信で HTTPS を必須化
  • webサイトホスティングでのみ可能な機能 も存在しており、これらを利用したいかどうか?という観点
  • 非・webサイトホスティング を 選ばざるを得ない 観点
    • CloudFront -> S3 間の通信を、何らかの事情で HTTPS にしなければならない
      • webサイトホスティングのバケットへの通信を HTTPS にする事はできないので、非・webサイトホスティング一択となる
      • 「何らかの事情」とは、例えば組織のセキュリティポリシー等
    • 何らかの事情でバケットにパブリックアクセスを許可する事が出来ない
      • webサイトホスティングのバケットはパブリックアクセスの許可が前提となるので、非・webサイトホスティング一択となる
    • publicに公開したくないコンテンツが含まれている
      • (これは言わずもがなですが、一応書いておきました)

セキュリティを重視したい場合は非・webサイトエンドポイントの方が有利 と言えるので、「webサイトエンドポイント or 非・webサイトエンドポイントどちらを選ぶか決め手に欠けて、webサイトエンドポイントでしか実現できない機能を使いたいという訳でもない…」ようなケースであれば、非・webサイトエンドポイントを選択するのが無難かもしれません。

ブックマーク