背景
CloudFrontで公開してるコンテンツですが、パブリックではなく、別にホストしてるログインが必要なWebサイトのコンテンツあるいはURLを知ってるユーザーからのみアクセスできるようにするための方法です。
そんな時に使えるのがCloudFrontの署名付きURLや署名付きCookieです。
ログインが必要なサイト ―(署名付きURL)→ CloudFront ----- S3
署名付きURLの仕組みは署名付き URL を使用する - Amazon CloudFrontで説明されています。
署名付きURLには既定ポリシーとカスタムポリシーがあります。違いは上記AWSドキュメントサイトで解説されていますが、今回は既定ポリシーとすることにしました。
鍵関連の作成・登録
(余談)WSLインストール
署名付きURLをつかうにはOpenSSLで秘密鍵や公開鍵を作成する必要がありますし、署名付きURLを作成する際にはPerlをつかうと楽です。
3流PGのメイン環境はWindowsですが、上記を行うにあたりLinux環境のほうがサクッとさぎょうできます。でも、仮想マシンとかたてるの面倒だなーと思っていましたが、今はWSLというWindows上で直接Linuxを実行できるサブシステムがあります。今回はWSLを使うことにしました。(WSLにはWSL1とWSL2がありますが、今回WSL2となります。)
WSLのインストールは簡単で以下のコマンドだけです。
wsl --install
/etc/os-releaseファイルを見ると、インストされたディストリビューションは Ubuntu 22.04.3 LTS のようです。
なお、WSLやカーネルのバージョンはWindowsターミナルから以下コマンドで確認できます。
wsl --version
WSL バージョン: 2.3.24.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.65
MSRDC バージョン: 1.2.5620
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26100.1-240331-1435.ge-release
Windows バージョン: 10.0.22631.4169
秘密鍵と公開鍵の作成
上記WSL環境のLinuxで、以下のコマンドで秘密鍵と、そこから公開鍵を生成します。
$ openssl genrsa -out aws_private_key.pem 2048
$ openssl rsa -pubout -in aws_private_key.pem -out aws_public_key.pem
なお、WSLの場合、Windows側からは以下のような共有フォルダパスでアクセスできます。
\\wsl.localhost\Ubuntu\home\user01\
CloudFrontでパブリックキーを登録
CloudFrontの[パブリックキー]→[パブリックキーを作成]から、上記で作成した公開鍵を登録します。
CloudFrontに キーグループ を作成
CloudFrotの[キーグループ]→[キーグループを作成]から、上記で作成したパブリックキーを選択してキーグループを作成します。
CloudFront側で署名付きURLまたは署名付きCookieのみアクセスするよう制限
CloudFront → [ディストリビューション] → 対象のディストリビューション → 対象のビヘイビア にて、[ビューワーのアクセスを制限する]を有効にし、キーグループに上記で作成したキーグループを設定します。
これで、署名付きでないURLでCloudFrontの公開サイトにアクセスすると以下のようなメッセージが表示されるようになります。
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>MissingKey</Code>
<Message>Missing Key-Pair-Id query parameter or cookie value</Message>
</Error>
ここまでで、CloudFront側の設定は完了です。
署名付きURLの作成
次に、署名付きURLを作成します。
AWS CLIを使う方法もあるようですが、うまくいかなかったので、Perl を使ってURL署名を作成することにしました。
PHPやC#、Java等を使う場合でもAWSドキュメントにはサンプルコードがあります。
Perlのサンプルコードは、AWSドキュメント 署名付き URL を作成する Perl スクリプトのソースに掲載されています。
書式は以下のような感じです。
--url: CloudFrontの該当ファイルへのURL
--private-key: 上記手順の秘密鍵ファイル
--expires: URL によるファイルへのアクセスの許可を停止する日付と時刻。エポックタイム。
--key-pair-id: CloudFrontでパブリックキーを登録した際のID
(なお、上記項目は必須です。最初、--expires を付けずにやったらURLは生成できたのですが、Must include policy filename with --policy argument or an expirestime using --expires と怒られ、実際にアクセスも不可でした。)
$ perl ./create_signurl_perl.pl --action encode --url https://xxxxxxx.cloudfront.net/index.html --private-key ./aws_private_key.pem --expires 2524575600 --key-pair-id K2XXXXXXXXX
ただ、自分のWSLの環境でPerlスクリプトを実行するといくつかエラーがでたので、その内容と対応をメモしておきます。
エラー:libfile-slurp-perl パッケージが無いとのこと。
Can't locate File/Slurp.pm in @INC (you may need to install the File::Slurp module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.34.0 /usr/local/share/perl/5.34.0 /usr/lib/x86_64-linux-gnu/perl5/5.34 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.34 /usr/share/perl/5.34 /usr/local/lib/site_perl) at ./create_signurl_perl.pl line 95.
BEGIN failed--compilation aborted at ./create_signurl_perl.pl line 95.
対応:リポジトリを更新し、不足しているパッケージをインストールします。
$ sudo apt update
$ sudo apt install libfile-slurp-perl
エラー:URIパッケージが入ってないとのこと。
Can't locate URI.pm in @INC (you may need to install the URI module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.34.0 /usr/local/share/perl/5.34.0 /usr/lib/x86_64-linux-gnu/perl5/5.34 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.34 /usr/share/perl/5.34 /usr/local/lib/site_perl) at ./create_signurl_perl.pl line 100.
BEGIN failed--compilation aborted at ./create_signurl_perl.pl line 100.
対応:個別でURIパッケージを入れてもよかったんですが、パッケージマネジャーである cpanm があったほうがよさげだったので、cpanm を入れてから、cpanm上でURIをインストールすることに。
$ sudo apt install cpanminus
$ cpanm URI
これで、create_signurl_perl.pl を走らせたURLでアクセスするとHTMLが表示されました。
しかし、ここで注意点が。。。
htmlファイルに記載してる同じCloudFront上の各リソースは読み込まれません。
なので、HTMLファイルから参照する全てのリソースについても、署名付きURLを発行してリンクを書き換える必要があります。
以下のような感じです。
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="none">
<link rel="stylesheet" type="text/css" href="https://xxxx.cloudfront.net/style.css?Expires=2524575600&Signature=xxxxxxxxxxx_&Key-Pair-Id=xxxxxxx">
↑を署名付きURLにする必要がある。
<title>テストサイト</title>
</head>
<body>
<p>hello world</p>
<img src="pic.jpg" alt="" />
</body>
</html>
あまりきれいではないですよね。
もっといい方法があるのかもしれませんが、今回はとりあえずこれで。
参考:
Amazon CloudFront の署名付きURL(既定ポリシー)を使って、安全にコンテンツを配信してみた