Keycloakコンテナ(jboss/keycloak)の構築手順

### まえがき
社内開発でdockerコンテナを立ててkeycloakを立ち上げているが、自分で1から構築したことなかったのでやってみる。

---
### この記事に書くこと
・Keycloakコンテナ(jboss/keycloak)の構築手順
・「jboss/keycloak」の概要

--- 
### Keycloakコンテナの構築手順

jboss/keycloakの概要について後述するが、すぐ立ち上げたい時用に構築手順を先述する。

#### 手順1：docker-compose.ymlを用意

```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
    ports:
      - 8180:8080
```

#### 手順2：コンテナ起動
・docker-compose.ymlのあるディレクトリ配下で下記コマンドを実行。
・起動に1分以上かかるので待機。

```
docker compose up -d
```

こんな感じのログがまず出力されて

```
Attaching to keycloak-practice_keycloak_1
keycloak_1  | Added 'keycloak' to '/opt/jboss/keycloak/standalone/configuration/keycloak-add-user.json', restart server to load user
keycloak_1  | -b 0.0.0.0
keycloak_1  | =========================================================================
keycloak_1  | 
keycloak_1  |   Using Embedded H2 database
keycloak_1  | 
keycloak_1  | =========================================================================
keycloak_1  | 
keycloak_1  | =========================================================================
keycloak_1  | 
keycloak_1  |   JBoss Bootstrap Environment
keycloak_1  | 
keycloak_1  |   JBOSS_HOME: /opt/jboss/keycloak
・
・
```

こんな感じのログが出たら起動完了。

```
・
・
keycloak_1  | 21:09:48,383 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 64) WFLYUT0021: Registered web context: '/auth' for server 'default-server'
keycloak_1  | 21:09:48,599 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 46) WFLYSRV0010: Deployed "keycloak-server.war" (runtime-name : "keycloak-server.war")
keycloak_1  | 21:09:48,714 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
keycloak_1  | 21:09:48,724 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 15.0.2 (WildFly Core 15.0.1.Final) started in 40834ms - Started 692 of 977 services (686 services are lazy, passive or on-demand)
keycloak_1  | 21:09:48,729 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
keycloak_1  | 21:09:48,730 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
```

#### 手順3：管理画面を開く

・手順2のログの最後にある通り、管理画面は http://127.0.0.1:9990 で公開されている。
・9990はコンテナ内で公開されているポートなので、ローカルPCで公開されているポート8180に置き換えた http://127.0.0.1:8180 にアクセスする。
・問題なければ↓のWelcome Pageが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/7522acad-0305-7196-ada5-a401acef7a33.png)

#### 手順4：管理コンソールにログイン
・「Administration Console」をクリック
・下記画面が表示されるので、docker-compose.yml内で定義したKEYCLOAK_USER、KEYCLOAK_PASSWORDの値を入力して「Sign In」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/7b6f1d4f-ba47-aa72-8281-8a8043432508.png)
・ログインに成功すると、管理コンソールのTOP画面が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/43bf1ca9-bdcf-1c8e-7a98-1783821aeaf7.png)

#### 手順5：Realmの設定
・Realmの作成
・RealmにログインするClientの追加
・RealmのUserに割り当てるRoleの追加
・RealmにログインするUserの追加
・Keycloakが生成するIDトークンにRoles情報が含まれるように設定

##### Realmの作成
・「Add Realm」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/e9e39b6b-4566-dae9-99c8-63725d5024b7.png)
・今回は「user」というrealmを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/fea9afc4-122a-f971-637c-ac1fbf7c965e.png)

##### RealmにログインするClientの追加
・RealmにログインするClientを追加。クライアントアプリとしてNuxtJSを利用する想定で「nuxt-app」というクライアントを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/3f29467b-393e-97e9-9f37-0052d8926a88.png)
・Web Originsに「*」を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/2bbf5edd-fc36-9e27-643f-9772fd332c6a.png)


##### Realm内のUserに割り当てるRoleの追加
・RealmにログインするUserには「user」というロールを割り当てるため、ロールを追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/bc106d18-c097-be29-bf4e-3d6bd8eb1950.png)
・Role一覧に追加されている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/e4a1a070-0f36-f7b2-d666-663869c9114f.png)


##### RealmにログインするUserの追加
・Realmにログインするユーザーを追加する
・必須入力項目のUsernameに「hoge」とだけ入力して作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/e9e73e38-4e67-9c7a-f09c-691797d27c1f.png)
・Users一覧画面に「hoge」が追加されている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/d605e1ac-a72b-ebb7-d3ed-6fece87669aa.png)
・User「hoge」にRole「user」を紐づける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/5743095f-4117-3378-3476-ee5a4c3cae2e.png)

