【新機能】AWS ELBのApplication Load Balancer(ALB)の認証機能でWebアプリにGoogle認証を追加する

644件のシェア(とっても話題の記事)

ども、大瀧です。
本日、AWS ELBのApplication Load Balancer(ALB)に認証機能が追加されました。

ALBの認証機能では様々なことができるのですが独自IdPとの連携は既に他のメンバーが記事を書いているので、本記事ではOpenID Connectの例としてGoogle Identity PlatformによるGoogleアカウント認証を設定する様子をご紹介します。

構成の概要

今回の構成図を示します。OpenID ConnectのAuthorization Code Flowに従うのですが、フローが複雑なのでALBの設定に関わるところだけざっくり解説します。

ALBへの初回(未認証)アクセスはGoogleのログイン画面にリダイレクトし、その画面でユーザーはGoogleアカウントで認証します。認証に通ると認証コードを付与して再度ALBにリダイレクトされ、その認証コードを使ってALBはGoogle APIとごにょごにょして最終的にユーザークレーム(ユーザー情報)を取得、ターゲット(EC2やECS)に転送するリクエストヘッダに付与します。クライアントにはセッションCookieを発行し、2回目以降はCookieを以て認証をスキップします。ごく一般的なフローですね。

必要なもの

  • Googleアカウント
  • ALBのHTTPSリスナに設定するTLS証明書(ACMでも持ち込みでも可)

ステップ1. Google Identity PlatformでのOAuth2.0 クライアントIDの発行

まずはGoogleのOpenID Providerを利用するために必要なOAuth2.0 クライアントIDを発行します。Googleアカウントでログインした状態でGoogle APIの管理コンソールにある認証情報にアクセスします。

[認証情報を作成]ボタンから「OAuthクライアントID」を選択します。

[アプリケーションの種類]では「ウェブ アプリケーション」を選択、任意の名前を設定し、承認済みのリダイレクトURIにはhttps://<ELBに設定するドメイン>/oauth2/idpresponseを入力、[作成]ボタンをクリックします。

クライアントIDとクライアントシークレットが表示されるので、コピーしておき[OK]をクリックします。

また、Google OpenID Providerの各エンドポイントは以下のURLのレスポンスとして一覧できるので、取得しておきます。

$ curl https://accounts.google.com/.well-known/openid-configuration
{
 "issuer": "https://accounts.google.com",
 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
 "token_endpoint": "https://www.googleapis.com/oauth2/v4/token",
 "userinfo_endpoint": "https://www.googleapis.com/oauth2/v3/userinfo",
 "revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke",
 "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
 "response_types_supported": [
  "code",
  "token",
  "id_token",
  "code token",
  "code id_token",
  "token id_token",
  "code token id_token",
  "none"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "RS256"
 ],
 "scopes_supported": [
  "openid",
  "email",
  "profile"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_post",
  "client_secret_basic"
 ],
 "claims_supported": [
  "aud",
  "email",
  "email_verified",
  "exp",
  "family_name",
  "given_name",
  "iat",
  "iss",
  "locale",
  "name",
  "picture",
  "sub"
 ],
 "code_challenge_methods_supported": [
  "plain",
  "S256"
 ]
}

これでOKです。

ステップ2. ALBの設定

ステップ1の情報を元に、ALBにOpenID Connectによる認証を設定します。今回は既存のALBにHTTPSリスナを追加し、合わせて認証を有効にします。

EC2管理画面の[ロードバランサ]からALBを選択し、[リスナー]タブの「リスナーの追加」ボタンをクリックします。

リスナーの追加画面ではプロトコルを「HTTPS」、ポートはデフォルトの443番とします。デフォルトアクションの[+ アクションの追加]から「認証...」をクリックします。

[認証]では「OIDC」を選択、各項目はステップ1で確認したエンドポイントと取得したクライアントID/シークレットを貼り付けます。

画面を下にスクロールし、チェックマークをクリックして保存します。続いて再度[+ アクションの追加]から「転送先...」をクリックし、ターゲットグループを登録します。

