Application Load BalancerとAWS Lambdaでベーシック認証をかけたコンテンツを作成する #reinvent

Application Load BalancerとAWS Lambdaが連携できるようになったことを受け、Lambdaでベーシック認証をかける方法を試してみました。
2018.12.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。サービスグループの武田です。

1週間に渡るre:Invent 2018も終わりを迎え、クラスメソッドメンバー一同も日本への帰路へとつきました。さまざまな新サービスやアップデートが発表されDevelopers.IOでもやってみたブログが多数上がっています。

さて今回は、Application Load Balancer(以下、ALB)とAWS Lambdaが連携できるようになったことを受け、Lambdaでベーシック認証をかける方法を試してみました。

[速報]ALB HTTP(S)のバックエンドにLambdaを選択できるようになりました!#reinvent

ALBのバックエンドにLambdaを選択してみた! #reinvent

やってみた

今回作成するLambda関数は次のとおりです。

index.js

'use strict';
exports.handler = async (event, context) => {
    const headers = event.headers || {};

    // ALB Health check
    if (headers['user-agent'] === 'ELB-HealthChecker/2.0') {
        return {
            statusCode: 200,
            statusDescription: '200 OK',
            isBase64Encoded: false,
            headers: {
                'Content-Type': 'text/html'
            }
        };
    }

    // Configure authentication
    const authUser = process.env.AUTH_USER;
    const authPass = process.env.AUTH_PASS;

    // Construct the Basic Auth string
    const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');

    // Require Basic authentication
    if (headers.authorization !== authString) {
        return {
            statusCode: 401,
            statusDescription: '401 Unauthorized',
            body: 'Unauthorized',
            isBase64Encoded: false,
            headers: {
                'WWW-Authenticate': 'Basic',
                'Content-Type': 'text/html'
            }
        };
    }

    // OK Basic authentication
    const response = {
        statusCode: 200,
        statusDescription: '200 OK',
        isBase64Encoded: false,
        headers: {
            'Content-Type': 'text/html'
        }
    };
    response.body = 'Authentication succeeded!';

    return response;
};

ALBからのヘルスチェックはユーザーエージェントを見て処理します(6行目)。22行目はユーザーから送信された認証情報と比較するために文字列を組み立てます。38行目以降が認証できた場合に行う処理となります。

それではマネジメントコンソールにアクセスしてやってみます。リージョンは東京です。

まずはLambdaのコンソールにアクセスし、[関数の作成]をクリックします。

名前は適当にbasic-auth、ランタイムは今回Node.js 8.10を選択。ロールはなんでもいいですが、今回はCloudWatchの権限がついたロールを付与しました。

関数が作成できたら、関数コードに上記のソースコードを貼り付けます。

環境変数としてAUTH_USERAUTH_PASSを設定します。今回はそれぞれcm_userreinvent2018を設定しました。設定できたら右上の[保存]ボタンをクリックします。

保存できました。

続いてALBを作成します。名前はbasic-auth-testとしました。ポートは80です。

AZの指定が必要ですので、適当に選んでおきます。

セキュリティグループは自分の環境からアクセスできるものを設定しましょう。今回は新規に作成しました。

ターゲットグループの指定です。ターゲットの種類として Lambda関数 が指定できますね。これを選択すると、関数を紐付けるUIに変わるので先ほど作成したbasic-authを指定します。またヘルスチェックも有効化しておきます。

最後に確認画面です。間違いなければ[作成]ボタンをクリックしましょう。

作成後少し待っているとステータスが healthy になるはずです。

それではALBのURLを指定してブラウザからアクセスしてみましょう。ベーシック認証のダイアログが出ました!

適当なID/PWを入力すると弾かれます。

環境変数に設定したID/PWを入力すると……。

認証が通ってコンテンツが表示されました!

さいごに

ALBのターゲットグループにLambdaが指定できるようになったことで新しい可能性が出てきたのではないかと思われます。Amazon CloudFrontとAWS Lambda@Edgeでベーシック認証をかける構成を西村が書いていました。

Amazon CloudFrontとAWS Lambda@EdgeでSPAのBasic認証をやってみる

この構成ではLambda関数はフィルターとして機能していましたが、今回の構成ではLambda関数はエンドポイントとなります(あくまでALBのルーティング先のひとつ)。そのためとりあえず興味本位と勢いで検証しましたが、実際にどういうユースケースで役立てられるのかピンときていません。誰か教えてください。