※Roleの紐づけ対象として「Group」もある。
※20人のユーザーに同じRoleの組み合わせを割り当てる場合は、直接Userに割り当てるのではなくGroupに割り当ててそのGroupとUserと紐づける方法が良い。


###### パスワードの設定
・作成した「hoge」ユーザーにパスワードを設定する（Credentialsタブ）
・TemporaryがONだとログインする度にパスワード変更が求められるので、ここではOFFにしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/c2432059-56c6-d5d5-8a75-27f58b19def6.png)

##### Keycloakが生成するIDトークンにRoles情報が含まれるように設定
・ログインに成功するとKeycloakから↓のような文字列が返ってくる。これを「IDトークン」と呼ぶ。
```
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwd2tvQ0JDOFBRanZjbWpIR3ZFWmhKZFBZNHlTeTlMMERzMHRtbHExLThBIn0.eyJleHAiOjE2NDU5MjgyOTcsImlhdCI6MTY0NTkyNzk5NywiYXV0aF90aW1lIjoxNjQ1OTI3OTk2LCJqdGkiOiIzOTk1Y2U0Mi0wZDZkLTRlYzktOTY1MC00MWNhMjNjYWYxZGUiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvdXNlciIsImF1ZCI6Im51eHQtYXBwIiwic3ViIjoiOWFkZmI3YjUtYWIwNS00YTFjLWJhZDgtMTQ0NmM1ZWI2NDk4IiwidHlwIjoiSUQiLCJhenAiOiJudXh0LWFwcCIsIm5vbmNlIjoiYTdhNTJkMDUtN2E4Yy00YjI5LTg4ZGUtYmNmOTQ0OTYzYTE3Iiwic2Vzc2lvbl9zdGF0ZSI6Ijk1MTZjYTM3LTc1NDktNGNiZi1hODcxLTlmMzM3MmJiMzIyZCIsImF0X2hhc2giOiJnOW1ia2F3UXlEMzlJTlNTVnFXTXRRIiwiYWNyIjoiMSIsInNpZCI6Ijk1MTZjYTM3LTc1NDktNGNiZi1hODcxLTlmMzM3MmJiMzIyZCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtdXNlciIsInVzZXIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiaG9nZSJ9.KWnR4FBwB5XcIULlj8HMFSyUyHPyjaxaNx61Sg7c_p3A64r7bkf1ctfAo9YOhSIqEuTv45h3EpxSSI6ZrUDVqPO-gX4lqMBrOHwI-L43jhn8lA9ksR-sIRagyxRLUl3nAlyLGxT3PY9xhKQyn8EencnFHlaMnhkuRCH6MeUKsbBXOMoCcaIPtxf2H_eMjB3QU0wfu8N_eaxtPZTiAKOySxctgYvP-4QbgAnS2UX44NFzCdriStK35i940O4dqWxRWSoSJR5fRlnVrvYvAWc72sH-YOqtWiQWvcbfP0lrQ3ccXxK__kMK5BacInKjDik3fHF-e48n7zhgclg24ZE1Lw
```

※IDトークンについて、下記の記事がとても参考になった。

https://qiita.com/TakahikoKawasaki/items/8f0e422c7edd2d220e06

・↑のIDトークンは「①ヘッダー.②ペイロード.③署名」で構成されており、②ペイロードに認証/ユーザー情報が↓のように格納されている。

```json
{
	"exp": 1645927303,
	"iat": 1645927003,
	"auth_time": 1645926802,
	"jti": "c72743e7-0396-4c33-bcef-3d1acc3eccf9",
	"iss": "http://localhost:8180/auth/realms/user",
	"aud": "nuxt-app",
	"sub": "9adfb7b5-ab05-4a1c-bad8-1446c5eb6498",
	"typ": "ID",
	"azp": "nuxt-app",
	"nonce": "023b0897-76f6-4b6d-8cf0-6701e0a57296",
	"session_state": "b6baa48e-0192-4286-9492-351a51387cee",
	"at_hash": "T5HufySZBmivhsMum_nDuQ",
	"acr": "0",
	"sid": "b6baa48e-0192-4286-9492-351a51387cee",
	"email_verified": false,
	"preferred_username": "hoge"
}
```

