こうこく
作 ▸

AWS CloudFrontの署名付きURLを使ってみる (+ AWS CLIでCloudFrontの署名付きURLを作る)

CloudFrontの機能で、一定時間のみS3バケットに対してPUT/GET可能なURLを作って使ってみます

Ubuntu 20.04aws-cli/2.4.9 Python/3.8.8 Linux/4.19.128-microsoft-standard exe/x86_64.ubuntu.20 prompt/off
もくじ

手順1. キーペアを作成

以下コマンドで公開鍵 public_key.pem と秘密鍵 private_key.pem を作成。

openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

公開鍵の中身は -----BEGIN PUBLIC KEY----- で始まる文字列。秘密鍵の中身は -----BEGIN RSA PRIVATE KEY----- で始まる文字列。

以下の記事を参考にさせていただきました。

【AWS】CloudFrontで署名付きURLの設定方法(プライベートコンテンツの配信) - Qiita

手順2. CloudFrontにパブリックキーを登録

CloudFrontの画面 → 左メニューの『パブリックキー』 → 右上の『パブリックキーを作成』ボタンを押す。

名前は任意のものを入力し、キーは public_key.pem の中身をコピペする。

パブリックキーを作成
パブリックキーを作成

作成が完了したら、パブリックキー一覧の『ID』列の値 (K始まり14桁) をメモしておく。

手順3. CloudFrontのキーグループを作成

CloudFrontの画面 → 左メニューの『キーグループ』 → 右上の『キーグループを作成』ボタンを押す。

名前は任意のものを入力する。パブリックキーは先程作成したものを選択する。

キーグループを作成
キーグループを作成

手順4. S3バケットを作成

パブリックアクセス不可のS3バケットが必要なので作成する。

「パブリックアクセスをすべてブロック」がチェックONならば、それ以外の設定はデフォルトでOK。(現時点では全部デフォルトでいいはず)

手順5. CloudFrontディストリビューションを作成

CloudFrontの画面 → 左メニューの『ディストリビューション』 → 右上の『ディストリビューションを作成』ボタンを押す。

以下の通りに入力してディストリビューションを作成する。記載が無い箇所は任意でよい。

オリジン

  • オリジンドメイン … 先程作成したS3バケットのドメインを選択
  • オリジンパス … 任意、このサンプルでは空欄
  • S3バケットアクセス … 「はい、OAIを使用します (バケットは CloudFront のみへのアクセスとなるように制限できます)」
  • オリジンアクセスアイデンティティ … 「新しいOAIを作成」ボタンを押して任意の名前で作成
  • バケットポリシー … 「はい、バケットポリシーを自動で更新します」

OAIを使用することで、パブリックアクセス不可のバケットにCloudFront経由でアクセスできるようになる。併せてバケットポリシーの方でOAIのアクセスを許可する必要があるが、バケットポリシーを自動更新するよう選択しておけば自動でやってくれる。

オリジン
オリジン

デフォルトのキャッシュビヘイビア

  • ビューワープロトコルポリシー … 任意、ここでは「HTTP and HTTPS」
  • 許可されたHTTPメソッド … ここでは「GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE」
  • ビューワーのアクセスを制限する … 「Yes」
  • 信頼された認可タイプ … 「Trusted key groups (recommended)」
  • キーグループを追加 … 先程作成したキーグループを選択
  • キャッシュキーとオリジンリクエスト … 「Cache policy and origin request policy (recommended)」
  • キャッシュポリシー … 任意、ここでは「CachingDisabled」
  • オリジンリクエストポリシー … 任意、ここでは未設定

「ビューワーのアクセスを制限する」は過去に「Restrict Viewer Access」と呼ばれていたもので、Yesにすると署名付きURL以外でのアクセスを拒否するようになる。Yesにしないと署名付きURLの意味が無いのでYesにする。

許可されたHTTPメソッドで「GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE」を選択すると、CloudFrontの署名付きURLでファイルのGETだけでなくPUTも行えるようになる。ただしバケットポリシーに手動で設定を追加する必要があるので、次項の手順を必ず行うこと。ファイルのGETしか行わない場合は「GET, HEAD」でよい。

キャッシュとオリジンリクエストの設定は任意だが、動作確認するならキャッシュは無効化しておいた方が楽かも。オリジンリクエストを設定するなら、リクエストヘッダ Host を転送しないように注意。詳しくは下記の記事を参照。

CloudFrontのS3 Originにはhostヘッダーを転送してはいけない - KAYAC engineers' blog

オリジン
オリジン

手順6. バケットポリシーを修正 (PUTを行う場合)

S3の画面 → CloudFront経由でアクセスするバケット → 上部の『アクセス許可』タブを押す。ページ中央上部あたりに表示されているバケットポリシーは以下の通りのはず。

自動で更新されたバケットポリシー
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {OAIのID}"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{バケット名}/*"
        }
    ]
}

これは前項のCloudFrontディストリビューション作成時に設定した「はい、バケットポリシーを自動で更新します」による設定で、OAIによるアクセスを許可している。

ただし Actions3:GetObject しか許可されていないので、このままだとCloudFront経由でのPUTが行えない。Access Denied と言われて弾かれてしまう。

なのでバケットポリシー右上の『編集』ボタンを押し、以下の通りバケットポリシーの Action を配列に変更して s3:PutObject を追加して保存する。

修正後のバケットポリシー
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {OAIのID}"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::{バケット名}/*"
        }
    ]
}

以上で準備は完了。

動作確認

ここではAWS CLIの aws cloudfront sign コマンドを使ってCloudFrontの署名付きURLを作り、ファイルのGETとPUTを行ってみる。

sign — AWS CLI 2.4.9 Command Reference

署名付きURLを作るときは、オプション url でアクセス先のURLを、key-pair-id でキーペアIDを、private-key で秘密鍵のファイルパスを、date-less-than で有効期限を指定する必要がある。

以下のコマンドは、CloudFront経由のURL https://dxxxxxxxxxxxxx.cloudfront.net/example.txt にキーペア KXXXXXXXXXXXXX で署名した署名付きURLを発行するもの。このコマンドは公開鍵 private_key.pem が存在するディレクトリで実行すること。オプション date-less-than は、ここではUNIX時間で現在日時の1時間後を指定している。

※公開鍵 private_key.pem が存在するディレクトリで実行すること
aws cloudfront sign \
  --url https://dxxxxxxxxxxxxx.cloudfront.net/example.txt \
  --key-pair-id KXXXXXXXXXXXXX \
  --private-key file://./private_key.pem \
  --date-less-than $((`date "+%s"` + 3600))

成功すると、以下のような署名付きURLが作成される。

https://dxxxxxxxxxxxxx.cloudfront.net/example.txt?Expires=1641769534&Signature=HT3ETQw-1JjlS3YkNWseiinefvfXU6-s~Sal3kgJ7XOq21zOVy-KE-mzkNaHMsTa6~8XESX7raP62cc5Y27HqUaVjAF3RC75rXiO1CpXU3muVHKi3aTcW7~utjxG3yxL1fbH-v4QsoK3sk-3Dmt67RoSj4TDrLEqsPCARNg4Yd3mn98D8Xx8j86DTd0S5l1EKrUkXRJhhBzcVGcWzVXD1a57qG80ZCl2~UOb8PAYClltCF24z~NNb05wbxUF6Y2FDb2eeahbtScL7djNlHsQQNp-tjG-hVNEqnHmWYSK0qL4~0oXeceqqB4y-aSkLJIEbmIS6B~xNugPlXlWiC~I2w__&Key-Pair-Id=KXXXXXXXXXXXXX

では、署名付きURLの期限が切れる前に動作確認を行っていく。

以下コマンドで、動作確認用のテキストファイル piyopiyo を作成して署名付きURLにPUTする。署名付きURLは、署名に記号がいっぱい含まれてるのでダブルクォーテーションで囲うこと。

署名付きURLにPUT
echo 'ぴよぴよ' > piyopiyo

curl -i -X PUT \
  -H 'Content-Type: text/plain' \
  -H 'X-Amz-Acl: bucket-owner-full-control' \
  --data-binary @piyopiyo \
  "{署名付きURL}"

ステータスコード200で成功した場合、S3バケットにオブジェクト example.txt が作成されているはず。作成されるオブジェクト名は、ローカルのファイル名に関わらず署名付きURLの発行時にURLとして指定したものになる。

リクエストヘッダに X-Amz-Acl: bucket-owner-full-control を付与してるのは、オブジェクトの所有者をバケットに移すことで、他のプログラムから当該オブジェクトを操作できるようにするため。この記事ではコマンドラインからしか操作しないので、実際のところ必須ではないけど、知ってた方がいいのでつけておいた。詳しくは以下のページを参照。

S3 のオブジェクトの所有権を使用したアップロードされたオブジェクトの所有権の管理 - Amazon Simple Storage Service

次に以下コマンドで、当該ファイルをGETして表示してみる。

署名付きURLからGET
curl --silent -X GET --output piyopiyo_get "{署名付きURL}"

cat piyopiyo_get

そしたら最初にPUTしたファイルの内容と同じ ぴよぴよ が表示されるはず。

以上で動作確認は完了。


なお、有効期限切れ後の署名付きURLにアクセスすると、ステータスコード403と以下のレスポンスボディが返却される。

CloudFrontの署名付きURLの有効期限切れレスポンスボディ
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access denied</Message></Error>
この記事に何かあればこちらまで (非公開)