LoginSignup
17
6

More than 3 years have passed since last update.

KeycloakでSAMLログインをテストしたい

Last updated at Posted at 2020-12-13

前振り

私が担当したシステムでは、KeycloakのIdentity Broker機能を使用し、外部Identity Provider(以下外部IdP)を経由したログインを実施しています。
Identity Broker機能とは、KeycloakがService Provider(以下SP)となることで、OpenID ConnectSAMLOAuthといった各種プロトコルに対応した外部IdPのアカウント情報を利用して、Keycloakと連携しているアプリケーションにSSOするという機能です。
本記事では、これらプロトコルのうち、SAMLを題材としています。

今回、このシステムを改修し、KeycloakがSAMLレスポンスを受け取った後のコールバックメソッドをカスタムして独自の処理を実行することになりました。
具体的には、以下図のNo.8の箇所、外部IdPからレスポンスを受け取った後の処理です。
ここでは、レスポンスの内容を基にKeycloakの内部的な認証処理を実施しています。
このタイミングでユーザーの認証情報に対し、特定の属性をアタッチするよう修正を加えました。
:information_source: 各フローの具体的内容はこちらのドキュメントをご確認ください。
image.png

本題

さて、本改修の動作確認をローカル環境で実施するにあたり、SAMLレスポンスを返してくれる外部IdPが必要です。
本番環境では、ADFSなどの外部IdPを使用していますが、ローカルでの動作確認用に同等のものを用意するのは難しいです。
そこで、今回は簡単に用意できるSAMLのIdPについて考えてみます。

前提条件

  • SP(Keycloak)にSAMLでログインできること
  • SP上(Keycloak上)の既存のユーザー「test@example.com」とアカウントリンクできること
    • アカウントリンクは、SPのユーザー名 = IdPのユーザー名でのマッピングとする
    • JITプロビジョニングは行わない。SP、IdPに同名のユーザーが存在することを前提とする
    • 本システムでは、SPに同名のユーザーが存在しない場合 エラーとしているため

備考

  • SP(Keycloak)のレルムはmainレルムとする
  • Keycloakのバージョンは、本記事公開時点で最新の11.0.3を使用している

解その1 Keycloak(同一インスタンス上)を使う

Keycloak自身を外部IdPとみなす方法です。
Keycloakでは、レルムという単位でアカウント情報、セッション情報、連携先アプリケーションの情報、認証方法などを管理しています。
レルムが異なると、同一のインスタンスにおいても別のIdPとみなすことが可能です。

:information_source: 一応、同一のレルムでもIdPとして設定可能ですが、既にログイン済みである旨の警告メッセージが表示されてしまいます。
image.png

手順

1.1 外部IdP用レルム作成(外部IdP)

まずは別レルムを作成します。
今回はtestレルムと名付けました。
image.png

1.2 matadata取得(外部IdP)

レルム作成後、以下のURLからIdPのmetadataを取得します。
https://<Keycloak-host>/auth/realms/test/protocol/saml/descriptor

ブラウザから取得するより、curlで取得するほうが容易です。

$ curl https://<Keycloak-host>/auth/realms/test/protocol/saml/descriptor > metadata.xml

1.3 外部IdPの定義作成(SP)

SPとするmainレルムの作業となります。
手順1.2で取得したmetadata.xmlをSPにインポートし、外部IdPの定義を作成します。
インポート手順ですが、Identity ProvidersにSAMLを選択し、metadata.xmlをインポートします。
image.png

すると、metadata.xmlの定義内容で設定が作成されます。
image.png

1.3.1 外部IdP定義の設定について

ここで、以下の設定を確認し、必要に応じて変更します。

前提条件で述べた通り、SPのユーザー名とIdPのユーザー名でマッピングを行いたいのでNameID Policy Formatには、Unspecifiedを設定します。

設定名 設定内容
NameID Policy Format Unspecified

次に、以下はSAMLリクエストの署名と検証です。
この設定は、本番環境の外部IdPと設定を揃えたい場合には設定が必要となります。
(ローカル環境での動作確認では、必ずしも必要な設定ではありません)