・↑のペイロードに「ユーザーhogeはどのロールを割り当てられいるのか」を追加する設定をKeyclaokにて行う。

・Clients > nuxt-app > Mappers で「Create」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/8f951990-1909-f6a8-ceb4-c94c2edef50b.png)

・下記の内容で作成
　- MapperTypeに「User Realm Role」が設定されていること
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/f25f0b25-0560-c7a4-cb2d-5a110b59a188.png)

・Mappersの一覧に「roles」が追加される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/2e175f1c-380d-8de8-79f6-ee38a9443516.png)

・再度IDトークンを取得して、"roles"があることを確認

```json
{
	"exp": 1645927303,
	"iat": 1645927003,
	"auth_time": 1645926802,
	"jti": "c72743e7-0396-4c33-bcef-3d1acc3eccf9",
	"iss": "http://localhost:8180/auth/realms/user",
	"aud": "nuxt-app",
	"sub": "9adfb7b5-ab05-4a1c-bad8-1446c5eb6498",
	"typ": "ID",
	"azp": "nuxt-app",
	"nonce": "023b0897-76f6-4b6d-8cf0-6701e0a57296",
	"session_state": "b6baa48e-0192-4286-9492-351a51387cee",
	"at_hash": "T5HufySZBmivhsMum_nDuQ",
	"acr": "0",
	"sid": "b6baa48e-0192-4286-9492-351a51387cee",
	"email_verified": false,
	"roles": [
		"offline_access",
		"uma_authorization",
		"default-roles-user",
		"user"
	],
	"preferred_username": "hoge"
}
```

##### RealmをIMPORT/EXPORTする

###### EXPORTするための手順
・EXPORT先フォルダがホストPC内のフォルダとなるようVolumesを設定

```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
    ports:
      - 8180:8080
    volumes:
      - ./keycloak/realm-config:/opt/jboss/keycloak/realm-config // Volume設定追加!
```

・下記コマンドを実行。これでVolumes配下のフォルダにEXPORTファイルが出力される。

```
docker exec -it <コンテナ名> /opt/jboss/keycloak/bin/standalone.sh 
　-Djboss.socket.binding.port-offset=100 
　-Dkeycloak.migration.action=export 
　-Dkeycloak.migration.provider=singleFile 
　-Dkeycloak.migration.realmName=<Realm名>
  -Dkeycloak.migration.usersExportStrategy=REALM_FILE 
　-Dkeycloak.migration.file=//opt/jboss/keycloak/realm-config/<出力するJSONファイル名>
```

・管理画面にある「EXPORT」から出力できると思っていたが、ここからだとユーザ情報が出力されない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/a6f81a20-28dd-c8d7-3755-d47d8193ba4f.png)
・公式READMEには↑のコマンドを実行するように記載されている。
https://github.com/keycloak/keycloak-containers/blob/main/server/README.md

###### IMPORTするための手順
・環境変数KEYCLOAK_IMPORTとDB_USERを追加。
　- DB_USERの初期値は''(空文字)
　- KEYCLOAK_IMPORTを指定することでコンテナ起動時にDBにRealm情報が登録されるようになるのだが、この時にDB_USERが''だとDB接続に失敗する。
・先ほどEXPORTしたファイルが格納されているパスを指定

```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
　　　DB_USER: keycloak
　　　KEYCLOAK_IMPORT: /opt/jboss/keycloak/realm-config/realms-user.json
    ports:
      - 8180:8080
    volumes:
      - ./keycloak/realm-config:/opt/jboss/keycloak/realm-config
```

・上記設定を行った上でコンテナ起動すると、realmが設定されていることを確認できる。

#### 手順6：themesのカスタマイズ
・各画面(ログイン画面等)のデザイン(theme)をカスタマイズすることができる。
・カスタマイズするベースとなる雛型ファイルは、Keycloakコンテナ内の`/opt/jboss/keycloak/themes/`配下に置いてある。

　・ftlファイル
　　- `/opt/jboss/keycloak/themes/base`配下
　　- HTMLのテンプレート的な
　・theme.properties
　　- `/opt/jboss/keycloak/themes/keycloak`配下
　　- テンプレートのftlファイルに、propertiesに定義された値を埋め込む。
　・css, imgファイル
　　- `/opt/jboss/keycloak/themes/resources`配下　
　　- ftlファイルで利用するcss, imgファイル

