Node.jsでCloudFront+S3の署名付きURLを発行する

2021.07.23

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

吉川@広島です。4連休は自宅で過ごします。

案件においてCloudFrontで署名付きURLを発行したい、というシチュエーションは結構ありそうですが、実は自分はこの設定を経験したことがありません。

いずれ来るであろう機会に備えて素振りしてみることにしました。

S3バケットを作成+サンプルファイルをアップロード

まずはバケットを作成します。

バケット名だけ決めて、後はデフォルト値で作成します。

バケットを作成したら、動作確認用の画像をアップロードします。今回は下記のサイトから150×150.pngというファイルをダウンロードして使用しています。

Placehold.jp|ダミー画像生成 モック用画像作成

CloudFront Distributionを作成

CloudFrontのCreate Distributionから作成していきます。

Origin Domainで先程作成したS3バケットを選びましょう。

S3 bucket accessはYes use OAIを選択し、Create new OAIから新しいOAIを作成します。

任意の名前を入力します。

Bucket PolicyはYes, update...を選択します。

Viewer protcol policyをRedirect HTTP to HTTPSに設定します。

この設定でDistributionを作成します。完了まで少し待ちましょう。

作成が完了したら、一旦動作確認してみます。

// CloudFront
https://{SUB_DOMAIN}.cloudfront.net/150x150.png

CloudFront URLにアクセスするとアップロードした画像ファイルが表示されました。

// S3
https://{BUCKET_NAME}.s3.ap-northeast-1.amazonaws.com/150x150.png

S3のURLから見ようとすると下記のようにエラーになるのが期待値です。

CloudFront用のPublicKey PrivateKeyを作成

CloudFront用のキー作成にあたって採る方法ですが、

[アップデート] root ユーザー作業が不要に!Amazon CloudFront で署名付き URL/Cookie 向け公開鍵を IAM ユーザー権限で管理できるようになりました。 | DevelopersIO

今後は信頼されたキーグループを推奨

ということでこちらの方法を採用していきます。

手元の端末(自分はMacです)で下記のコマンドを実行します。

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

すると下記の2つのキーが作成されます。

  • private_key.pem
  • public_key.pem

こちらのPublicKeyの方をAWSにアップロードします。

CloudFrontの左メニューから、Public keysを選択します。そしてCreate Public Keyから作成していきます。

  • Name: 任意
  • Key: 先程作成したPublicKeyの内容をペースト

で作成しましょう。

続いてまた左メニューを確認し、Key groupsを選択します。そしてCreate key groupから作成します。

Public keysは上で作成したpublic keyを紐付けます。

CloudFront Behaviorを編集

ここからは今作成したpublic keyとDistributionを紐付けていきます。

作成済のCloudFront Distributionを編集します。Distributionを選択し、さらにBehaviorを選択します。この状態でEditを押下します。

Restrict viewer accessをYesにして、Trusted key groupsを選択します。そして、先程作成したkey groupを追加します。

以上でWebコンソールの設定は完了です。

Node.jsで署名付きURLを発行する

aws-sdk v3を使う方法 (現状厳しい)

現時点のaws-sdk v3では、v2のAWS.CloudFront.Signerクラスに相当する機能が用意されていないようです。

Create CloudFront signed URL · Issue #1822 · aws/aws-sdk-js-v3

そのため、sdk v3で実現するのは厳しいと感じました。

aws-sdk v2を使う方法

sdk v2はSignerクラスがあるので実装可能です。

サンプルコードはこちらの記事が参考になりそうでした。

Node.jsでAmazon CloudFrontの署名付きURLを生成する - Qiita

aws-cloudfront-signを使う方法 (一番手軽)

最後にaws-cloudfront-signというパッケージを使う方法です。

Creating Amazon CloudFront Signed URLs in Node.js | AWS Developer Tools Blog

jasonsims/aws-cloudfront-sign: Utility module for AWS CloudFront

紹介した方法の中で一番記述量が少なく手軽だったため、今回はこちらを採用しました。

ただ注意点として、TypeScriptの型定義がないため、anyを許容するか、自分で型定義を書くしかなさそうです。

環境

  • Node.js 15.11.0
  • typescript 4.3.5
  • aws-cloudfront-sign 2.2.0

コード

// main.ts

// @ts-ignore
import { getSignedUrl } from 'aws-cloudfront-sign'

const main = async () => {
  const signedUrl = getSignedUrl(
    'https://{SUB_DOMAIN}.cloudfront.net/150x150.png',
    {
      keypairId: '{PUBLIC_KEY_ID}',
      privateKeyPath: '/path/to/private_key.pem',
    }
  ) as string

  console.log(signedUrl)
}

main()

keyPairId はpublic keysのIDを入力しましょう。画面でいうと下記になります。

以下の出力が得られればOKです。

https://{SUB_DOMAIN}.cloudfront.net/150x150.png?Expires=1627019755&Policy=...
✨  Done in 0.78s.

実際アクセスして画像が表示されることも確認できました。

参考