設定名 意味 ONとなるケース
Want Assertions Signed SAMLリクエストの署名有無 外部IdP側の設定によっては、リクエストの発行を信頼済みのSPにのみ許可するケースがあり、その場合、この設定はONにする必要があります。
Want Assertions Encrypted SAMLリクエストの暗号化 何等かの秘匿データをSAMLリクエストに含める場合、この設定はONにする必要があります。
Validate Signature SAMLレスポンスの署名検証 レスポンスの署名検証を行わないと、アサーションの改ざんを検知できなくなり容易になりすましログインが可能となるため、この設定はONとなっているべきです。

これらの設定が完了したら、保存します。

1.4 matadata取得(SP)

exportタブから、SPのmetadata(keycloak.txt)をダウンロードします。
image.png

SP側の準備はこれで完了です!

1.5 クライアント(SPの定義)作成(外部IdP)

testレルムに戻り、手順1.4でダウンロードしたSPのmetadata(keycloak.txt)をインポートして、クライアントを作成します。

これが、IdPに対するSPの定義となります。
image.png

1.6 SPに連携するユーザーを作成(外部IdP)

testレルム上にユーザーの作成と、パスワードの設定を実施します。
前提条件で述べた通り、SPのユーザー名と同一となるtest@example.comとします。

image.png

これでIdP側の準備も完了です!

ログインしてみる

ログイン先は、Keycloakのユーザー用管理コンソールとします。

まず、https://<Keycloak-host>/auth/realms/main/accountにアクセスし、画面右側の外部IdP用ログインリンクを選択します。
image.png

外部IdPであるtestレルムのログイン画面にリダイレクトするので、test@example.comでログインします。
image.png

test@example.comとしてSPへのログインが成功し、連携先アプリケーション(管理コンソール)にリダイレクトしました。
image.png

アカウントリンクも問題ありません。
image.png

思うところ

ここまでの手順で、1つのKeycloakインスタンスで外部IdPを使用したSP(Keycloak)へのログインの動作確認が可能となりました。
なのですが、この環境には以下の制約が存在します。

  • ユーザーをSP、外部IdPのそれぞれに都度作成しなければならない
  • KeycloakデフォルトのBrowser Flowでは、ログイン時、ログインIDとパスワードを入力する必要がある
    • 入力の手間を減らすため、パスワードは不要としたい
    • これについては、外部IdP側で以下のような認証フローを作成、Browser Flowに設定すれば、ログインID単独でログイン可能ではある image.png

そこで、前提条件を満たせつつ、IdP側のユーザ管理が不要で、かつ使いやすいモジュールを探したところ、saml-idpを発見しました。

解その2 saml-idpを使う

saml-idpは、nodeで書かれたSAML IdPとして動作するモジュールです。
Dockerfileがあらかじめ用意されているため、導入が容易で、設定もシンプルです。

手順

本手順では、予め用意されたDockerfileを使用して、saml-idpを構築します。

2.1 クローン

まずはgit cloneします。

# git clone https://github.com/mcguinness/saml-idp
Cloning into 'saml-idp'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 481 (delta 4), reused 6 (delta 3), pack-reused 469
Receiving objects: 100% (481/481), 1.43 MiB | 1.37 MiB/s, done.
Resolving deltas: 100% (249/249), done.

このようなファイル構成となっています。

# cd saml-idp/
# ll -rt
合計 112
-rw-r--r-- 1 root root  1448  7月  2 12:25 config.js
drwxr-xr-x 2 root root    20  7月  2 12:25 bin
-rw-r--r-- 1 root root 26281  7月  2 12:25 app.js
-rw-r--r-- 1 root root 10597  7月  2 12:25 README.md
-rw-r--r-- 1 root root  1082  7月  2 12:25 LICENSE
-rw-r--r-- 1 root root   473  7月  2 12:25 Dockerfile
drwxr-xr-x 4 root root    29  7月  2 12:25 public
-rw-r--r-- 1 root root   919  7月  2 12:25 package.json
-rw-r--r-- 1 root root 41871  7月  2 12:25 package-lock.json
drwxr-xr-x 2 root root    36  7月  2 12:25 lib
-rw-r--r-- 1 root root  1184  7月  2 12:25 idp-public-cert.pem
-rw-r--r-- 1 root root  1679  7月  2 12:25 idp-private-key.pem
-rw-r--r-- 1 root root    65  7月  2 12:25 docker-compose.yml
drwxr-xr-x 2 root root   101  7月  2 12:25 views
drwxr-xr-x 2 root root   108  7月  2 12:25 test

