はじめに
前回の記事では、CloudFront の署名付きURL(規定ポリシー)を使って、Private の S3 Bucket に格納している画像データを、安全性を高めたアクセス方法を確認しました。
今回の記事では、より柔軟なアクセスポリシーを定義できるカスタムポリシーを利用した方法を確認していきましょう。
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(カスタムポリシー)の生成
カスタムポリシーを利用した署名付き URL は、AWS CLI では生成できず、何らかのプログラムで生成する必要があります。AWS Document にサンプルコードが記載されているため、これを参考にするとよいでしょう。
今回の記事では、Python を使った署名付き URL の生成を行います。ソースコードはこんな感じです。
- Python の実行環境上で秘密鍵
/home/ec2-user/temp/cloudfront-presign/private_key.pem
が存在している - 署名付き URL の対象となるパス
https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg
を指定 - 署名付き URL の有効期限を
expire_date
で指定 - アクセス元の IP アドレスを
54.250.23.0/32
と指定 (とある EC2 インスタンスのもの)
import datetime
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from botocore.signers import CloudFrontSigner
from dateutil import tz
def rsa_signer(message):
with open('/home/ec2-user/temp/cloudfront-presign/private_key.pem', 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())
key_id = 'K1DYHGI8XWLXZL'
url = 'https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg'
expire_date = datetime.datetime(
2022, 2, 25, 22, 10, 0, 0, tz.gettz('Asia/Tokyo'))
cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)
# Create a signed url that will be valid until the specific expiry date
# provided using a canned policy.
policy = cloudfront_signer.build_policy(
url, expire_date, date_greater_than=None, ip_address='54.250.23.0/32')
signed_url = cloudfront_signer.generate_presigned_url(
url, date_less_than=None, policy=policy)
print(signed_url)
この Python コードを実行すると、署名付き URL を生成できます
> python cloudfront-create-presign.py
https://d31z1elfcc2o1s.cloudfront.net/mycat.jpg?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9kMzF6MWVsZmNjMm8xcy5jbG91ZGZyb250Lm5ldC9teWNhdC5qcGciLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDU4ODEwMDB9LCJJcEFkZHJlc3MiOnsiQVdTOlNvdXJjZUlwIjoiMC4wLjAuMC8wIn19fV19&Signature=RmBo1DqAyEMSQWvMgubzHs2zufS03rceMEIvv8JHkQaMyQDtuu3aLTDSyQ9eO4Oyp~lkB2QrC-KM8v21eo9i0IRWFsbY5IreNpOqo5sLc8RWL9LpKWo6~EM1ykD29SCp8fObkLBe9ed85lNLqml2kDztHCV7ebYvU7fBUGHLnsQ4n4NZjRpipDfZdMJbhjx0DWfpp5VmaKIGh0XwZxMeNbR8vMIUEKn1DHLcyLLywkzyFZaI146NvekpDgS-VqMmxBIRVOjxhXc3p8JvPoTdAEYW3Dxp7Cit~Xd1xNoO9cn6GiFQUDn9uWD1fETSzASilj8NbpIzpJXU6S1n0I57XQ__&Key-Pair-Id=K1DYHGI8XWLXZL
アクセス確認
まず、通常通りに CloudFront 経由でアクセスを試みると、想定通りにエラーになります。
次に、署名付き URL で、許可された IP アドレスからアクセスすると、正常に家猫の画像が出ます。かわいい。
署名付き URL の 有効期限が切れたときの結果です。想定通りエラーになります。
IP アドレスを制限するための WAF
署名付きURLのカスタムポリシーでは、アクセス元の IP アドレスが指定可能ですが、1個までという制限があります。Web アプリケーションとして認証を加えたうえで、アクセス元のクライアント IP を取得し生成することで、URL が仮に漏れたとしてもアクセスを防ぐ構成も可能です。
上記とは別の方法で、WAF を利用した 複数の IP アドレス制限を加えることも可能です。WAF の構成もしてみましょう。Create IP set を押します。
アクセスを許可する IP アドレスを複数入力します。
IP set が作成されました。
WAF の Web ACL を作成します。
適当に名前を入れて、CloudFront に紐づけます。
Add my own rules and rule groups を選択します。
作成した IP set を選択して、Allow を選びます
ルールに合致しないものは Block とすることで、指定された IP アドレス以外を防ぎます
Next
Next
Create を押します
WAF の Web ACL が作成されました
アクセス確認
WAF でブロックしている IP アドレスから、署名付き URL にアクセスしてみると、想定通りアクセスができません。WAF としてのエラーとなっており、カスタムポリシーとは異なったエラーが表示されます。
検証を通じてわかったこと
- カスタムポリシーでは、アクセス元の IP アドレスを指定可能
- ただし、カスタムポリシー上では 1 個のみ指定可能
- IP アドレスを複数利用して制限したい場合は、WAF を利用した IP アドレス制限が検討可能
付録 : ワイルドカードで複数のファイルを配信
参考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