AWS

S3+CloudFrontの静的ウェブサイトにBASIC認証を掛けてみた

はじめに

S3の静的ウェブサイトホスティングはPHPなどを用いない静的ウェブサイトを公開するには非常に安価で便利なサービスです。

しかし、

  • ステージングサイト
  • 資料配布サイト

など、特定の人だけに閲覧を許可したい場合に、簡単な認証方法としてはBASIC認証を掛けるのが一般的ですが、それがS3の静的ウェブサイトホスティングで行えないか調べて検証してみました。

今回のウェブコンテンツ

イベント参加者専用の資料配布サイトを作成しました。
websim.aiで大枠を生成し、一部カスタマイズを加えています。
今回は検証用なので、PDFのダウンロードリンクはダミーです。

イベント参加者のみが閲覧できるように、今回は「BASIC認証」を掛けることを目指します。

S3だけではBASIC認証をサポートしていない

S3の静的ウェブサイトホスティングについては、過去に記事を執筆したので、こちらを参照お願いします。
S3の静的ウェブサイトホスティングで簡単なウェブページを公開してみた
S3の静的ウェブサイトホスティングで公開したウェブページに独自ドメインを設定してみた

一般的な方法は「.htpasswd」と「.htaccess」を使う方法です。
.htpasswd」は事前にIDとパスワードを決め、下記のような暗号化ツールで暗号化し、それを「.htpasswd」に記載するだけで完成です。
https://www.luft.co.jp/cgi/htpasswd.php

一方、「.htpasswd」は下記のような決まり文句です。

AuthType Basic
AuthName "Input your ID and Password."
AuthUserFile {.htpasswdのフルパス}
require valid-user

.htpasswd」のパスはディレクトリ(フォルダ)を含めたフルパスでなければ機能せず、S3ではサーバレスであり、パスはキー、ファイルはバリューで表し、フォルダの概念が存在しないため、この時点でサポートしていないことが分かります。

ACMでSSL証明書を発行する

CloudFrontで独自ドメインを使用するためには、SSL証明書を取得し、ACM(AWS Certificate Manager)に設置することが必須です。
AWSの公式ドキュメントでは下記ページに詳しく書かれています。
代替ドメイン名と HTTPS を設定する - Amazon CloudFront

なお、ACMでAmazonのSSL証明書を発行する場合は無料なので、AWSの各サービスでSSL証明書を設置したい場合には非常に便利です。
なので、まずはACMでSSL証明書を発行します。

CloudFrontで使用するSSL証明書は、必ず米国東部 (バージニア北部)リージョン(us-east-1)に置いている必要があります。

日本では一般的に東京リージョン(ap-northeast-1)を使っていることが多いと思いますが、ACMにアクセスしたら、まずはリージョンを米国東部 (バージニア北部)リージョン(us-east-1)に変更します。

「リクエスト」ボタンをクリックし、「パブリック証明書」をリクエストします。

*.nakkun.jp」のSSL証明書を発行します。

今回の記事でも、私の過去の記事と同じく「nakkun.jp」のサブドメインで検証を行います。
今回は今後の利便性を考えてワイルドカード証明書を発行しますが、使用するドメイン、サブドメイン専用のSSL証明書を発行しても構いません。

検証方法は、Route 53にホストゾーンがあるならば、「DNS検証」が簡単なので、それを選択しましょう。
後は、キーアルゴリズムは「RSA 2048」のままで「リクエスト」をクリックしてリクエストします。

次に検証(認証)を行います。

「Route 53」にホストゾーンがあるドメインならば、「Route 53でレコード作成」をクリックします。

Route 53にレコードの追加が可能ならば「レコードを作成」ボタンが表示されるのでクリックします。

Route 53に検証用のCNAMEレコードが自動で追加されるので、後は発行されるまで待つだけです。

「発行済み」になったら有効なので、証明書が利用できます。

ACMでSSL証明書を発行できたので、次のステップへ行きます。

S3バケットを作成する

S3バケットの作成

次にS3バケットを作成します。
S3バケットのリージョンには縛りは無いので、主に日本で利用するウェブページの場合は東京リージョン(ap-northeast-1)に設定して作成すると良いです。

今回はCloudFrontを使い、アクセスをCloudFront経由に限定するので、S3バケットは外部には公開する必要がありません。
「パブリックアクセスをすべて ブロック」にチェックを入れたままで、バケット名を入力して後はそのままで作成します。

今回は、S3はウェブコンテンツを置くための箱、ストレージとして使用すると考えれば良いと思います。

ウェブコンテンツのアップロード

S3バケットに事前にウェブコンテンツをアップロードします。

今回はこのコンテンツをアップロードしました。
トップページは「index.html」になるため、必須です。
背景の画像は「images」内に入れています。

CloudFrontディストリビューションを作成する

オリジン

オリジンの設定を行います。

「Origin domain」は先程作成したS3バケットを選択します。
そうすると、名前にオリジンドメイン名が自動的に入力されます。

オリジンアクセスは、現在推奨とされている「OAC (Origin access control)」を使用するため、「Origin access control settings (reccomended)」を選択します。

既に「Origin access control」の設定を持っている場合はそれを選択すれば良いのですが、持っていない場合は「Create new OAC」からその場で作ることができます。

デフォルトキャッシュビヘイビア

「デフォルトキャッシュビヘイビア」は今回は特に設定を変更しないので、デフォルトのまま進めます。

関数の関連付け、WAF

