CloudFrontを利用する際、アプリを介してのアクセス以外はコンテンツ配信出来ないようにしたい
という要望があります。
調べてみたところ、CDP(クラウドデザインパターン)にPrivate Cache Distributionパターンがありました。
上記によると、CloudFrontに対して署名付きURLを送信することで、アクセス条件を制限することが
できるようです。
今回はこちらを使用してみました。

○S3バケットの作成

まずはS3バケットをひとつ用意し、ファイルをアップします。

アップしたファイル(index.html)は以下のような内容になります。



private


This is Private Contents



○CloudFrontディストリビューションの作成

次にCloudFrontでディストリビューションを追加します。
「Create new Distribution」をクリックします。
そうすると、以下のようにディストリビューションの設定画面が表示されますので、以下のように入力します。

・Origin Settings

  • Origin Domain Name:先ほど追加したS3バケットを選択
  • Origin ID:自動で設定されるので今回はこのまま
  • Restrict Bucket Access:Yes(YesにするとCloudFrontからしかS3にアクセスできないようにできます)
  • Origin Access Identity:Create a New Identity(S3にアクセスするこのCloudFrontの接続子です)
  • Comment:コメントです
  • Grant Read Permissions on Bucket:Yes(S3に対してCloudFrontからのBucketPolicyを設定します。)

・Default Cache Behavior Settings

  • PathPattern:Default
  • View Protocol Policy:HTTP and HTTPS
  • Object Caching:Use Origin Cache Headers(Customizeにすると次の3項目が設定できます。)
  • Minimum TTL:0(最短TTL、デフォルト24時間)
  • Forward Cookies:None(キャッシュURLにCookieを含めることができます)
  • Whitelist Cookies:Forward Cookiesを有効にすると、Whitelist内のCookieだけをオリジンに転送することができます。
  • Forward Query Strings:No(YesにするとQueryParameter付きURLでキャッシュできます)
  • Restrict View Access:Yes(署名付きURLでしかアクセスできないようにします)
  • Trusted Signers:Self (Specify Accoutsにチェックを入れると別アカウントの署名も有効になります。)

・Distribution Settings

こちらの項目はひとまずデフォルトのままにしておきます。

ここまで設定できたら「Create Distribution」をクリックします。

そうすると以下のように、CloudFrontをprivateアクセスするために必要な次の手順が表示されます。

簡単に訳すと以下のような内容になります。

Step1:S3バケットへのアクセス制限

  • ディストリビューションを新規作成したままS3の設定を変更していないのであれば、
    S3バケットはCloudFrontとバケットオーナーからしかアクセスを受け付けないように正しく設定されています。

Step2:署名付きURL

  • 信頼された署名者用にCloudFrontのキーペアを作成します。
  • 署名付きURLを作成するには、コーディングするかサードパーティツールを使用する必要があります。
  • ディストリビューションへ信頼された署名者を追加します。

上記より、自動でS3のパーミッションが変更されたようです。
S3のパーミッションを確認してみます。
S3のバケットのPermissionsから[Edit Bucket Policy]をクリックします。
そうすると以下のように設定されています。

{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E366VCXL4Q6CB9"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::memorycraft-private/*"
}
]
}

Cloud Front Origin Access Identityとして設定されています。
また、作成されたディストリビューションを確認すると、以下のように無事登録されていることが確認できます。
ディストリビューションのホスト名は「dapubd7a26puj.cloudfront.net」となっています。

この段階でブラウザでアクセスすると以下のようになります。

上記は、すでに署名付きURLしか受け付けておらず、通常のURLではエラーになるためこのような表示となっています。

○署名付きURLの作成

次に署名付きURLを作成してみたいと思います。

まず、署名をするためのCloudFront用のキーペアを取得します。
AWSの証明書のページで「一対の鍵」のタブをクリックします。

CloudFrontの一対の鍵という項目があるので、「新しい一対の鍵を作成する」をクリックします。

作成完了のモーダルウィンドウが表示されますので、確認して閉じます。
そうすると、「一対の鍵」にCloudFront用のキーペアが追加されていることを確認できます。
「一対の鍵ID」というキーペアIDが表示され、下にダウンロードリンクが現れます。

「(公開鍵をダウンロード)」をクリックすると「rsa-キーペアID.pem」、「pk-キーペアID.pem」というファイルが
ダウンロードされます。

任意の環境にSDKをダウンロードします。
ここでは、PHPのSDKを利用します。
また、keysというディレクトリを作り「pk-キーペアID.pem」を配置します。

$tree -L 2 .

.
├── app
│ ├── key.php
│ ├── sdk -> sdk-1.6.0
│ ├── sdk-1.6.0
│ └── sdk-latest.zip
└── keys
└── pk-キーペアID.pem

そして、以下のようにAmazonCloudFrontクラスを使って署名付きURLを生成します。

cat key.php

require_once('sdk/sdk.class.php');
date_default_timezone_set('Asia/Tokyo');

//CloudFrontクラスを初期化
$cf = new AmazonCloudFront(array('key'=>'通常のAWSアクセスキー', 'secret'=>'通常のAWSシークレットキー'));

//キーペアIDをセット
$cf->set_keypair_id('CloudFrontのキーペアID');

//配置した鍵ファイルの中身をセット
$cf->set_private_key(file_get_contents(dirname(__FILE__).'/../keys/pk-キーペアID.pem'));

//index.htmlのURLを期限付きで取得します(expireはstrtotimeが解釈できる文字列かUnixTimeならOK)
$url = $cf->get_private_object_url('dapubd7a26puj.cloudfront.net', 'index.html', strtotime('+5 minutes'));
echo $url;

○確認

実行します。

# php app/key.php

http://dapubd7a26puj.cloudfront.net/index.html?Expires=1360671363&Key-Pair-Id=APKAIGNAXKEEXGUYEZ5A&Signature=UQantZ1gIp5-mm6d6Lb-HKQr1jxKDRW2NfFyC-E2dDx33ekBbFjLKmO6vHCOhv7liBrfcaTFc7~yKZCd4P1tfZE4TltU6TilhnAUFT6mCC-Db-dmnrU7XbcWSjt29~yOVVhLTv5RoPtIjoW~iPFR2BTyoM~4vlV40y9ypbO5M1U_

URLが出力されましたので、こちらを利用してアクセスしてみます。

上記の通り表示されました。

先ほどのコードでは5分後がアクセス期限となっています。
そこで、5分以上経過してからアクセスしてみると以下のような結果となりました。

想定通りアクセスが拒否されました。

○おまけ

ユーザーがアクセスするごとにURLの期限を設定したい場合があります。
その場合は、以下のように元のURLにクエリーパラメータを付与するといいかもしれません。

$url = $cf->get_private_object_url('dapubd7a26puj.cloudfront.net', 'index.html?u='.session_id(), strtotime('+5 minutes'));

以上の手順の多くはAWSのAPI経由でも設定が可能ですが、必要に応じてプログラムで設定しても
よいかもしれません。
また、署名の仕方や署名ポリシーにはいくつかパターンがあるため、機会があれば他の方法も
試してみたいと思います。

こちらの記事はなかの人(memorycraft)監修のもと掲載しています。
元記事は、こちら