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

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


やりたいこと

  • 認証が必要なWebアプリケーションをAWS上に構築する


    • Amazon S3で画像ファイルを管理する
    • 画像ファイルには認証済みユーザーのみがアクセス可能
    • 独自URLで公開する
    • 当然SSL化する
いろいろとググったところ、Amazon CloudFront署名付きURL (signed url) を使うと目的を達成できそう。

環境整備が面倒くさかったので手順を整理しておきます。

この記事では 署名付きURL の作成方法について取り上げます。Webアプリケーションの構築については範囲外です


手順


リソース管理用のS3バケットを準備する

画像ファイルを格納するS3のバケットを作成します。

今回は cloudfront-private-content という名前にしました。

テストのために cat.jpgdog.jpg をバケットにアップロードします。


CloudFrontでS3バケットを公開設定する

CloudFrontを迂回してアクセスできないように設定します。
Step 2: Create distribution の画面で以下の設定を行います。


Origin Settings

項目 設定値
Origin Domain Name 作成したS3バケットを指定
Restrict Bucket Access Yes
Origin Access Identity Create a New Identity
Grant Read Permissions on Bucket Yes, Update Bucket Policy


Default Cache Behavior Settings

項目 設定値
Viewer Protocol Policy Redirect HTTP to HTTPS
コンソールの CloudFront Distributions にて status が In Progress から Deployed に変わるまで結構時間がかかります。

ゆっくりコーヒーを飲む時間くらいはかかるので、余裕を見て作業しましょう。


CloudFrontへ独自ドメインでアクセスする

Route 53 で先程作成した CloudFront Distribution へのaliasを作成します。

まず、CloudFrontのコンソールで Domain Name をコピーしておきます。
xxxxxxxxxxxxx.cloudfront.net

つづいて、Route53で Aレコード を追加します。

項目 設定値
Name private-content.example.com
Type A - IPv4 address
Alias Yes
Alias Target xxxxxxxxxxxxx.cloudfront.net
ふたたび CloudFrontのコンソールに戻り、Alternate Domain Names (CNAMEs) を更新します。

リストから作成したDistributionを選択し、Distribution Settingsボタンをクリックします。
GeneralタブのEditボタンをクリックします。
Alternate Domain Names (CNAMEs) にRoute53で登録したAレコードのNameを入力します。
Yes, Editボタンをクリックし、変更を保存します。


独自ドメインをSSL化する

また Distribution Settings > General > Editボタンをクリックします。
SSL CertificateCustom SSL Certificate (example.com): を選択したいところですが、まだアクティブになっていないと思います。(選択可能な証明書があればアクティブになる)
AWS Certificate Manager で証明書を作成し登録します。

Request or Import a Certificate with ACMボタンをクリックします。
AWS Certificate Managerが開きます。このときリージョンが バージニア北部 となっていることを確認してください。(そうでなければ変更する)

ドメイン名は *.example.com として続行します。

今回、ドメインの管理もRoute53で行っているのでCNAMEの登録による確認で証明書が発行されます。

ここでも検証が完了するまで少し時間がかかりますが、状況発行済み になるまで何度かリロードしてください。

(私の環境では、以降のCloudFrontのコンソールでなかなか証明書が一覧に表示されず、しばらく悩みました。

経験上、AWSコンソールでの作業はひとつづつ確実にこなすのが良いです。)

CloudFrontのコンソールに戻り、SSL CertificateCustom SSL Certificate (example.com): が選択可能であればチェックしてください。

(私の環境では一旦Editをキャンセルし、ブラウザのリロードを行い、再度Edit画面に戻ると選択可能になっていました。)

Custom SSL Certificate (example.com): のテキストボックスにフォーカスを合わせると、AWS Certificate Managerで発行した証明書が表示されますので、選択してください。

Yes, Editボタンをクリックし、変更を保存します。


キーペアの作成

