ちょっと話題の記事

CloudFront+S3で署名付きURLでプライベートコンテンツを配信する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

S3をWebコンテンツの置き場所として使う場合、Webアプリケーション側でそのS3上のコンテンツに対するPre-signed URLを生成することで、Webアプリケーションで認証されたユーザに限りコンテンツにアクセス可能とするような仕組みを作ることは良くあります。

zu1

ただしこのS3アクセス用として生成したPre-signed URLは、CloudFrontを経由した形では使えません。

zu2

CloudFront経由で、限られたユーザのみS3からコンテンツを取得出来るようにするためには、CloudFront用の署名付きURLを発行する必要があります。

zu4

そこで今回は、CloudFront+Amazon S3を組み合わせて、署名付きURLを使った制限されたコンテンツ(プライベートコンテンツ)の配信を試してみました。

やってみる

今回試したことは、以下のAWSのドキュメントを参照しながら行っています。

オリジンアクセスアイデンティティを設定する

CloudFrontの署名付きURLを使ってコンテンツへのアクセスを制限する場合、CloudFrontを介さずに直接S3にアクセスされては意味がありません。このため「CloudFront経由に限りS3バケットにアクセス出来ることとし、CloudFrontを経由しない場合はS3バケットにアクセス出来ない」ように設定します。詳しくは以下のドキュメントを参照して下さい。

設定は、CloudFrontのディストリビューションの[Origin]設定から行います。[Restrict Bucket Access]を"Yes"に設定します。[Origin Access Identity]で"Create a New Identity"とすることで、新規にオリジンアクセスアイデンティティが作成されます。また[Grant Read Permissions on Bucket]を"Yes, Update Bucket Policy"とすると、S3バケットのバケットポリシーを書き換えてくれます。

cfa1 するとオリジンとなっているS3バケットのバケットポリシーが以下のように書き換えられます。

cf2なお、元々何らかのバケットポリシーを設定していた場合、そのポリシーは削除されず残ったままとなります。以下ドキュメントからの引用です。

CloudFront は、指定されたオリジンアクセスアイデンティティにバケット内のオブジェクトの読み取り許可を付与するようにバケット許可を更新します。ただし、CloudFront は既存の許可を削除しません。現在、Amazon S3 URL を使用してバケット内のオブジェクトにアクセスするための許可を持っているユーザーは、CloudFront がバケット許可を更新した後もその許可を依然として持っています。

このため、既にバケットポリシーを設定していた場合には該当箇所を削除し、オリジンアクセスアイデンティティからのみアクセス可能なようにしておきましょう。

この設定を行うことで、S3へ直接アクセスしてもコンテンツが参照できなくなっています。直接参照した場合には以下のようなエラーが帰ってきます。

403 Forbidden

Code: AccessDenied
Message: Access Denied
RequestId: XXXXXXX
HostId: XXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXX
An Error Occurred While Attempting to Retrieve a Custom Error Document

Code: AccessDenied
Message: Access Denied

署名付きURLを発行するためのCloudFrontキーペアを作成する

署名付きURLを発行するには、CloudFrontキーペアを作成する必要があります。なお、CloudFrontキーペアはIAMユーザには作成することが出来ません。このためAWSアカウント自体でCloudFrontキーペアを作成する必要があります。詳しくは以下のドキュメント参照のこと。

AWSアカウントでマネージメントコンソールにログインし、画面右上のアカウント名をクリックして、ドロップダウンから[Security Credentials]をクリックします。

cfk01

[Your Security Credentials]画面が表示されますので、[CloudFront Key Pairs]を選択します。

ckf02

[Create New Key Pair]をクリックします。

ckf03

Key Pairが作られますので、[Download Private Key File][Download Public Key File]をクリックして、2つの鍵ファイルをダウンロードしておきます。

ckf4

CloudFrontキーペアが作成されます。

ckf05更に、このCloudFrontキーペアの所有者(AWSアカウント)を、CloudFrontのディストリビューションに紐つける必要があります。CloudFrontのディストリビューションの[Behavior]設定で、[Restrict Viewer Access]を"Yes"にします。CloudFrontキーペアの所有者がそのCloudFrontのディストリビューションを作成しているAWSアカウント自体であれば[Trusted Signers]で"Self"にチェックします。別のAWSアカウントなのであれば"Specify Accounts"をチェックし、該当のAWSアカウント番号を入力します。

cf02これで、署名付きURLを受け付ける準備は出来ました。

署名付きURLを発行しアクセスする

では署名付きURLを発行してみます。詳しい説明は以下のドキュメントを参照。

今回はPerl を使用して URL 署名を作成する - Amazon CloudFrontの通り、Amazon CloudFront Signed URLs helper toolを使って生成してみます。なおPerl以外の言語については以下のドキュメントを参照して下さい。

helper tool (cfsign.pl)は以下のような形で使います。詳しくは--help参照。

$ perl ./cfsign.pl --action encode --url http://xxxxxxxxx.cloudfront.net/docment.txt?response-content-disposition=attachment%3B%20filename%3Ddoc.txt --expires 1408819738 --private-key ./pk-APKAXXXXXXXXXXXXXXX.pem --key-pair-id APKAXXXXXXXXXXXXXXX

すると以下のように署名付きURLが発行されます。

Encoded URL:
http://xxxxxxxxx.cloudfront.net/docment.txt?response-content-disposition=attachment%3B%20filename%3Ddoc.txt&Expires=1408819738&Signature=ABCDEFG1234567890&Key-Pair-Id=APKAXXXXXXXXXXXXXXX

で、この署名付きURLに対してWebブラウザでアクセスすると、該当のコンテンツが取得出来ます。このツールは独自ドメインにも対応していますので、urlのところをhttp://www.example.com/のようにCloudFrontに割り当てている独自ドメインのURLとしても、ちゃんとコンテンツのダウンロードが出来ます。

ということで、CloudFront+S3で署名付きURLでプライベートコンテンツを配信することが出来ました!

まとめ

ここに至るまで結構ハマりました...しかしコンテンツサイズが大きい動画や画像などの配信の場合、やはりCloudFrontを使いたい場合は多いかと思います。署名付きURLを使うことで、CloudFrontで最適なコンテンツ配信を行いつつ、制限を保持することが可能ですので、様々なシステムで活用出来るのでは無いでしょうか。