「関数の関連付け」と「ウェブアプリケーションファイアウォール (WAF)」は特に設定を行いません。

「ウェブアプリケーションファイアウォール (WAF)」は利用しない場合も「セキュリティ保護を有効にしないでください」にチェックを入れる必要があるので入れましょう。

設定

ここで、他の詳細な設定を行います。

料金クラスは基本的に日本で使うことを想定しているので「北米、欧州、アジア、中東、アフリカを使用」を選択します。

「代替ドメイン名(CNAME)」に今回使用するドメイン名を入力します。

Cunstom SSL certificateでは、先程ACMで取得したSSL証明書を選択します。
米国東部 (バージニア北部)リージョン(us-east-1)で取得し、正常に検証(認証)できているならば、プルダウンに表示され、選択できるようになっています。

デフォルトルートオブジェクトは一般的にトップページに表示する「index.html」を入力し、説明は分かりやすいように今回使用するドメイン名を入力しておきます。

ここまでできたら、「ディストリビューションを作成」します。

S3バケットにバケットポリシーを追加

CloudFrontがS3にアクセスするための許可が必要なので、バケットポリシーを追加します。
設定後、「ポリシーをコピー」のボタンがポップアップで出てくるので、そのままクリックしてコピーし、先程のS3バケットに設定します。

ディストリビューションドメインでウェブ確認

ここまでの設定が正しければ、ディストリビューションドメイン名にウェブアクセスすれば、S3バケットにアップロードしたウェブコンテンツが正常に見られるようになります。

なお、DNSレコードは未登録なので、現時点で独自ドメインでこのCloudFrontにアクセスすることはできません。

Route 53にAliasレコードを追加

Route 53のホストゾーンにレコードを追加します。

ここまでの設定が正しければ、Aliasレコードで今回作成したCloudFrontディストリビューションを選択できるので、選択します。

独自ドメインでウェブ確認

ここまで無事完了すれば、独自ドメインでウェブアクセスできるようになります。

なお、今回はSSL証明書を設置しているので、HTTPSでアクセスするとSSLが有効になっています。

BASIC認証を掛ける

いよいよS3+CloudFrontで作成したウェブページにBASIC認証を掛けます。
今回は、CloudFrontの機能で手軽に実装できる、CloudFront関数を用いて実装します。

CloudFront Functionsを作成する

「関数」から関数を作成します。

関数はJavaScriptで作成します。

関数のコードは下記の通りになります。

function handler(event) {
    var request = event.request;
    var headers = request.headers;

    //ID:admin
    //Password:********
    var authString = "Basic {BASE64エンコードしたID:パスワード}";

    if (headers.authorization && headers.authorization.value === authString) {
        return request;  // 認証成功
    }

    // 認証失敗
    return {
        statusCode: 401,
        statusDescription: 'Unauthorized',
        headers: {
            'www-authenticate': { value: 'Basic realm="Restricted Area"' }
        }
    };
}

{BASE64エンコードしたID:パスワード}には、一般的には「.htpasswd」内に記載する、BASE64エンコードしたID:パスワードが入ります。
下記のようなツールで、「ID:パスワード」をエンコードします。
https://rakko.tools/tools/24/

コードを入力して保存しただけでは関数として利用できないので、
関数を発行します。

特に問題が無ければ数秒で発行できます。

関数の一覧で「発行済み」と出ると、発行済みなので関数として利用できる状態になっています。

ディストリビューションに実装する

ディストリビューションに移動し、ビヘイビアを編集します。

「関数の関連付け」で「CloudFront Functions」を選択し、先程作成した関数を選択します。

ウェブ確認

ブラウザでウェブ確認し、BASIC認証のログイン画面が表示されれば成功です。

ログイン画面が表示されたならば、ログインテストしましょう。
ログインできれば成功ですが、できなければBase64したパスワードが間違っている可能性があるので、もう一度ID:パスワードをBase64エンコードし、関数のコードを修正しましょう。

まとめ

S3の静的ウェブホスティングの方法を知って以来、静的ウェブサイトを公開するのにS3が安くて便利だと思うようになったのですが、BASIC認証を実装したいとなり、今回の方法に行き着きました。

やってみると思ったより簡単でした。
非公開のサイトやイベント参加者用の資料配布サイトなどではこの方法が使えそうです。

しかし、S3+CloudFrontのBASIC認証は今回紹介したCloudFront FunctionsのほかにLambda@Edgeを使う方法があったり、CloudFront Functions、Lambda@Edgeを問わず子階層のみに掛ける方法なども奥が深いので、今後の記事で紹介できればと思います。

おわりに

秋になり、少し涼しくなってきましたね。
涼しくなると恋しくなる?のが温泉です。
コロナ禍にハマった旅行のひとつが温泉です。

今回のデモの「資料配布サイト」の背景に使用したのは山形県尾花沢市の銀山温泉です。

NHK連続テレビ小説第31作『おしん』に登場して一躍有名になり、近年では大正浪漫を感じるノスタルジックな風景が写真映えすると、国内外の観光客に大人気の温泉街です。

何度か山形市内泊の日帰りで訪れたことはあったのですが、10月に念願叶って友達と宿泊してきました。
予約が困難なこともあり半年前の予約開始日すぐに予約しましたが、その甲斐があって大きな思い出になりました。

技術ブログは今回のS3+CloudFrontのBASIC認証の更なる方法に加え、以前から進めていたLambdaの件もあります。
毎月1件は記事を書けるように精進します。

では…。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA