KeycloakがActive Directoryに登録されたユーザで認証できるようにする

社内システムを立ち上げるにあたり、前の会社ではWebシステム毎にログインしていて嫌気がさしてたので、是非ともシングルサインオンにしたいと思った。その実現に、昔ちょっとだけ評価したOpenAMとかの情報をググり漁り始めたら、Keycloakの情報の方が目についたので、新しいものに流されやすいのでKeycloakの勉強を始めました。
とは言え、Keycloakに全社員のアカウント情報を登録するのは面倒なので、既存のActive Directoryと連携させる方法を試してみました。

## 構成

![](http://www.plantuml.com/plantuml/png/RP2zJeSm5CNt-nGtx3MEJWOI1X8T64YSQtjD28KsfPgmqXTmStTmSDV9j-cBI4bhmFn7piVl7bhEZbltF2gO1LU4rrSt05muRJ7uZ-1_mlgDV4OQZT8B4S2a9U43FORrFVYtu3-ZpOs1Vd9adxWWRBgkJMhZd4cTErg4qdm8wrVa-jSO6P9uePsj0wAeXEjV2ElUKjou_B6A_PuyOExhgWL84v3j4pCIsPaUip4Y0VAFCdQRkaVfrDhy_rB-sFJzVacJt1xt5m00)

プロトコルは本来、HTTPSとLDAPSでなければならないけど、それは次のステップということで安易なまま。


## Active Directoryサーバ
LDAPとして情報を確認するのに、[こちら](https://gihyo.jp/admin/serial/01/ad2010/0003)で紹介されていた、Windows Serverに元からあるLDPを使用した。

## Keycloakサーバ
### 構築
Docker Composeで、ちょちょいと。

```yml
version: '3'
services:
  keycloak:
    image: jboss/keycloak
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: postgres
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_SCHEMA: public
      DB_PASSWORD: password
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: password
    ports:
      - 50000:8080
  postgres:
    image: postgres
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
```
お試しなのでDBの永続化をしていません。
なお、この時のkeycloakは6.0.1、PostgreSQLは11.3でした。

### 設定
1. Realmを作成。
2. User Federationを選択。
3. Add provider...でLDAPを選択。
4. 開いた画面で以下の項目を設定してSave。

    | 項目 | 値 | 備考 |
    |:---:|:---:|:---:|
    |Console Display Name|${任意の名前}|
    |Import Users|OFF|ONだとActive DirectoryからKeycloak内にアカウント情報をコピーするようだが、今回は認証できるか確認するだけなのでOFF。
    |Edit Mode|READ_ONLY|安全のため、Keycloak側で行ったアカウント情報の更新がActive Directory側に反映されないようにした。
    |Vendor|Active Directory|
    |Username LDAP attribute|sAMAccountName|
    |Connection URL|ldap://${Active Directoryサーバのホスト名}|
    |Users DN|${LDPで調べたユーザが登録されているDN}|
    |Bind DN|${LDPで調べた自アカウントのDN}|
    |Bind Credential |${自アカウントのパスワード}|
    ${}は環境依存の値。
    Import Usersで、
    Edit ModeをREAD_ONLYにすると、
5. 同画面のMappersタブを開き、usernameを選択。LDAP AttributeをsAMAccountNameに変更する。
6. あとはClientsでWebアプリ用の設定を登録。Client Protocolはデフォルトのopenid-connectのままで、Client IDにsample-keycloakと、Root URLにWebアプリのURLを指定。

## Webアプリ
Spring Bootで作成。

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>quo.vaids.megasys</groupId>
	<artifactId>sample-keycloak</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sample-keycloak</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-spring-boot-2-starter</artifactId>
			<version>4.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
```

```yml:application.yml
keycloak:
  realm: ins.local
  auth-server-url: http://${Keycloakが動いている}:50000/auth
  resource: sample-keycloak
  public-client: true
  security-constraints:
    - securityCollections:
        - patterns:
            - /*
      authRoles:
        - uma_authorization
```
あとはsrc/main/resources/staticにindex.htmlを置いた。これでsecurity-constraintsで/
*を指定しているのでWebアプリにアクセスすればKeycloakにリダイレクトされる。

この時、Active Directory側にはロールに該当する情報が存在しないが、authRolesを指定しないとアクセス制限がかからないので悩んだ。が、Keycloak側でRelmのデフォルトとしてoffline_accessとuma_authorizationロールを割り当てているのに気が付き、これを指定した。


## 動作確認
WebアプリにアクセスするとKeycloakにリダイレクトされ、アカウント情報を入力すると、正常にindex.htmlが表示された。

なお、実際にはLDAP AttributeをsAMAccountNameに変更するのを知らず、アカウント情報を入力してもエラーとなった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387890/466e4e08-0beb-a47b-b680-5282ad42cf0e.png)
Keycloakのログを見ると

```
KC-SERVICES0013: Failed authentication:
org.keycloak.models.ModelException:
User returned from LDAP has null username!
Check configuration of your LDAP mappings.
```
が出て、解決まで時間がかかりました。