CloudFront + S3 静的ウェブサイトホスティング

CloudFront_s3

CloudFrontを経由して、S3バケットへアップロードしたコンテンツへのアクセスについて解説したいと思います。

構成図は以下です。

目的: CloudFront経由でアクセスすることを目的としているため、S3のオリジン内に直接アクセスされては無意味になってしまうため、以下の方法でオリジンへのアクセスを制御する。

必須条件
S3バケットへのアクセス許可をオリジンアクセスコントロールに付与する

S3バケットへ付与するポリシーは後ほど紹介しますが、前提として上記が必要であることを覚えておきましょう。

CloudFrontとは

Amazon CloudFrontとは、動画、アプリケーション、静的、動的なコンテンツを迅速かつ安全に配信できるCDN (Content Delivery Network)サービスです。

エッジロケーションというデータセンターの世界的なネットワークを経由してコンテンツを配信します。

閲覧者がウェブサイトからファイルをリクエストすると、CloudFront は自動的に最も近いエッジロケーションのファイルのコピーにリクエストをリダイレクトします。

これにより、遠い場所にあるデータセンターからコンテンツをリクエストした場合よりもダウンロード時間が高速になります。

今回はオリジン(コンテンツが格納されているサーバ)をS3として、静的なフファイルを配置し、CloudFront経由でS3へアクセスする手順やコンテンツをS3へアップロードした際の暗号化について深堀しました。

S3 handson

早速ですが、handsonをしながら途中途中解説していき、ポイントで深堀りしていきたいと思います。

S3に対してオリジンアクセスコントロールのアクセス許可をしてCloudFront経由でコンテンツにアクセスする方法の手順を解説します。

S3バケットの作成

バケット名を入力します。

ACL 無効を選択します。

このバケットのブロックパブリックアクセス設定のパブリックアクセスをすべてブロックのチェックを外します。

現在の設定により、このバケットとバケット内のオブジェクトが公開される可能性があることを承認します。にチェックを付けます。

デフォルトの暗号化は以下の通り選択します。

暗号化タイプ: Amazon S3 マネージドキーを使用したサーバ―側の暗号化 (SSE-S3)

バケットキー: 有効にする

これでバケットを作成します。

全世界へ静的Webサイトの公開

作成したS3バケットをクリックします。

プロパティタブを選択します。

静的ウェブサイトホスティングの項目までスクロールし、編集ボタンをクリックします。

静的ウェブサイトホスティングを”有効にする” に変更し、

インデックスドキュメント: index.html

エラードキュメント: error.htmlと入力し、変更の保存をします。

オブジェクトをアップロードします。

作成したS3バケット名をクリックし、以下の画面からオブジェクトをアップロードします。

アップロードするファイルはなんでも構いませんが、今回は以下のindex.htmlstyle.cssをアップロードします。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Hello world!</title>

        <meta charset="utf-8">

        <link rel="stylesheet" href="./css/style.css" />
    </head>

    <body>

        <h1>Hello world!</h1>

    </body>
</html>
@font-face {
    font-family: AmazonEmber;
    src: url("https://a0.awsstatic.com/libra-css/fonts/amazon-ember/AmazonEmber_Lt.woff");
    font-weight: 400;
    font-style: normal;
    font-size: 12pt;
}

body {
    background: #236192;
    font-family: AmazonEmber, Helvetica Neue, Helvetica, Arial, sans-serif;
    color: #ffffff;
    max-width: 75em;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
}

上記と同様のファイルはAWS公式からダウンロードできますので、併せてリンクを載せておきます。

簡単な CloudFront ディストリビューションの開始方法

このセクションの手順では、CloudFront を使用して、次のことを行う基本的な設定をセットアップする方法について説明します。

S3バケットポリシーの編集

バケットポリシーを付与することで、S3バケットのオブジェクトにアクセスすることができます。

バケット名をクリックします。

アクセス許可を選択します。

バケットポリシーの編集からバケットポリシーを編集します。

