署名付きのなにか。
どうも、やまもとやまです。
前回の記事では、OACによりS3のプライベートなオブジェクトをCloudFront経由で配信できる設定を行いました。
今回はいよいよ本命の署名付きCookieで、認証によるコンテンツへのアクセス制御を実現します。
設定手順
署名付きCookieを利用するためにPEM形式のキーペアが必要となるため、事前に用意しましょう。
適当な手元のLinuxサーバーで、以下のコマンドで秘密鍵、公開鍵のペアを作成します。
※SSH-2 RSA、base64でエンコードされた2048ビットのキーペアが利用の要件となります
$ openssl genrsa -out private_key.pem 2048
$ openssl rsa -pubout -in private_key.pem -out public_key.pem
作成したキーペアの公開鍵を、AWSマネージメントコンソールで設定します。
CloudFront ⇒ パブリックキー ⇒ 「パブリックキーを作成」ボタンをクリック。
任意の名前を入力し、キー欄には先ほど作成した公開鍵を入力します。
作成するとIDが割り振られます。
さて、下準備が完了したのでアクセス制御の設定に移ります。
前回S3バケットにはsample.htmlとusagi.jpgの2つのオブジェクトをアップロードしました。
そこで、拡張子jpgはそのままアクセスを許可し、拡張子htmlの場合は制限してみます。
設定箇所としては、対象CloudFrontディストリビューションのビヘイビアですね。
CloudFront ⇒ ディストリビューション ⇒ 前回作成したディストリビューション ⇒ ビヘイビア ⇒ 「ビヘイビアを作成」ボタンをクリック。
設定は以下になります。
・パスパターン:*.html
・オリジンとオリジングループ:前回作成したオリジンのS3バケット
・ビューワーのアクセスを制限する:Yes
・信頼された認可タイプ:Trusted key groups (recommended)
「キーグループを追加」項目のところに「キーグループを作成」リンクがあるので、クリックすると別ウインドウでキーグループ作成ページが開きます。
名前に任意の名前を入力し、パブリックキーで先ほど作成した公開鍵を選択しましょう。
元の画面でキーグループが選択できるようになるので、選択してやります。
以上でビヘイビアが作成され、拡張子htmlのファイルは署名付きCookieや署名付きURL使用でなければアクセスできない状態となります。
試しにブラウザからアクセスしてみましょう。
予想通り怒られました。
一方、jpg画像は変わらず表示できています。
さて、後は正常なCookieを指定してリクエストしてやれば表示できるはずです。
設定するべきCookieですが、既定ポリシーの場合とカスタムポリシーの場合で異なります。
ポリシーに含むURLが既定ポリシーでは単一となるため、今回はワイルドカードも利用できるカスタムポリシーで設定することにします。
※カスタムポリシーでは送信元IPアドレスの指定も可能ですが、今回は行いません
なお、設定するCookieはそれぞれ以下になります。
既定ポリシーの場合:CloudFront-Expires CloudFront-Signature CloudFront-Key-Pair-Id の3つ
カスタムポリシーの場合:CloudFront-Policy CloudFront-Signature CloudFront-Key-Pair-Id の3つ
カスタムポリシーの各Cookieの内容は次のものを指定します。
CloudFront-Policy:空白文字や改行を削除し、base64エンコードされたjson形式のポリシーステートメント
CloudFront-Signature:base64エンコードされ、事前準備で作成した秘密鍵で署名されたポリシーステートメント
CloudFront-Key-Pair-Id:事前準備で作成したパブリックキーのID
というわけで、Cookieに必要な文字列を用意します。
まずはCloudFront-Policyですが、元になるjson形式のポリシーを用意します。
$ vi policy
{
"Statement": [
{
"Resource": "http://testexample.cloudfront.net/*",
"Condition": {
"DateLessThan": {
"AWS:EpochTime": 1672921362
}
}
}
]
}
Resourceには実際の対象となるURLを、EpochTimeは有効期限となる時刻をUnixtimeで指定してください。
用意したポリシーから空白文字や改行を削除し、base64エンコード後にURLクエリパラメータで無効な文字を削除すると、Cookie用の文字列となります。
$ cat policy | tr -d "\n" | tr -d " \t\n\r" | openssl base64 -A | tr -- '+=/' '-_~'
eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QzNzBva2EybHVpNXQ1LmNsb3VkZnJvbnQubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NzI5MjEzNjJ9fX1dfQ__
次にCloudFront-Signatureですが、同様にポリシーから署名を行い、base64エンコードにより生成します。
$ cat policy | tr -d "\n" | tr -d " \t\n\r" | openssl sha1 -sign private_key.pem | openssl base64 -A | tr -- '+=/' '-_~'
ohVRbLuEVk3V5cR9~6MCrOZ7BeBTB5kWrnD-fWYkMzXflUBxnV-2TGzXNgOE6sDoqWncICD5oqWP83OU5Acl-sTeWU68pPRr0PpvXc7nWxSP8QFzbCbtonlBgH~14xAhgeVSedTxPjJEUYMVhRD3QyrA6A7-05T1CTfeFpoUb3aQ-9kEIdbI8zYj1i-szv1PivK9Tx3ae~XyvnVSy84AEwFoeQZw7QsttaFC76vGhz9VhH67~BHOJJsR6FvmpMp~lu8ZjzKbzj8tpyNUuDMiNcuT7E55GI1U-8X-RAcyWPSsZsRt8IPZyx0H58QgYynzInTa3l~e1iflv0LYuB7z-w__
最後のCloudFront-Key-Pair-IdはIDそのままの文字列です。
さて、Cookieをセットせずにcurlでアクセスするとこうなります。
当然エラーですね。
$ curl http://d370oka2lui5t5.cloudfront.net/sample.html
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MissingKey</Code><Message>Missing Key-Pair-Id query parameter or cookie value</Message></Error>
では、Cookieをセットしたらどうなるでしょうか・・・
$ curl -H 'Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2QzNzBva2EybHVpNXQ1LmNsb3VkZnJvbnQubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NzI5MjEzNjJ9fX1dfQ__;CloudFront-Signature=ohVRbLuEVk3V5cR9~6MCrOZ7BeBTB5kWrnD-fWYkMzXflUBxnV-2TGzXNgOE6sDoqWncICD5oqWP83OU5Acl-sTeWU68pPRr0PpvXc7nWxSP8QFzbCbtonlBgH~14xAhgeVSedTxPjJEUYMVhRD3QyrA6A7-05T1CTfeFpoUb3aQ-9kEIdbI8zYj1i-szv1PivK9Tx3ae~XyvnVSy84AEwFoeQZw7QsttaFC76vGhz9VhH67~BHOJJsR6FvmpMp~lu8ZjzKbzj8tpyNUuDMiNcuT7E55GI1U-8X-RAcyWPSsZsRt8IPZyx0H58QgYynzInTa3l~e1iflv0LYuB7z-w__; CloudFront-Key-Pair-Id=K13BWTZ3RQOZ3G' http://d370oka2lui5t5.cloudfront.net/sample.html
<html><head></head><body>sample page</body></html>
(分かりにくいですが)無事表示されました!
とりあえず満足した
本記事では簡単ではありますが、署名付きCookieによる、CloudFront経由でのコンテンツへのアクセス制御の動作を確認することができました。
簡単のためcurlでのテストを行いましたが、実際はアプリケーションでログイン成功時にCookieをセットし、ブラウザからアクセスするような形になると思います。
何かしらのフレームワークを利用すればすぐに実装できると思いますので(フレームワークとかなくてもPHPあたりならさくっとできますね)、試してみてはいかがでしょうか。
それではまた!