S3互換のCloudflare R2で署名付きURLを発行する(AWS CLI, Python + Boto3)

2023.01.05

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

どうも、ベルリンオフィスの小西です。

S3 互換のオブジェクトストレージ Cloudflare R2 では、S3 同様に署名付き URL をサポートしており、今回はそちらを試してみたいと思います。

先に書いておくと、作業は S3 に対して行うのとほぼ一緒です。S3互換ってそういうことかという感じで読んでもらえれば。

R2 署名付き URL でできること

署名付き URL を作成することで、バケット全体やオブジェクトを完全公開することなく、URL と署名を知る一部のユーザーのみに対し、期限付きでバケットやオブジェクトのアクセス・操作許可を付与することができます。

例えば、

  • 一部のユーザーに、1 時間のみ限定のオブジェクトダウンロード URL を配布する
  • 一部の管理者に、24h 有効なオブジェクトアップロード URL を配布する

R2 バケットの準備

R2バケットの作成方法については以前に下記で紹介していますので、そちら参考にしていただければと思います。

作成したバケットはプライベートのままにしておいてください。

Cloudflare R2 に HTTP アクセスログをプッシュして API で取得するまで

ダウンロード用の署名付き URL を発行する by AWS CLI

R2 操作には下記の AWS SDK がそのまま利用できます。

今回は AWS CLI で R2 バケットの署名付き URL を発行してみたいと思います。

1. R2 の API トークンの生成

以前の記事でも紹介していますが、トークン作成は下記の流れになります。

  1. Cloudflare ダッシュボードで R2 に移動する
  2. ダッシュボード右側 [Manage R2 API Tokens] に移動
  3. [Create API token] をクリックする
  4. [Token name] を編集する
  5. [Permissions] で [Edit] 選択する
  6. [Create API Token] をクリックする

2. AWS CLI の準備

AWS の CLI を使いますので、まだの人はこちらを参考にダウンロードしてください。

AWS アクセスの設定を行います。キー情報は先ほど Cloudflare R2 側で生成したトークンを入力します。

$ aws configure
aws configureAWS Access Key ID : <ACCESS_KEY_ID>
AWS Secret Access Key : <ACCESS_KEY_SECRET>
Default region name : auto
Default output format : json

3. 署名付き URL の作成

CLI では、オブジェクトダウンロード用 URL 発行は下記のコマンドになります。

$ aws s3 presign --endpoint-url https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com  s3://<R2_BUCKETT_NAME>/<R2_OBJECT_PATH> --expires-in 3600

例えば下記の例は、対象オブジェクトに対し 3 分(= 180秒)の有効期限付きURLが発行できます。

$ aws s3 presign --endpoint-url https://142411ba14a3fd8e6dce641de1152c07.r2.cloudflarestorage.com s3://sample/sample.pdf --expires-in 180

上記を実行すると、署名パラメータが付与されたURLが返ってきます。

https://142411ba14a3fd8e6dce641de1152c07.r2.cloudflarestorage.com/sample/sample.pdf?X-Amz-Algorithm...

3分後にアクセスすると想定通り、期限切れのエラー(403)が返ってきます。

<Error>
<Code>ExpiredRequest</Code>
<Message>Request has expired</Message>
</Error>

ここで発行する “署名” は、指定したオブジェクトと指定したアクションに限定されます。

試しにアクセス先のオブジェクトパスのみ変更し、それ以外のパラメータは同様の URL にアクセスすると、403 の SignatureDoesNotMatch というエラーコードが返され、オブジェクトは閲覧できません。

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your secret access key and signing method. </Message>
</Error>

有効期限について

署名付き URL には有効期限の設定が必要です。

タイムアウト期間は 1 秒から 7 日(= 604,800秒)の間で設定可能です。

署名付き URL のパラメーターとして、URLの生成時刻(例: X-Amz-Date=20230104T130633Z )とタイムアウト(例: X-Amz-Expires=180)が含まれ、これらを改ざんしても同様に SignatureDoesNotMatch の 403 が返ります。

独自ドメインの利用

R2 ではバケットに対して独自ドメインを充てることができます。

署名付き URL の発行時に独自ドメインを指定したところ、URL自体は発行されましたが、アクセスすると 404 エラーが返ってきました。

利用は Cloudflare R2 の URL でのみ利用できる点も注意が必要です。

アップロード用の署名付き URL を発行する by Python + Boto3

AWS CLI ではアップロード用の署名付き URL は発行できません(知らなかった)。

せっかくなので Python + boto3 を使ってアップロード用の署名付き URL を発行してみます。

Python に boto3 のインストール

$ pip install boto3

Pythonのコード

まず試しに下記を実行し、R2 のバケット一覧が返ってきたら成功です。

import boto3

r2 = boto3.resource('s3',
  endpoint_url = 'https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com',
  aws_access_key_id = '<ACCESS_KEY_ID>',
  aws_secret_access_key = '<ACCESS_KEY_SECRET>'
)
for bucket in r2.buckets.all():
    print(bucket)

次に署名付き URL を実際に生成するコードです。

import boto3

BUCKET = 'test-konishi'
KEY = 'sample' #任意のオブジェクトキー

s3 = boto3.client('s3',
      endpoint_url = 'https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com',
      aws_access_key_id = '<ACCESS_KEY_ID>',
      aws_secret_access_key = '<ACCESS_KEY_SECRET>'
)

response = s3.generate_presigned_url(
    ClientMethod = 'put_object',
    Params = {'Bucket' : BUCKET, 'Key' : KEY},
    ExpiresIn = 300,
    HttpMethod = 'PUT')

print(response)

上記を実行すると URL が返ってきます。

https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com/<R2_BUCKETT_NAME>/<R2_OBJECT_PATH>?X-Amz-Algorithm...

上記に対してオブジェクトを投稿してみます。

$ echo This is a sample. > sample.txt
$ export URL="<先ほど発行されたURL>"
$ curl -D - -X PUT --upload-file sample.txt $URL

成功すると下記のような返り値になります。 ServerAmazonS3 ではなく cloudflare になってますね。

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Wed, 04 Jan 2023 14:41:04 GMT
Content-Type: text/plain;charset=UTF-8
Content-Length: 0
Connection: keep-alive
ETag: "9b9af6945c95f1aa302a61acf75c9bd6"
x-amz-version-id: 56c671e83d23484da97eca5b325571d2
Server: cloudflare
CF-RAY: 7844bba4dd4f2c45-FRA

Cloudflare R2 のダッシュボードに行くと、無事オブジェクトが PUT されていました。

Cloudflare R2

以上、やりかたは S3 のそれと一緒でした。

Python + Boto3 で署名付き URL を生成する詳しい方法については、下記記事で紹介しています。

Boto3でS3のpre-signed URLを生成する

そのほかの留意点

  • Public になったバケットのオブジェクト URL は Cloudflare アカウント ID とバケット名を含まない一方、署名付き URL はバケット保有者の アカウント ID とバケット名を含むようになり、それらが公になる点に留意が必要です。(とはいえこれらが漏れたところで、通常の運用範囲内であれば直接的な影響はないかと思います)

最後に

以上、特にダウンロード URL の生成は非常に簡単に行えることが見ていただけたかと思います。また同様の制限を Workers で実装する例もドキュメントで紹介されています。

クラスメソッドは Cloudflare のパートナーになっています。ご興味のある方、お困りの方は是非一度クラスメソッドにご相談ください。

参考