[AWS] CloudFrontでBASIC認証を行う - Lambda@Edge + Node.js

S3をWebサーバ代わりに使っている方も多いと思いますが、開発中のページや社内だけで閲覧したい場合など簡易的なアクセス制限をかけたい場合がありますよね。以前はEC2の安いインスンスを用意することもありましたが、Lambda@Edgeが登場してからはS3とCloudFrontだけで完結することができます。

というわけで今回はCloudFrontを使ってBASIC認証をかける方法をまとめます。

準備する

CloudFront

BASIC認証のために特別な設定は不要です。いつもと同じ手順でディストリビューションを用意します。

Lambda@Edge

Lambda@Edgeを利用する場合、リージョンを「バージニア北部(us-east1)」にしておく必要があります。またIAM関係の設定を行うため権限があるかも事前に確認しておいてください。それ以外は通常のLambdaとそれほど変わりません。

手順としては以下のようになります。

  1. コードを貼り付ける
  2. デプロイ
  3. トリガーの設定(CloudFrontと関連付け)

コードを貼り付ける

Lambda関数を新たに作成したら、エディター部分に以下のコードを貼り付けてください。ユーザー名とパスワードの部分は適宜ご変更ください。こちらのページのコードをベースにさせていただきました。

exports.handler = async (event, context, callback) => {
  const request = event.Records[0].cf.request
  const headers = request.headers

  // ユーザー名とパスワードを設定
  const authUser = 'user'
  const authPass = 'pass'
  const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64')

  // 初アクセス or 認証NG
  if (typeof headers.authorization === 'undefined' || headers.authorization[0].value !== authString) {
    callback(null, {
      status: '401',
      statusDescription: 'Unauthorized',
      body: '<h1>401 Unauthorized</h1>',
      headers: {
        'www-authenticate': [{key:'WWW-Authenticate', value:'Basic'}]
      }
    })
  }
  // 認証OK
  else{
    callback(null, request)
  }
}

デプロイ

エディターの上にある「Deploy」ボタンをクリックします。これでコードが反映されます。

トリガーの設定

最後に先ほどDeployしたコードとCloudFrontの関連付けを行います。大体ここでつまずくんですよねw

画面上部にある「トリガーを追加」をクリック。

設定先として「CloudFront」を選択。「Lambda@Edgeへのデプロイ」をクリック。

どこにどうデプロイするのか聞かれるので、事前に準備したCloudFrontのディストリビューションを選択、イベントは「ビューアーリクエス」を選択、最後のチェックボックスにチェックをし「デプロイ」ボタンをクリックします。

すると、エラーになりますw ← 最初はここで途方に暮れるんですよね。Lambda@Edgeの場合、最初の1回目だけIAMのページでロールの設定を行う必要があります。Lambdaの「設定」からIAMの設定ページへのリンクが用意されていますので、今回はそこからたどります。現在開いているデプロイするためのモーダルなウィンドウはいったん閉じます。

下のエラーを修正し、もう一度お試しください。 関数の実行ロールは、edgelambda.amazonaws.com サービスプリンシパルによって引き受け可能である必要があります。

現在開いているLambdaのページの下部にある「設定」→「アクセス権限」→「ロール名」のリンクをクリックします。

するとIAMのロール設定のページに移動するので、ページのやや下の方にある「信頼関係」→「信頼関係の編集」をクリック。

JSONを入力する画面になりますので、Serviceedgelambda.amazonaws.comを追加します。Serviceを配列にすることをお忘れなく。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "lambda.amazonaws.com",
            "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

反映するまで少し時間がかかるようなので、数分待った後にLambdaのページからもう一度トリガーのデプロイを行います(数分待っても相変わらずエラーが出る場合はウィンドウをリロードしてみてください)。

無事にデプロイが終わったらCloudFront側のステータスが「有効化」になるまで待てば完了です。

確認する

CloudFrontのディストリビューションへアクセスし、BASIC認証のダイアログが表示されていれば成功です。

CloudWatchからログを確認する

追記

現状のままだとconsole.log()の出力結果をCloudWatchから確認できません。デバッグが面倒なため設定を行います。

IAMロールの変更

Lambdaの「設定」→「アクセス権限」からIAMロールを変更する画面へ移動、「ポリシーの編集」ボタンをクリックし以下のハイライトしている行を編集します。

数字の箇所はデフォルトのままで大丈夫です。リージョンが記載されている箇所をワイルドカード()に、お尻の文字列を編集してこちらもワイルドカード()にしておきます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:*:123456789012:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:*:123456789012:log-group:/aws/lambda/*"
            ]
        }
    ]
}

CloudWatchから確認する

Lambda@Edgeが実行されたらCloudWatchの「ログ」→「ロググループ」から確認できるのですが、リージョンの指定方法に注意が必要です。

Lambda@Edgeはバージニア北部(us-east-1)に設置したので、ログもこのリージョンに記録されると思いきや、関数が実行されたリージョンに近い場所に記録されるのです。例えば日本国内であれば東京か大阪リージョンですね。

詳細はClassmethodさんのブログを参照ください。 dev.classmethod.jp

参考ページ