Note
カスタムポリシーを利用した記事もあります。
https://qiita.com/sugimount-a/items/53febd43e4cad7ffdb54
はじめに
Web アプリケーションやモバイルアプリなどを提供する際に、画像などの素材を Amazon Simple Storage Service (Amazon S3) に格納したものを配信できます。セキュリティやパフォーマンスを考慮して、以下の 2 点を実現していきたいユースケースがあります。
- S3 に格納したファイルは、一部のユーザーに限定して配信したい
- たくさんのユーザーがいるため、CloudFront を使った配信をしたい
上記を満たすために、CloudFront の署名付き URL や 署名付き Cookie が活用できます。S3 を Private Bucket にしたうえで、CloudFront の署名付き URL や 署名付き Cookie を生成することで、CloudFront のスケーラビリティを活かしながら、一部のユーザーに限定したアクセスが提供できます。Web アプリケーション側で、何らかの認証機能を利用しながら、ログイン済みユーザーに限定して署名付き URL ・署名付き Cookie を発行する仕組みを作り上げることで、実現頂けます。
次の構成図に、概要図を載せています。
- ユーザーは、Web アプリケーションに備わっている認証機能でログインを行う
- Web アプリケーションはリクエストに応じて、画像データなどに紐づく CloudFront 署名付き URL を生成
- Web アプリケーションは、ユーザー側に署名付き URL をレスポンス
- ユーザは、署名付き URL にアクセスすることで、Private な S3 Bucket に格納されている画像データなどを取得
概要図では、Web アプリケーション側に秘密鍵を持たせて署名付き URL を生成していますが、API Gateway + Lambda を活用したサーバーレス構成の選択肢が有ります。サーバーレス構成は運用負担の軽減といったメリットがあります。
2種類の署名付き URL
署名付き URL は、セキュリティに関するポリシーの違いによって、「規定ポリシー」と「カスタムポリシー」の 2 種類あります。2 つのポリシーの違いを AWS Document に書かれている表から抜粋します。規定ポリシーは、デフォルトの設定になっており、手を加えない分比較的楽に生成できます。一方、カスタムポリシーは自分たちのワークロードに合わせたカスタマイズが可能で、柔軟にコントロールができます。カスタムポリシーの方は、複数のオブジェクトのために再利用出来たり、アクセス元の IP アドレスの制限が出来るため、便利に利用できそうですね。
今回の記事では、規定ポリシーを使った署名付き URL を生成する方法を確認していきます。
秘密鍵・公開鍵の作成
署名付き URL を生成するために、秘密鍵・公開鍵の生成が必要です。手元の Linux マシンなどで作成していきましょう。
mkdir ~/temp/cloudfront-presign/
cd ~/temp/cloudfront-presign/
秘密鍵を生成します
openssl genrsa -out private_key.pem 2048
秘密鍵から、CloudFront に登録するための公開鍵を生成します
openssl rsa -pubout -in private_key.pem -out public_key.pem
こんな感じに生成できました
> ls -lha
total 12K
drwxr-xr-x 2 ec2-user docker 51 Feb 25 20:18 .
drwxrwxr-x 36 ec2-user docker 4.0K Feb 25 19:12 ..
-rw-r--r-- 1 ec2-user docker 1.7K Feb 25 20:18 private_key.pem
-rw-r--r-- 1 ec2-user docker 451 Feb 25 20:18 public_key.pem
CloudFront に公開鍵をアップロード
CloudFront のページに移動して、Create public key を押します
公開鍵の中身を確認して、登録をしていきます
cat public_key.pem
登録されました。
CloudFront に Key Group を作成
Key Group を作成して、アップロードした公開鍵を登録します
名前や公開鍵の名前を選択します
作成されました
S3 Bucket の作成
適当な名前で Private な S3 Bucket を作成して、うちの猫の写真をアップロードします。この写真を、署名付き URL のアクセス確認に使っていきます。
CloudFront の Distribution の作成
署名付き URL を利用するために、Create distributon を押します。
- Distribution の Origin として、作成した S3 Bucket を指定
- OAI を利用して、S3 Bucket のアクセスを CloudFront のみに制限
Restrict viewer access を有効にして、署名付き URL が無いとアクセスできないように設定します
Create Distribution を押します
Deploy プロセスが走りました
署名付きURLの生成
今回は、AWS CLI を使って CloudFront の署名付き URL を生成していきましょう。実際の環境では、SDK を使ったプログラムから署名付き URL を生成すると思います。次の Document にサンプルコードがあるので参考にできます。
aws cloudfront sign \
--url https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg \
--key-pair-id K1DYHGI8XWLXZL \
--private-key file:///home/ec2-user/temp/cloudfront-presign/private_key.pem \
--date-less-than 2022-02-25T20:50:00+09:00
実行例
> aws cloudfront sign \
--url https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg \
--key-pair-id K1DYHGI8XWLXZL \
--private-key file:///home/ec2-user/temp/cloudfront-presign/private_key.pem \
--date-less-than 2022-02-25T20:50:00+09:00
https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg?Expires=1645789800&Signature=lteo65gt~7K2UFPyG9KEOhIEkTYid2Zn9IKG6Ync605wwnygvo~dUWjwX~N~8BmGiAPnMavtADL-mf-24aGRQa~xRSgLm6tPvoyqeX6rSptXvkrPMqtQ9HOiaHPMY8PuzMkj3CQLb-0O53CwD5HJNUeQbAers6IFvxeJoEgtWTn1YE8J1umezTxHlsHuRl7Eq0soT8iktFXaRKE0AQFjYvT5UvmkgShA2j7eqtcIW~mmKHT2LQ5XSSxztiZj5OWzFXLyET9va0Amj2t7~EOxA21n~7AWamrmU9xNCKO5bnuz31dV4L5fKSVQdxl5jFddXk6~qyxDP2r26JgXtYFMJA__&Key-Pair-Id=K1DYHGI8XWLXZL⏎
アクセス確認
通常のアクセス
まず、通常通り、署名付き URL なしで CloudFront にアクセスしてみます。想定通り、エラーになりました。
署名付き URL でアクセス
AWS CLI で生成した URL にアクセスしてみます。S3 Bucket にアップロードした、家猫の写真が見えました。かわいい。
有効期限切れ
署名付き URL の有効期限が切れた後にアクセスしてみた結果です。これも想定通りエラーになりました。
検証を通じてわかったこと
- 秘密鍵・公開鍵を生成して、CloudFront に公開鍵をアップロードする必要がある
- 署名付き URL を生成するために、秘密鍵を持っているコンピュートリソース (EC2, Lambda, ECS, App Runner など) を用意する方式が考えられる
参考URL
AWS Document : 署名付き URL の使用
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html
Python のサンプルコード
https://github.com/boto/boto3/blob/develop/boto3/examples/cloudfront.rst
【AWS】CloudFrontで署名付きURLの設定方法(プライベートコンテンツの配信)
https://zenn.dev/may_solty/articles/807dbad3a30de8