Keycloakで統合Windows認証を試してみる

# 今日やること

Keycloakアドベンドカレンダー14日目の今回は、Keycloakで統合Windows認証を試してみます。

統合Windows認証は、Windowsドメインにログイン済みの端末からブラウザ(HTTPプロトコル)経由でKerberos認証を実施する仕組みです。
この仕組みを利用すると、Windowsサーバー(KDC)から配布されるセッション・チケットを元にKerberos認証が行われるため、ブラウザからはID/PWを送信せずにユーザー認証が行えます。このようにブラウザ経由でKerberos認証を実現するためのプロトコルを「SPNEGO(The Simple and Protected GSS-API Negotiation Mechanism)」 といいます。

Keycloakではこのブラウザ経由のKerberos認証(SPNEGO)に対応しているため、統合Windows認証が利用できます。Kerberos認証自体はWindowsに特化した仕様ではないため、Linux上に構築されたKDCとKerberos認証を実施することも可能ですが、一般的な事例としては、Windowsサーバー(Active Directory)をKDCとして利用する方式が多いようです。

以下がKeycloakでのKerberos認証(SPNEGO)のログインシーケンスとなります。
![Kerberos認証シーケンス.png](https://qiita-image-store.s3.amazonaws.com/0/209129/896b8be4-4f4d-223c-614f-a5dfe0ca2050.png)


1. クライアントがブラウザでKeycloakのログイン画面にアクセスします。
2. Keycloakが401応答を返します。(このとき、HTTPヘッダにWWW-Authenticate: Negotiate ヘッダがつきます)
3. 2 のNegotiateヘッダを受けて、クライアントがWindows(AD/KDC)にHTTPサービスチケット(SPNEGOで利用されるサービスチケット)を要求します。
4. 適切にドメインにログインしているユーザーであれば、HTTPサービスチケットが取得できます。
5. クライアントがブラウザからAuthorization: Negotiateヘッダに 4 のチケット情報をつけて、Keycloakに送信します。
6. Keycloakがkeytabファイルを使ってHTTPサービスにログインします。(KeycloakはWindows(AD/KDC)とは直接通信しません[^openam])
7. KeycloakがHTTPサービスにより、送信されたチケット情報が正しいかどうかを検証します。
8. 検証に問題なければ、チケット情報からプリンシパル（認証済みユーザー名)を取得し、Keycloakのログインセッションを生成します。
9. Kerberos認証が成功したユーザーは、Keycloak上にユーザーが作成されます。(ただ、取り込まれる属性は限定的です。こちらは後述します。）
9. Keycloakのログインが完了します。

SPNEGOやGSSAPI、Kerberos認証の詳細に関しては、この記事の範囲を超えるため割愛します。詳しくは[参考資料のURL](#参考資料)をご確認ください。

今回の動作確認では、Kerberos認証のKDCサーバーとして、Windows 2012サーバーのActive Directoryを利用して動作確認を行います。

# 動作確認用のサーバー構成

今回のお試し環境はこのような感じです。

|FQDN|OS|JDK|構成|
|:---|:---|:---|:---|
|kc-server.example.com|CentOS 7.4.1708|OpenJDK 1.8.0.151|・Keycloakサーバー 3.3.0.CR2|
|{任意}.example.com|Windows Server 2012 R2|－|・Active Directory<br>・DNS|

# Windowsサーバー側の設定

Windowsサーバー側では以下の設定が必要です。

1. Active Directoryの構成
    - ドメインの作成(ドメイン名： EXAMPLE.COM)
    - ドメイン所属ユーザー作成(動作確認用)
2. DNSの構成
    - Keycloakサーバーの正引き、逆引きが可能なように設定
3. Kerberos認証用ユーザー作成
4. keytab ファイルの作成

1と2に関しては、この記事の範疇を超えるので手順は割愛します。
3からの手順を説明します。

## Kerberos認証用ユーザー作成

KeycloakサーバーからKerberos認証実行時に利用されるマップユーザーを作成します。
今回は、Active Directory上に `kc-kerberos`というユーザーを作成します。
パスワードは任意ですが、「パスワードを無期限にする」のチェックをつけておく必要があります。パスワード有効期限が切れたことで、Kerberos認証用ユーザーのパスワードを変更してしまった場合に、keytabファイルも再作成が必要になってしまうためです。

## keytab ファイルの作成

KeycloakサーバーがHTTPサービスへのログインを行う際に必要なkeytabファイルを生成します。
Windows のコマンドプロンプトから、以下の`ktpass`コマンドを実行し、keytabファイルを生成します。

```bat:
ktpass ^
-out kc-kerberos.HTTP.keytab ^
-princ HTTP/kc-server.example.com@EXAMPLE.COM ^
-ptype KRB5_NT_PRINCIPAL ^
-mapuser kc-kerberos ^
-pass P@ssw0rd!
```

以下、ktpassコマンドのコマンドオプションの説明です。

|オプション |説明  |
|:-----------------|:------------------|
| -out     |出力するkeytabファイル名を指定します。|
| -princ   |プリンシパル名を指定します。<br>　命名ルール： HTTP/{KeycloakサーバーのFQDN(Aレコード)}@{Active Directoryドメイン名}|
| -ptype   |KRB5_NT_PRINCIPALを指定を指定します。|
| -mapuser |Kerberos認証用マップユーザー名を指定します。Kerberos認証時のHTTPサービスチケットはこのマップユーザーを経由して発行されるようになります。そのため、マップユーザーを削除したり、パスワードを変更したりすることは基本的にできません。|
| -pass    |マップユーザーのパスワードを指定します。|
| -crypto    |keytabファイルの暗号化形式を {DES-CBC-CRC/DES-CBC-MD5/RC4-HMAC-NT/AES256-SHA1/AES128-SHA1/All} の中から選択します。指定しなかった場合は、デフォルトで RC4-HMAC-NT になります。|

作成したkeytabファイルはKeycloakサーバーの任意のパスにコピーしておきます。

:information_source: keytab ファイルは共有鍵の一種なので、Keycloakサーバー上で Keycloak実行ユーザのみが参照できるように設定しておくことが望ましいです。
:information_source: crypto オプションで、デフォルトのRC4-HMAC-NT 以外の形式を選んだ場合、Active DirectoryのKerberos認証用ユーザーのオプションの変更が必要となるので注意が必要です。たとえば、AES256-SHA1 を選択した場合には、以下のアカウントオプションにチェックが必要となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209129/5a7cb4ba-b6b9-adcf-8717-b94390548eef.png)
:information_source: -princで指定する {KeycloakサーバーのFQDN(Aレコード)} は、DNS上のAレコードである必要があります。たとえば、Aレコード(kc-server-internal.example.com)、CNAMEレコード(kc-server.example.com)というようなDNS定義をしている場合は、AレコードのFQDNを指定する必要があります。

# Keycloakサーバー側の設定

Keycloakサーバー側には「ユーザーフェデレーション」の設定が必要です。
Keycloakの「ユーザーフェデレーション」では
- kerberos
- ldap
の2つが利用できます。

今回は`kerberos`の設定を追加します。

## ユーザーフェデレーション(kerberos)の設定
1. Keycloakの管理コンソールにログインします。
1. 左メニューバーから、`demo`レルムを選択します。
1. 左メニューバーで `ユーザーフェデレーション` をクリックします。ユーザーフェデレーションの一覧が表示されます。
2. 右上の `プロバイダーを追加...` から`kerberos`を選択します。
3. 以下のような設定値を入力し、`保存` ボタンを押下します。
 - コンソール表示名: kerberos
 - 優先度: 0
 - Kerberosレルム: EXAMPLE.COM
 - サーバープリンシパル: HTTP/kc-server.example.com@EXAMPLE.COM
 - KeyTab: {keytabファイルへのフルパス}
 - デバッグ: オン(デバッグ不要の場合は、オフのままでOK)
 - パスワード認証を許可: オフ
 - 初回ログイン時にプロフィールを更新: オフ
![kerberos01.png](https://qiita-image-store.s3.amazonaws.com/0/209129/b39bfc90-dc4c-602a-4095-e1fec2bc1e49.png)



## デバッグログの設定

Kerberos認証が正常に動作しなかった場合に原因が特定できるように、以下のJavaオプションを、Keycloak(Wildfly)の起動オプションに追加します。
設定後はKeycloakサーバーの再起動が必要です。

```sh:/usr/local/keycloak-3.3.0.CR2/bin/standalone.conf
#
# Specify options to pass to the Java VM.
#
if [ "x$JAVA_OPTS" = "x" ]; then
   ・
   ・
   # Kerberos Authentication debug setting
   JAVA_OPTS="$JAVA_OPTS -Dsun.security.krb5.debug=true -Dsun.security.spnego.debug=true"

```

# ブラウザ側の設定

SPNEGO経由でKerberos認証を実行する場合、ブラウザがNegotiationヘッダを送信しても問題ないFQDNかどうかを明示しておく必要があります。そのため、ブラウザで以下の追加設定が必要です。
ブラウザによって設定方法が異なるため、ここでは、IEとFireFoxの場合の設定方法を記載します。(Chromeの設定はIEと共用されます)

- IEの場合
    1. 「ツール > インターネットオプション」を選択
    2. 「セキュリティ」タブの「ローカルイントラネット」を選択
    3. 「サイト」ボタンを押下
    4. 「詳細設定」を押下
    5. 「この Web サイトをゾーンに追加する」に `https://kc-server.example.com` を入力し、「追加」ボタンを押下
    ![kerberos05.png](https://qiita-image-store.s3.amazonaws.com/0/209129/c9f1b6c6-2378-f769-84b2-a6ba695b2331.png)
    6. ブラウザの再起動

:information_source: IE 11以前では、ブラウザの詳細設定の中に「統合 Windows 認証を使用する」というチェックボックスがあり、このチェックをつけておく必要がありましたが、この設定はIE 11ではなくなりました。

- FireFoxの場合
    1.  アドレスバーに `about:config` と入力
    2.  「危険性を承知の上で使用する」ボタンを押下
    3.  「検索」バーに `negotiate` と入力
    4.  以下の設定名に、以下の値を設定
        - network.negotiate-auth.delegation-uris: kc-server.example.com
        - network.negotiate-auth.trusted-uris: kc-server.example.com
    ![kerberos07.png](https://qiita-image-store.s3.amazonaws.com/0/209129/e8d0c5bb-f7a4-7208-3b0c-d25a03b08bb6.png)
    5. ブラウザの再起動


# 動作確認

1. Windowsクライアント端末をEXAMPLE.COMドメインに参加させます。
2. ドメイン所属ユーザーでWindowsにログインを行います。
3. https://kc-server.example.com:8443/auth/realms/demo/account にアクセスします。
4. 2でWindowsにログインしたユーザー名で、Keycloakにもログインされます(Keycloakのログイン画面が表示されることなくユーザー詳細画面が表示されます)
![kerberos06.png](https://qiita-image-store.s3.amazonaws.com/0/209129/b1f6cfa4-5e31-7a99-442a-2edb37a11bdc.png)

「アカウントの編集」画面に表示されているユーザー情報は、Kerberos認証成功後にKeycloakに取り込まれたデータとなっています。ユーザーフェデレーション(kerberos)の場合は、「ユーザー名」と「Eメール」しか設定されません。なおかつ「Eメール」は、AD上のメールアドレスではなく、 {ユーザー名}@{Kerberosレルム} という値が設定される動きになります。
そのため、Active Directoryから上記以外の属性も取り込みたい場合には、ユーザーフェデレーションの `ldap`([13日目の記事を参照](https://qiita.com/izey0306/items/13075668536c62879f4e))を利用する必要があります。こちらもあわせてご確認ください。


:information_source: IE 11 で動作確認する場合は「ツール ＞ 互換表示設定」で、「イントラネット サイトを互換表示で表示する」にチェックがついていると、Keycloakのログイン画面やユーザー詳細画面のデザインが崩れるのでこのチェックをはずしてください。


# まとめ

KeycloakのKerberos認証を使って、Windowsドメイン参加ユーザーでブラウザからKeycloakに透過的にログインする方法を確認しました。
Keycloak側の設定は数箇所しかないため、Windowsサーバー側のActive DirectoryやDNSが適切にセットアップされている環境であれば、比較的容易に動作確認ができるはずです。

Kerberos認証がうまく動作しない場合のほとんどは、Windows(AD/KDC)からHTTPサービスチケットが取得できていないことに起因します。

 - keytabのプリンシパルで指定したFQDNが実際のアクセスのFQDNとあっているか
 - クライアントから FQDN の正引き、逆引きが正しく検索できるか
 - クライアントの hosts ファイルで名前解決されていないか

をなど確認してみてください。


# 参考資料

- [Keycloak Documentation](http://www.keycloak.org/documentation.html)
    - [6.5. Kerberos](http://www.keycloak.org/docs/latest/server_admin/index.html#_kerberos)
- [NRI OpenStandia Keycloak日本語ドキュメント](http://keycloak-documentation.openstandia.jp/master/ja_JP/server_admin/index.html#_kerberos)
- [SPNEGOとは](https://www.ibm.com/support/knowledgecenter/ja/SS9H2Y_7.5.0/com.ibm.dp.doc/spnego_protocol.html)
- [Active Directory: Using Kerberos Keytabs to integrate non-Windows systems](https://social.technet.microsoft.com/wiki/contents/articles/36470.active-directory-using-kerberos-keytabs-to-integrate-non-windows-systems.aspx)

# 脚注

[^openam]: OpenAMのKerberos認証では、OpenAMがWindows(AD/KDC)と直接通信を行い、HTTPサービスへログインを実施するため、OpenAMとWindowsサーバー間のKerberos通信(88ポート)を考慮する必要がありました。Keycloakではこの通信経路の考慮はいりません。