CloudFrontを通過する特定のパスのみにBasic認証を設定してみた

こんにちは、AWS事業本部の荒平(@0Air)です。

特定のパスのみにBasic認証を設定したいという相談をいただきました。
以下の記事をベースに設定できますが、コンソール画面の表示が改修の度に変わるため、主にAWS CLIを利用して最初から確認してみます。

CloudFront Functions(関数)の作成

ディストリビューションを作成する前に、CloudFront Functionsの関数を作成します。
CloudShellにて、以下のコマンドを使用します。ID/Passwordは適宜読み替えてください。

echo -n "classmethod:P@ssw0rd" | base64

出力された文字列を利用します(ここでは、Y2xhc3NtZXRob2Q6UEBzc3cwcmQ=

以下のコードを、任意のファイル名で保存します。(ここでは、basic-auth.js
6行目のBasic認証の文字列は、各自で書き換えが必要です。

basic-auth.js

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

  // echo -n user:pass | base64
  var authString = "Basic Y2xhc3NtZXRob2Q6UEBzc3cwcmQ=";

  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: {
        "www-authenticate": { value: "Basic" } 
      }
    };
  }

  return request;
}

CloudShell上で以下のコマンドを実行して、CloudFront 関数を作成、パブリッシュします。
1行目の作成を走らせた際にETagが生成されるので、これを2行目で使います。

aws cloudfront create-function --name "basic-auth" --function-config '{"Comment":"My function","Runtime":"cloudfront-js-2.0"}' --function-code fileb://basic-auth.js
aws cloudfront publish-function --name "basic-auth" --if-match ETVPDKIKX0DER(Etag名)

それぞれ、以下のような応答が返っていれば、上手く作成できています。
(返らない場合は、CloudShellを操作するIAMユーザーの権限を見直してください)

[cloudshell-user@ip-10-134-7-94 ~]$ aws cloudfront create-function --name "basic-auth" --function-config '{"Comment":"My function","Runtime":"cloudfront-js-2.0"}' --function-code fileb://basic-auth.js
{
    "Location": "https://cloudfront.amazonaws.com/2020-05-31/function/arn:aws:cloudfront::000000000000:function/basic-auth",
    "ETag": "ETVPDKIKX0DER",
    "FunctionSummary": {
        "Name": "basic-auth",
        "Status": "UNPUBLISHED",
        "FunctionConfig": {
            "Comment": "My function",
            "Runtime": "cloudfront-js-2.0"
        },
        "FunctionMetadata": {
            "FunctionARN": "arn:aws:cloudfront::0000000000000:function/basic-auth",
            "Stage": "DEVELOPMENT",
            "CreatedTime": "2024-02-15T14:47:34.770000+00:00",
            "LastModifiedTime": "2024-02-15T14:47:34.770000+00:00"
        }
    }
}
[cloudshell-user@ip-10-134-7-94 ~]$ aws cloudfront publish-function --name "basic-auth" --if-match ETVPDKIKX0DER
{
    "FunctionSummary": {
        "Name": "basic-auth",
        "Status": "UNASSOCIATED",
        "FunctionConfig": {
            "Comment": "My function",
            "Runtime": "cloudfront-js-2.0"
        },
        "FunctionMetadata": {
            "FunctionARN": "arn:aws:cloudfront::0000000000000:function/basic-auth",
            "Stage": "LIVE",
            "CreatedTime": "2024-02-15T14:52:27.691000+00:00",
            "LastModifiedTime": "2024-02-15T14:52:27.691000+00:00"
        }
    }
}

コンソール画面にて、以下のような状態になっていれば問題ありません。

CloudFront Distributionの設定

CloudFront Distributionを設定します。
以下ディストリビューションの設定ファイルを作成し、任意の名前で保存します。(ここでは、dist-config.json

デフォルトパスへのアクセスは何もせず、/auth パスのみ先程の関数が呼び出されるようにします。
オリジン名(12行目)、関数のARN(59行目)は各環境において書き替えてください。

dist-config.json

{
    "CallerReference": "unique-caller-reference",
    "Aliases": {
        "Quantity": 0
    },
    "DefaultRootObject": "",
    "Origins": {
        "Quantity": 1,
        "Items": [
            {
                "Id": "S3",
                "DomainName": "arap.s3-website-ap-northeast-1.amazonaws.com",
                "OriginPath": "",
                "CustomHeaders": {
                    "Quantity": 0
                },
                "CustomOriginConfig": {
                    "HTTPPort": 80,
                    "HTTPSPort": 443,
                    "OriginProtocolPolicy": "http-only",
                    "OriginSslProtocols": {
                        "Quantity": 3,
                        "Items": ["TLSv1", "TLSv1.1", "TLSv1.2"]
                    },
                    "OriginReadTimeout": 30,
                    "OriginKeepaliveTimeout": 5
                }
            }
        ]
    },
    "DefaultCacheBehavior": {
        "TargetOriginId": "S3",
        "ViewerProtocolPolicy": "allow-all",
        "ForwardedValues": {
            "QueryString": false,
            "Cookies": {
                "Forward": "none"
            }
        },
        "MinTTL": 0,
        "DefaultTTL": 86400,
        "MaxTTL": 31536000,
        "FunctionAssociations": {
            "Quantity": 0
        }
    },
    "CacheBehaviors": {
        "Quantity": 1,
        "Items": [
            {
                "PathPattern": "/auth/*",
                "TargetOriginId": "S3",
                "ViewerProtocolPolicy": "redirect-to-https",
                "FunctionAssociations": {
                    "Quantity": 1,
                    "Items": [
                        {
                            "EventType": "viewer-request",
                            "FunctionARN": "arn:aws:cloudfront::000000000000:function/basic-auth"
                        }
                    ]
                },
                "ForwardedValues": {
                    "QueryString": true,
                    "Cookies": {
                        "Forward": "all"
                    }
                },
                "MinTTL": 0,
                "DefaultTTL": 0,
                "MaxTTL": 0
            }
        ]
    },
    "Comment": "CloudFront Function for /auth path",
    "Enabled": true

}

保存したら、CloudShellから、CloudFront Functionを紐付けた状態のディストリビューションを作成します。

aws cloudfront create-distribution --distribution-config file://dist-config.json

動作確認

オリジンのS3にはindex.htmlをひとつ置いてあります。(構成の参考は以下を御覧ください)

デフォルトパス(/index.html)にアクセスした場合は、ページ内容が表示されます。

制限したいパス(/auth/*)にアクセスした際には、Basic認証が出現することを確認できました。
CloudFront Functionで指定したID/パスワードを入力することで、認証したのちにファイルを読み込むことができます。

おわりに

「一時的にWebサイトを公開したいけど、完全無防備はちょっと・・・」というニーズがある場合に使えそうです。
CloudFront Functionsを用いるとサクッとBasic認証をかけることができるため、お困りの方は試す価値がありそうです。

このエントリが誰かの助けになれば幸いです。
それでは、AWS事業本部 コンサルティング部の荒平(@0Air)がお送りしました!

参考