その他、TLSのセキュリティポリシーやTLS証明書を選択し、右上の[保存]ボタンをクリックすれば設定完了です。

動作確認

では、WebブラウザからALBにアクセスしてみると...

おなじみのGoogle認証画面にリダイレクトされました!認証情報を入力すると...

ELBからターゲットのレスポンスが表示され、正常にアクセスできました。ちなみにこのURL(/dump)ではリクエストヘッダをレスポンスにダンプするようWebアプリケーションを動作させており、ALBが付与するユーザークレームであるX-Amzn-Oidc-*ヘッダが確認出来ますね。

JWTの検証

ターゲットに転送されるX-Amzn-Oidc-DataヘッダにはALBによって署名されたJWTのトークンが入るので、これをターゲット側で検証することが出来ます。検証で使う公開鍵は、以下のURLから取得出来ます。

https://public-keys.auth.elb.<リージョン名>1.amazonaws.com/<JWTヘッダのkid>
$ curl https://public-keys.auth.elb.ap-northeast-1.amazonaws.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhnw6CUZ6i1mIvMx4CJmwObLc2QN81IML
GDFqjFwet/7e9HDFFPG8I9/PSK4pVG6CZtt2m1bXAyOuwo+5xZPTGw==
-----END PUBLIC KEY-----

では、jwt.ioで検証してみます。Encodedにトークンをペーストするとデコードされたヘッダおよびペイロード(ユーザークレーム)が確認できます。

さらに[VERIFY SIGNATURE]の1つ目のテキストエリアに公開鍵をペーストすると「Signature Verified」と表示され、検証できました!

ELBのログ

ALBのアクセスログはS3に格納されます。今回の認証に係るログは以下のようになりました。

h2 2018-05-31T04:59:51.812260Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57497 - -1 -1 -1 302 - 206 543 "GET https://<ドメイン名>:443/dump HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 - "Root=1-XXXXXXXX-XXXXXXXXXXXXXXXX" "<ドメイン名>" "arn:aws:acm:ap-northeast-1:<アカウントID>:certificate/bef10a17-abf3-4aa1-a8fb-76416f9de10f" 0 2018-05-31T04:59:51.811000Z "authenticate"
h2 2018-05-31T05:03:42.955191Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57745 - -1 -1 -1 302 - 552 986 "GET https://<ドメイン名>:443/oauth2/idpresponse?state=XXXXXXXXXXXXXXXX&code=XXXXXXXXXXXXXXXX&authuser=0&session_state=XXXXXXXXXXXXXXXX&prompt=none HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 - "Root=1-5b0f822e-5824a8c6c44fa81b0da2cb48" "<ドメイン名>" "session-reused" -1 2018-05-31T05:03:42.459000Z "authenticate"
h2 2018-05-31T05:03:42.992511Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57745 <ターゲットのIP>:32771 0.001 0.003 0.000 200 200 718 1691 "GET https://<ドメイン名>:443/dump HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:ap-northeast-1:<アカウントID>:targetgroup/tg0/XXXXXXXXXXXXXXXX "Root=1-XXXXXXXX-XXXXXXXXXXXXXXXX" "<ドメイン名>" "session-reused" 0 2018-05-31T05:03:42.988000Z "authenticate,forward"

1,2行目が初回の認証周り、3行目はターゲットへの転送にかかるログですね。

まとめ

Application Load Balancer(ALB)の認証機能のひとつ、OpenID Connect連携の例としてWebアプリにGoogle認証をかける様子をご紹介しました。既存のWebアプリケーションに手軽にアクセス制限をかけられる、ナイス機能だと思います。ALBによるWebアプリケーションの構築に役立ててください。

続編があります

この構成は任意のGoogleアカウントの認証を通してしまうので、Google Appsの自組織のメールアドレスのみ許可したいというような業務向けのユースケースだとターゲット側で認可機能を実装しなくてはならず、片手落ち感があります。少し複雑になりますが、Amazon Cognitoユーザープールでドメイン縛りを追加した記事を書きました。

こちらもどうぞ!

参考URL