2.2 設定修正

必要に応じてconfig.jsを修正します。
今回修正したのは以下の設定です。

設定名 設定値 備考
userName test@example.com SP(Keycloak)上のユーザーを設定
nameIdFormat urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified SPのユーザー名とIdPのユーザー名でマッピングを行いたいのでUnspecifiedとする
/**
 * User Profile
 */
var profile = {
  userName: 'test@example.com',
  nameIdFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
  firstName: 'Saml',
  lastName: 'Jackson',
  displayName: 'saml jackson',
  email: 'saml.jackson@example.com',
  mobilePhone: '+1-415-555-5141',
  groups: 'Simple IdP Users, West Coast Users, Cloud Users'
}

2.3 Dockerfileの修正、ビルド

Dockerfileを修正し、nodeのバージョンを固定並びに実行時引数を追加、修正します。

FROM node:12.18.1-buster

ADD ./package.json package.json
RUN npm install

EXPOSE 7000 7000

# ADD ./node_modules node_modules
ADD ./lib lib
ADD ./views views
ADD ./app.js app.js
ADD ./config.js config.js
ADD ./idp-public-cert.pem idp-public-cert.pem
ADD ./idp-private-key.pem idp-private-key.pem
ADD ./public public

ENTRYPOINT [ "node",  "app.js", "--acs", "https://<Keycloak-host>/auth/realms/main/broker/saml/endpoint", "--aud", "https://<Keycloak-host>/auth/realms/main", "--host", "0.0.0.0"]

ビルドします。

docker build . -t saml-idp

2.4 起動

docker-composeで起動します。

デフォルトの設定では、コンテナ側のポートは7000を使用しています。

  saml-idp:
    container_name: saml-idp
    image: saml-idp
    ports:
      - 7000:7000
    network_mode: bridge
    extra_hosts:
        - keycloak.path.local:192.168.2.13
        - hoka.nanika.areba.path.local:192.168.2.14
# docker-compose up -d saml-idp

以下のログが出力されていれば起動完了です。

# docker logs saml-idp
-- 省略 --
IdP server ready at
  http://f6cdd171af63:7000

2.5 リバースプロキシ作成

saml-idpにアクセスするためのリバースプロキシを設定します。

以下はApacheの場合の設定例となります。

<VirtualHost _default_:443>
ServerName test.saml.local

RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port 443

SSLEngine on
SSLProxyEngine On
SSLCertificateFile /root/certs/server.crt
SSLCertificateKeyFile /root/certs/server.key

ProxyPreserveHost On

ProxyPass / http://localhost:7000/
ProxyPassReverse / http://localhost:7000/
</VirtualHost>

2.6 metadata(外部IdP)取得

以下のURLからmetadataを取得します。
https://<saml-idp-host>/metadata

$ curl https://<saml-idp-host>/metadata > metadata.xml

これで、saml-idp側の準備は完了です。
なお、SP(Keycloak)のmetadataをsaml-idpにインポートする必要はありません。

2.7 外部IdPの定義作成(SP)

SP(Keycloak)側で外部IdP(saml-idp)の定義を作成します。
設定については、手順1.5と同様にmetadata.xmlをインポートして外部IdP定義を作成すればよいので省略します。

ログインしてみる

ログイン先は、Keycloakのユーザー用管理コンソールとします。

https://<Keycloak-host>/auth/realms/main/accountにアクセスし、画面右側の外部IdP用ログインリンクを選択します。
image.png

外部IdPであるsaml-idpにリダイレクトするので、右下のSign inを押してログインします。
image.png

SPにログインが成功し、連携先アプリケーション(管理コンソール)にリダイレクトしました。
image.png

アカウントリンクも問題ありません。
image.png

なお、Subject NameIDを変更すると、別ユーザーとしてログインすることができます。
image.png

終わりに

SAMLのテスト用IdPは、今回紹介した二つに限らず、数多く存在します。
例えば、テスト用IdPのオンラインツールが存在します。
自身のユースケースに沿ったテストツールを探してみると面白いかもしれません。

17
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
6