署名付きURLを生成するための公開鍵、秘密鍵、アクセスキーIDを取得します。

AWSコンソールのヘッダー右上、ユーザー名の部分をクリックして セキュリティ認証情報 を選択します。

ダイアログが表示される場合はContinue to Security Credentialsボタンをクリックして続行します。

CloudFrontのキーペア にて 新しいキーペアの作成 をクリックし、公開鍵と秘密鍵をダウンロードします。

また、CloudFrontのキーペアアクセスキーID が表示されるので、これもコピーしておきます。


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

やっと本題。

今回は署名付きURLを生成する機能だけを実装します。

適当にNode.jsのプロジェクトフォルダを作成し、AWS-SDKとその他のパッケージをインストールします。

(その他のパッケージは署名付きURLの生成に直接関与するものではありません。AWS-SDKだけが必須です。)

$ mkdir cloudfront-secret-content 
$ cd cloudfront-secret-content 
$ npm init -y 
$ npm install --save aws-sdk dotenv fs-extra moment 
以下のように、コンソールに生成したURLを出力するコードを作成します。

index.js
require('dotenv').config(); 
const AWS = require('aws-sdk'); 
const fs = require('fs-extra'); 
const moment = require('moment'); 
 
// 対象となるリソースのURL 
const target = 'https://private-content.example.com/cat.jpg'; 
 
/** 
 * AWS.CloudFront.Signer.getSignedUrlを呼び出す。 
 * 
 * Class: AWS.CloudFront.Signer — AWS SDK for JavaScript 
 * https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront/Signer.html 
 * @param {String} keypairId 
 * @param {String} privateKey 
 * @param {Object} options  
 */ 
function getSignedUrlAsync(keypairId, privateKey, options) { 
  return new Promise((resolve, reject) => { 
    // Signerインスタンスを生成 
    const signer = new AWS.CloudFront.Signer(keypairId, privateKey); 
    // URL生成 
    signer.getSignedUrl(options, (err, url) => { 
      if (err) { 
        reject(err); 
      } 
      resolve(url); 
    }); 
  }); 
} 
 
async function main() { 
  // private keyを読み込む 
  const privateKey = await fs.readFile(process.env.PRIVATE_KEY_FILE, { encoding: 'utf-8' }); 
 
  // 期限を設定 
  // 現在日時から1日後まで有効とする 
  const expires = moment.utc().add(1, 'days').unix(); 
 
  // URL生成 
  const url = await getSignedUrlAsync( 
    // キーペアのID 
    // AWSコンソールの以下の場所で確認可能 
    // セキュリティ認証情報 > CloudFront のキーペア > アクセスキーID 
    process.env.KEYPAIR_ID, 
    // 秘密鍵を渡す 
    privateKey, 
    { 
      // 対象となるCloudFrontのURL 
      url: target, 
      // 生成されるURLの期限 (UTCのunixtime) 
      expires 
    } 
  ); 
 
  console.log(url); 
} 
 
main().then(() => { 
  console.log('done.'); 
}).catch((err) => { 
  console.error(err); 
}); 
秘密鍵の保存場所とアクセスキーIDを .env ファイルに記述します。

.env
KEYPAIR_ID=xxxxxxxxxxxxxxxxxxxx 
PRIVATE_KEY_FILE=./secret/pk-xxxxxxxxxxxxxxxxxxxx.pem 
実行すると以下のような結果が返ります。

$ node index.js 
https://private-content.example.com/cat.jpg?Expires=1540535260&Key-Pair-Id=XXXXXXXXXXXXXXXXXXXX&Signature=..... 
done. 
返されたURLにブラウザからアクセスし、画像が表示されることを確認してください。

また、QueryString部分をカットして 403 が返されること、cat.jpgdog.jpg に変更してもアクセスできないことを確認します。



参考

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2024-02-12 22:08:06 RSSフィード2024-02-12 22:00分まとめ(7件)