・リポジトリ管理するために、コンテナ内からローカルPCに利用したい雛型ファイルをコピーして編集する。
・ローカルPCにて編集した雛型ファイルをVolumesに配置。
```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
　　　DB_USER: keycloak
　　　KEYCLOAK_IMPORT: /opt/jboss/keycloak/realm-config/realms-user.json
    ports:
      - 8180:8080
    volumes:
      - ./keycloak/realm-config:/opt/jboss/keycloak/realm-config
      - ./keycloak/theme-config/themes/user:/opt/jboss/keycloak/themes/user
```

・カスタマイズ完了したので、あとはKeycloak管理画面よりテーマを指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/0ca29a9b-c276-c6e9-78a2-484e79c8c0bc.png)


・編集したテーマを反映するために`standalone.xml`をVolumesに配置することでキャッシュを無効化する
・`standalone.xml`
　- ～ で取得できる
　- 
```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
　　　DB_USER: keycloak
　　　KEYCLOAK_IMPORT: /opt/jboss/keycloak/realm-config/realms-user.json
    ports:
      - 8180:8080
    volumes:
      - ./keycloak/realm-config:/opt/jboss/keycloak/realm-config
      - ./keycloak/theme-config/themes/user:/opt/jboss/keycloak/themes/user
      - ./keycloak/standalone.xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml
      - ./keycloak/standalone-ha.xml:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml
```


#### お疲れ様でした
・上記の手順を踏まば、Keycloakを利用してログインするための最低限の設定は完了となる（他にも色々あるがここでは割愛）
・下記の環境で動作確認を実施したので、こちらの記事(後日作成予定)に備忘録として残しておく。

　クライアントアプリ：NuxtJS
　認証認可サーバ：Keycloak
　リソースサーバ：SpringBoot
　

---
### 概要

まず公式ドキュメントを見て、大事そうなことをまとめる。
https://hub.docker.com/r/jboss/keycloak/

#### ① Adminユーザーの設定

・Keycloakでは管理画面にログインして色々設定をする。
・**管理画面にログインするために必要なAdminユーザーはデフォルト設定だと作成されない。**

```
By default there is no admin user created so you won't be able to login to the admin console.
```

・Adminユーザーの作り方1「コンテナ起動時に環境変数KEYCLOAK_USER、KEYCLOAK_PASSWORDを指定」

```
docker run -e KEYCLOAK_USER=<USERNAME> -e KEYCLOAK_PASSWORD=<PASSWORD> jboss/keycloak
```

・Adminユーザーの作り方2「起動中のコンテナに環境変数KEYCLOAK_USER、KEYCLOAK_PASSWORDを指定＆再起動」

```
docker exec <CONTAINER> /opt/jboss/keycloak/bin/add-user-keycloak.sh -u <USERNAME> -p <PASSWORD>
docker restart <CONTAINER>
```

・Adminユーザーの作り方3「ファイル経由...環境変数KEYCLOAK_USER_FILE、KEYCLOAK_PASSWORD_FILEを指定」

・docker-compose.ymlで書くとこんな感じ

```yaml
version: "3.7"
services:
  keycloak:
    image: jboss/keycloak:15.0.2
    environment:
      KEYCLOAK_USER: keycloak
      KEYCLOAK_PASSWORD: keycloak
    ports:
      - ${KEYCLOAK_PORT}:8080  -- ローカルPCで8080ポート公開

```


#### ② データベース

・jboss/keycloakがサポートしているDBは「H2, MySQL, PostgreSQL, MariaDB, Oracle, SQLServer」

・デフォルトで使用されるDBは「H2」。↓のように「Using Embedded H2 Database」とコンテナ起動時にログが出る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/7f0a10f5-795f-d880-7bae-749b0a3ab040.png)

・H2以外のDBを利用する場合、環境変数DB_VENDORの指定＆該当DBコンテナを別で立ててKeycloakコンテナと連携する。

※ 本記事ではH2で検証していく。


#### ③ realmの設定

・ログイン直後に表示される画面は「"Master"のRealm設定画面」である。

・左上の「Master」をホバーすると「Add Realm」ボタンが表示され、ここでRealmを追加できる。

・Realmという単語の定義は人によって色々あると思うが、**私は「グループ」という解釈で問題ないと思っている。**

・Realmは同義語/類似語として挙げられる「area, domain」も、グループみたいなもんだろう。

・**「アプリAにログインしたらアプリBにもアクセス可能！」とするなら、「RealmAにログインしたら、アプリA/アプリBの認証OK！」という具合。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/ebdf9fc0-49d6-3c6e-5744-47045751bce8.png)

#### ※他にも重要なポイントはあるので、都度書き足していく。