以下のバケットポリシーを付与します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::dev-s3-content/*"
        }
    ]
}

AWS公式のActionsリファレンスにどんなことができるのかの内容が記載されているのでこちらを参照すると良いでしょう。

Amazon S3 のアクション、リソース、条件キー

IAM ポリシーで Amazon S3 へのアクセスを管理するために使用できるサービス固有のリソース、アクション、条件キーの一覧を示します。

内容はdev-s3-content/*以下全てのファイルに対してS3からオブジェクトを取得するためのアクセス許可を付与しています。

インデックスドキュメントの設定

上記のバケットポリシーを設定することで、S3へアップロードしたindex.htmlを全世界に公開することができましたが、インデックスドキュメントについて少し触れていきたいと思います。

インデックスドキュメントとはAWS公式ドキュメントでは以下のように説明されています。

バケットに対して静的ウェブサイトホスティングを有効にする場合は、インデックスドキュメントの名前 (index.html など) を入力します。バケットに対して静的ウェブサイトホスティングを有効にした後、インデックスドキュメント名を含む HTML ファイルをバケットにアップロードします。 ルートレベル URL の末尾のスラッシュは省略可能です。たとえば、index.html をウェブサイトのインデックスドキュメントとして設定した場合は、以下の URL のどちらからも index.html が返されます。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/IndexDocumentSupport.html
http://example-bucket.s3-website.Region.amazonaws.com/
http://example-bucket.s3-website.Region.amazonaws.com

フォルダごとアップロードした場合、例えば以下のようなディレクトリ構成でアップロードした場合

hello-world-html/
	├ index.html
	├ css/
		└style.css

以下のURLではindex.htmlは表示できませんでした。

http://example-bucket.s3-website.Region.amazonaws.com/
http://example-bucket.s3-website.Region.amazonaws.com

結論を先に言うと以下のようにディレクトリを指定することで、インデックスドキュメントを探しに行けるようです。

http://example-bucket.s3-website.Region.amazonaws.com/hello-world-html
http://example-bucket.s3-website.Region.amazonaws.com/hello-world-html/index.html

AWS公式ドキュメントには

フォルダ構造をバケット内に作成する場合は、各レベルにインデックスドキュメントが存在している必要があります。各フォルダでは、インデックスドキュメントは同じ名前 (index.html など) であることが必要です。ユーザーが指定した URL がフォルダルックアップに似ている場合は、末尾のスラッシュの有無によって、ウェブサイトエンドポイントの動作が決まります。たとえば、末尾にスラッシュのある次の URL はphotos/index.html インデックスドキュメントを返します。

http://bucket-name.s3-website.Region.amazonaws.com/photos/

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/IndexDocumentSupport.html

上記の通り、

  • フォルダ構造をバケット内に作成する場合は、各レベルにインデックスドキュメントが存在している必要がある。
  • フォルダ内のインデックスドキュメントを探しに行くため、URLの指定は/フォルダ名 まで指定しないとインデックスドキュメントを見つけることができず以下のように404 Not Foundエラーが返ってきます。

CloudFront OAC(Origin Access Control)とOAI(Origin Access Identity)について

OAI (Origin Access Identity)とは

CloudFront OAIはCloudFront経由でリクエストしているビューワに変わってバケット内のファイルにアクセスすることができます。

-> もう少し簡単に言うと”S3バケット上のコンテンツを署名や期限などの複雑な設定を設けず、CloudFrontからアクセスできる”

CloudFront OAIに対してS3バケット内のファイルへアクセス許可を付与する必要があります。

S3バケット内のファイルへのアクセス許可をするためには、以下のバケットポリシーを付与する必要があります。

Principal: "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"

上記を指定することでS3内のファイルにアクセス (このポリシーはGetObjectなので読み取り)することをOAIに委任する、という意味になります。

OAIのIDを確認するためにはCloudFrontコンソールのARNを確認する必要があります。

参考

Amazon S3 オリジンへのアクセスの制限

Amazon CloudFront オリジンアクセスコントロール (OAC) で、Amazon S3 オリジンへのアクセスを制限します。

バケットポリシーの全体は以下です。

{
    "Version": "2012-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<S3 bucket name>/*"
        }
    ]
}

書き込み許可も付与したい場合は、s3:PutObjectをActionに付与することで、書き込みすることが可能です。

{
    "Version": "2012-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::<S3 bucket name>/*"
        }
    ]
}

OAIの特徴

OAIの特徴
  • S3バケット側でAWS Key Management Service(KMS)を使った暗号化、SSE-KMSを使用した場合に対応していないという制限がある
  • S3に対する動的なリクエストがサポートされていない
  • ※AWS KMSとはデータを保護するために暗号化キーを作成し、そのキーを使用して、対象のAWSリソースにアクセス許可を求めるもの

    AWS Key Management Serviceキーで SSE-KMSでサーバー側の暗号を使ってデータを保護します。

    サーバー側の暗号化とは、データを受信するアプリケーションまたはサービスによって、送信先でデータを暗号化することです。

    S3の暗号化について深堀

    S3におけるデータ暗号化について

    ここで少し暗号化に関する話が出てきたので、S3におけるデータ暗号化について深堀していきたいと思います。

    S3にはいくつかの暗号化の仕組みを備えています。

    サーバーサイドの暗号化 (SSE: Server Side Encryption)

    オブジェクトをS3へ保存する前に暗号化します。

    オブジェクトをダウンロードするときに復号します。

    サーバーサイドでの暗号化はデフォルトで設定されます。

    メリット: カスタマー側で暗号化処理の仕組みを実装しなくても済みます。

    クライアントサイドの暗号化 (CSE: Client Side Encryption)

    クライアント側で暗号化し、S3へアップロードします。

    暗号化の仕組みをクライアントサイドで用意する必要があります。

    データが暗号化された状態でネットワーク経路で転送されるため、S3はデータ転送時における暗号化もS3へデータ書き込みする際の暗号化、データにアクセスしてダウンロードする際の復号には関与しません。

    データ転送中に暗号化

    S3の暗号化方法は3つ存在

    SSE-S3: Amazon S3マネージドキーによるサーバー側の暗号化

    AWS公式引用

    Amazon S3 では、Amazon S3 のすべてのバケットに対する基本レベルの暗号化として、Amazon S3 マネージドキーによるサーバー側の暗号化 (SSE-S3) が適用されるようになりました。2023 年 1 月 5 日以降、Amazon S3 にアップロードされるすべての新しいオブジェクトは、追加費用なしで、パフォーマンスに影響を与えずに自動的に暗号化されます。S3 バケットのデフォルト暗号化設定と新しいオブジェクトのアップロードのための自動暗号化ステータスは、AWS CloudTrail ログ、S3 インベントリ、S3 ストレージレンズ、Amazon S3 コンソール、および AWS Command Line Interface と AWS SDK の追加の Amazon S3 API レスポンスヘッダーとして利用できるようになりました。詳細については、「デフォルトの暗号化のよくある質問」を参照してください

    Amazon S3によって管理される一意のキーを使って暗号化します。

    暗号化規格はAES-256 (共通鍵暗号方式 : 対称鍵)を使用して暗号化します。

    x-amz-server-side-encryption: オブジェクトを格納するときに付与したメタデータ (ドキュメントなどを例にすると、最終更新日などの内部データのことで、いつ、誰が、何を、更新したかが分かるようにしておくデータのこと)を表しています。

    この場合、x-amz-server-side-encryptionはヘッダー情報であり、Amazon S3のサーバー側の暗号化を示しています。

    “x-amz-server-side-encryption”:”AES-256”とすることでAmazon S3のサーバーサイドでAES-256という暗号化規格を使って暗号化しますよ、ということを示しています。

    次のバケットポリシーを付与することで、x-amz-server-side-encryption ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。

    {
      "Version": "2012-10-17",
      "Id": "PutObjectPolicy",
      "Statement": [
        {
          "Sid": "DenyIncorrectEncryptionHeader",
          "Effect": "Deny",
          "Principal": "*",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
          "Condition": {
            "StringNotEquals": {
              "s3:x-amz-server-side-encryption": "AES256"
            }
          }
        },
        {
          "Sid": "DenyUnencryptedObjectUploads",
          "Effect": "Deny",
          "Principal": "*",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
          "Condition": {
            "Null": {
              "s3:x-amz-server-side-encryption": "true"
            }
          }
        }
      ]
    }

    実際に上記のバケットポリシーを付与して、何も意識せずにS3へファイルをアップロードすると以下にように、アップロードに失敗します。

    エラー内容はアクセスが拒否されているため、と出ている通り、”s3:x-amz-server-side-encryption": "AES256" を指定していないからですね。

    では、AWS CLIを使用して”s3:x-amz-server-side-encryption": "AES256"を指定してファイルをアップロードしてみましょう。

    マネジメントコンソールからアップロードしたい場合は以下を参照してください。

    Amazon S3 マネージドキーによるサーバー側の暗号化 (SSE-S3) の指定

    Amazon S3 マネージドキー (SSE-S3) を使用したサーバー側の暗号化を Amazon S3 オブジェクトに追加する方法。

    aws s3api put-object --bucket DOC-EXAMPLE-BUCKET1 --key object-key-name --server-side-encryption AES256  --body file path

    --bucket DOC-EXAMPLE-BUCKET1
    --body file path
    上記は自身の環境に置き換えて変更してください。

    --key object-key-name
    上記はファイル名を指定する -> index.htmlの場合はindex.htmlと指定

    上記コマンドを実行すると無事にファイルのアップロードをすることができました。

    SSE-KMS: AWS Key Management Service に保存されているKMSキーによるサーバ―側の暗号化

    "x-amz-server-side-encryption":"aws:kms" ヘッダーを指定します

    次のバケットポリシーを付与することで、"s3:x-amz-server-side-encryption":"aws:kms" ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。

    {
       "Version":"2012-10-17",
       "Id":"PutObjectPolicy",
       "Statement":[{
             "Sid":"DenyUnEncryptedObjectUploads",
             "Effect":"Deny",
             "Principal":"*",
             "Action":"s3:PutObject",
             "Resource":"arn:aws:s3:::DOC-EXAMPLE-BUCKET1/*",
             "Condition":{
                "StringNotEquals":{
                   "s3:x-amz-server-side-encryption":"aws:kms"
                }
             }
          }
       ]
    }

    SSE-C: クライアント (お客さん)が指定したキーによるサーバ側の暗号化

    SSE-Cを使用する場合はAmazon S3はHTTP経由で行われたリクエストは全て拒否されます。

    HTTPSを利用する必要があります。

    次のバケットポリシーを付与することで、"x-amz-server-side-encryption-customer-algorithm" ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。

    {
        "Version": "2012-10-17",
        "Id": "PutObjectPolicy",
        "Statement": [
            {
                "Sid": "RequireSSECObjectUploads",
                "Effect": "Deny",
                "Principal": "*",
                "Action": "s3:PutObject",
                "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
                "Condition": {
                    "Null": {
                        "s3:x-amz-server-side-encryption-customer-algorithm": "true"
                    }
                }
            }
        ]
    }

    OAC (Origin Access Control)とは

    それではOACの話に行きます。

    OAIで制限されていたAWS KMSSSE-KMSのサポートが行われていることです。

    SSE-KMSで暗号化されているS3バケットに対してもデータのダウンロード、アップロードができます。

    サポートしている内容は以下の通りです。

    すべての AWS リージョンのすべての Amazon S3 バケット (2022 年 12 月以降に開始されたオプトインリージョンを含む)

    AWS KMS による Amazon S3 サーバー側の暗号化 (SSE-KMS)

    Amazon S3 に対する動的なリクエスト (PUT と DELETE)

    AWS公式 Amazon S3 オリジンへのアクセスの制限

    OAC handson

    それではCloudFront OACを経由してS3バケット内のオブジェクトにアクセスして、先ほどのindex.htmlを表示する手順をハンズオンしていきましょう。

    S3 handsonの中で全世界へのアクセスを許可していましたが、S3への直接のアクセスを防ぐため、パブリックアクセスを拒否するように戻します。

    バケット名を選択し、アクセス許可のタブを選択します。

    ブロックパブリックアクセス (バケット設定)の編集をクリックします。

    パブリックアクセスを全てブロックにチェックを入れ変更の保存をします。

    CloudFrontディストリビューションを作成します。

    CloudFrontの画面を開き、ディストリビューションの作成をクリックします。

    オリジンドメインは作成したS3バケットを選択します。

    オリジンアクセスをOrigin access control settings (recommended)を選択します。

    Origin access controlからコントロール設定を作成を選択します。

    Create control settingでOACを作成します。

    WAFは今回使用しないのでセキュリティ保護を有効にしないにチェックをいれます。

    これでディストリビューションを作成します。

    作成が完了すると以下のように、S3バケットポリシーを更新する必要があります、というメッセージが表示されるので、ポリシーをコピーをクリックして、S3バケットの権限に移動してポリシーを更新するをクリックします。

    CloudFront OACからS3バケットへアクセスするバケットポリシーを付与します。

    以下のポリシーを付与することで、CloudFrontに対してS3バケット内への読み取りアクセスを許可する権限を付与できます。

    {
            "Version": "2008-10-17",
            "Id": "PolicyForCloudFrontPrivateContent",
            "Statement": [
                {
                    "Sid": "AllowCloudFrontServicePrincipal",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "cloudfront.amazonaws.com"
                    },
                    "Action": "s3:GetObject",
                    "Resource": "arn:aws:s3:::dev-s3-content/*",
                    "Condition": {
                        "StringEquals": {
                          "AWS:SourceArn": "arn:aws:cloudfront::xxxxxxxx:distribution/EISYxxxxxx"
                        }
                    }
                }
            ]
          }

    CloudFront経由でアクセス

    CloudFrontディストリビューション名/ディレクトリ名/index.htmlでアクセスすると、以下のようにアクセスすることができました。

    まとめ

    CloudFront OACを経由してS3バケット内にあるオブジェクトにアクセスする方法についてまとめてました。

    S3にはデータ暗号化に関する部分がかなり奥が深いと感じており、特にSSE-KMSについてはまだまだ理解が浅いと感じておりますので、その点については別途また深堀する記事を書いていきたいと思います。

    また、SSE-KMSを使用したCloudFront OACの作成についても併せて理解を深める必要があると感じております。

    参考書籍

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です