AWS Application Load Balancer (ALB) の認証機能をKeycloakと使う


はじめに

2018/05/30に、丁度日本でのAWS Summit開催中にALBの組み込み認証機能がリリースされました。この機能を使うと、Amazon Cognito または OpenID Connect (以下、OIDC)に準拠したOpenID Provider (Identity Provider、IdP)と組み合わせることで、ALB配下のアプリケーションを保護することができます。詳しくは、本家のBlog記事の以下を参照されると良いでしょう。

本記事では、この新機能をOIDCに準拠しているOSSのIdentity and Access ManagementのソフトウェアであるKeycloakと組み合わせて使う方法について説明します。丁度最近、Keycloakのメーリングリストに動かなくて困っている人の投稿を目にしたので書き記しておきます。

なお、Keycloak以外をIdPとして使用してみた日本語記事が既にクラスメソッドさんにて書かれているので、GoogleやOneLoginをIdPとして使いたい方はこちらを参照すると良いでしょう。

最初に残念なお知らせ

  • 現状はそのままでは残念ながらKeycloakと組み合わせることができない。
  • 原因はAWS ALB側にある (フォーラムに投稿したバグレポート)。

    • ALBはToken Endpointのレスポンスに含まれる token_typeBearer であることを期待している。
    • 一方、Keycloakはすべて小文字の bearer を返す (GitHubなんかも同様っぽい)
    • OAuth 2.0の仕様としては、RFC 6749 の 4.2.2. Access Token Responseに記載の下記の通り、大文字小文字を区別はしないが、ALBは区別して処理してしまっている。結果、ALBにて500エラーが発生してしまう。

    token_type
    REQUIRED. The type of the token issued as described in
    Section 7.1. Value is case insensitive.

  • 現状のALBをKeycloakと組み合わせるには、Keycloak本体を改修するか、フロントにApache HTTPD Serverなどを立ててレスポンスを書き換えてあげる必要あり。

2017/07/30 追記: フォーラムに投稿したバグレポートにAWSから以下のレスポンスがあり、近々修正されるかもしれません。そうすると上記のワークアラウンドは不要になるはず。

We are already tracking this issue internally. Currently the service team is working on deploying the fix.

設定方法

設定として必要な

  • Keycloak側の設定
  • Keycloak前段のApache設定 (現時点でのワークアラウンド例)
  • AWS ALB側の設定

を説明していく。

Keycloak側の設定

  • 適当なレルムでOIDC用のクライントを適当なクライアントIDで作成する(下図ではoidctestとしている)。
  • 下図のようにAccess Typeconfidential としつつ、Valid Redirect URIsに保護するアプリケーションのURIを設定し、一度 Save ボタンをクリックして保存する。

  • また、Credentials タブを開いて、クライアントシークレットである Secret の値を控えておく (ALB側の設定で使用する)。

Keycloak前段のApache設定 (現時点でのワークアラウンド例)

最初に述べたとおり、AWS ALB側がOAuth 2.0の仕様どおりとして動いていないため500エラーとなってしまう。これを回避するためのワークアラウンド例として、KeycloakのフロントにApache HTTPD Serverを立て、Substituteディレクティブを設定して Bearer に置換している。

<VirtualHost *:80>
    ServerName    test-sso.・・・

    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
    ProxyErrorOverride off

    AddOutputFilterByType SUBSTITUTE application/json
    Substitute s/"token_type":"bearer"/"token_type":"Bearer"/n
</VirtualHost>

ALB側の設定

ALBリスナールールのOIDC認証設定にて、Keycloakの情報を設定する。

  • クライアントIDには、Keycloak側で作成したクライアントのClient IDを入力する。
  • クライアントのシークレットには、控えたクライアントシークレット値を入力する。
  • 発行者、認証エンドポイント、トークンエンドポイント、ユーザー情報エンドポイントについては、使用するKeycloakのレルムのOpenID Provider情報を参照して設定する。https://<Keycloakホスト>/realms/<使用するレルム名>/.well-known/openid-configuration でJSONにて参照できるので、JSON中の下記項目をそれぞれ設定すれば良い。
    • 発行者: "issuer" の値
    • 認証エンドポイント: "authorization_endpoint" の値
    • トークンエンドポイント "token_endpoint" の値
    • ユーザー情報エンドポイント "userinfo_endpoint" の値

動作確認

アプリケーションにアクセスすると、Keycloakの該当レルムのログイン画面にリダイレクトされます。別途該当レルムにユーザーを作成しておきログインすると、無事にアプリケーションの画面にアクセスすることができるようになります。もし、Apacheにて置換処理を入れていない場合は残念ながら現状はALBが500エラーを返してしまいます。

余談

KeycloakはOIDC準拠だし余裕で連携できるでしょ、と試してみたものの全然動かなくて原因を切り分けるのに苦労しました。Keycloak側はToken Endpointを200ステータスで正常に返しているだけであり、ALB側のアクセスログを参照しても詳細なエラー情報は記録されていないので一体何が悪いのか当初はさっぱり分からず状態でした。
結局、GolangでダミーのオレオレOpenID Providerを実装してみてKeycloakと同じ様なレスポンスを返すようにしつつ、内容をちょっと変えてみながら動作確認していたところtoken_typeBearerでないとALBの認証機能が動作しないことが分かりました。