Docker-Composeを使用してKeyCloak+リバースプロキシをセットアップする

# Docker上で認証情報をまとめたい
[以前書いた記事] (https://qiita.com/tosier/items/8342e6d968f64f0f79a9)の延長上のお話。
(ちなみに、GitLab本体の日本語対応がされたものの、日本語化自体はあまり進んでいないので、上記記事のままだったりします)
GitLabだけならいいけど、いろいろサービスを増やすとなると問題となるのがユーザーの登録と管理。
どこか1カ所でまとめたい。ついでに言うと、SSOまで出来ればうれしい。(GitLabでもできるじゃんというのはおいといて)

# 認証やSSOで検索を行うと...

認証と言えばActiveDirectoryだけど無償でという要件の時は使えないし、LDAPはいろいろなところで使えるけど管理が大変そう。(よくわからない)
あと、SSOで検索すると[OpenAM] (https://www.forgerock.com/platform/access-management/)が出てくるけど、もはや何がオープンなのかわからない上にトライアルのダウンロードの時点で挫折したので使いません。
[Atlassian] (https://ja.atlassian.com/)の[Crowd] (https://ja.atlassian.com/software/crowd)も[Confluence] (https://ja.atlassian.com/software/confluence)を使っているから興味はあるものの、ADとかと一緒で有償なので断念。

# そこでKeyCloak
あきらめてしばらくたった頃に、[KeyCloak] (http://www.keycloak.org/)の[記事](http://www.atmarkit.co.jp/ait/articles/1708/31/news011.html)をみつけた。
OSSで見た目もいいし(モチベーション大事！)、要件をかなり満たしてそうなので、試してみようとおもったのが今回の記事のきっかけ。
ちなみにこんな画面。

![KeyCloak.PNG](https://qiita-image-store.s3.amazonaws.com/0/110594/cf1b4cbb-9bab-aeae-1919-7e4a3fab7ca9.png) ![KeyCloak_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/110594/092a055b-20e5-c4bb-57f9-c03d1a6a24fa.png)

日本語記事がないため、使ってみたいという人に役立ってくれれば。(未検証で自分に合っているかもまだわからない。。。)

# 今回の構成

上にも書いたとおり、機能検証はまだなのでKeyCloakのセットアップまで。
下記構成で行います。

- Ubuntu 16.04 LTS

![KeyCloak_3.png](https://qiita-image-store.s3.amazonaws.com/0/110594/b99d35f2-a306-17ba-5558-6d13c72d8841.png)

特に苦労したのがMariaDB(JDBC)の構成。(ただし、運用上で問題があるためもう少し調整が必要)


## まずはMariaDBの構成
Docker-Composeとは関係のないほうからセットアップ(パスは最終的に配置されるパス)
## JDBCドライバのロード準備
1. [ここ] (https://downloads.mariadb.org/connector-java/)からJDBCドライバ(mariadb-java-client-x.x.x.jar)をダウンロードします。
2. モジュールを読み込むためのXMLファイルを作成します。(nameの中身は後に配置する`layers/keycloak/`配下のファイルパスと合わせる必要があることに注意)

``` xml:/opt/jboss/keycloak/modules/system/layers/keycloak/org/mariadb/jdbc/module.xml
<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.3" name="org.mariadb.jdbc">
    <resources>
        <resource-root path="mariadb-java-client-x.x.x.jar"/>
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>
```

## KeyCloak本体とJDBCの接続設定

- ExampleDSは必要ないので消します。
- `jndi-name`は`default-bindings`の`datasource`とあわせます。
- `datasource`内の`driver`は`drivers`の`driver`と合わせます。

- `drivers`の`xa-datasource-class`はJDBC内にあるクラスを設定すること(必要に応じてjarの展開が必要)
- `connection-url`は[MariaDBのサイト](https://mariadb.com/kb/en/library/about-mariadb-connector-j/)を参考にします。
- **<font color="red">UTF-8の設定は不十分？</red>** (あってるか少しあやしい)

```xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml
...
  <subsystem xmlns="urn:jboss:domain:datasources:5.0">
    <datasources>
      <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
        <connection-url>jdbc:mariadb://keycloak-mariadb:3306/keycloak?useUnicode=true&amp;characterEncoding=utf8</connection-url>

        <driver>mariadb</driver>
        <security>
          <user-name>keycloak-db</user-name>
          <password>password</password>
        </security>
      </datasource>
      <drivers>
        <driver name="mariadb" module="org.mariadb.jdbc">
          <xa-datasource-class>org.mariadb.jdbc.MariaDbDataSource</xa-datasource-class>
        </driver>
      </drivers>
    </datasources>
  </subsystem>
...
  <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/KeycloakDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
...
```

もしサブディレクトリを変更したい場合は`web-content`の中身を書き換えます。

```xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml
...
  <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
    <web-context>keycloak</web-context>
...
```

### 運用上の問題点
実はこの設定だけでは何もせずに時間が経過するとConnection Timeoutが発生して再起動しないと認証出来ないという問題がある様子。
タイムアウトを伸ばすか、SQLによるハートビートを行う必要がある？


# リバースプロキシの設定
リバースプロキシを使用する場合はリバースプロキシがKeyCloakに情報を渡す必要があります。(サーバー名の`keycloak`はdocker-composeで指定)

```nginx:/etc/nginx/conf.d/reverseproxy.conf
...
    location /keycloak {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://keycloak:8080/keycloak;

        access_log /var/log/nginx/keycloak_access.log;
        error_log /var/log/nginx/keycloak_error.log;
    }
...
```

# docker-composeの準備
## フォルダ構成
docker-compose用のディレクトリ構成は以下(logsの配置を忘れるとパーミッション不足で落ちるかも)

```
base
│  docker-compose.yml
│
├─keycloak
│  ├─configuration
│  │      standalone.xml
│  │
│  ├─driver
│  │      mariadb-java-client-2.2.0.jar
│  │      module.xml
│  │
│  └─logs
└─nginx
    ├─conf.d
    │      reverseproxy.conf
    │
    ├─html
    │      50x.html
    │      index.html
    │
    └─logs
```

## docker-compose.ymlの準備


``` yaml:docker-compose.yml
version: "2"

services:
# KeyCloak data volume container
  keycloak-data:
    image: library/busybox:latest
    volumes:
      - ./keycloak/database:/var/lib/mysql
      - ./keycloak/driver:/opt/jboss/keycloak/modules/system/layers/keycloak/org/mariadb/jdbc
      - ./keycloak/logs:/opt/jboss/keycloak/standalone/log
      - ./keycloak/configuration/standalone.xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml:ro

# MariaDB for KeyCloak
  keycloak-mariadb:
    image: library/mariadb:10.2
    restart: always
    volumes_from:
      - keycloak-data
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak-db
      MYSQL_PASSWORD: password

# KeyCloak
  keycloak:
    image: jboss/keycloak:3.4.0.Final
    restart: always
    volumes_from:
      - keycloak-data
    links:
      - keycloak-mariadb
    expose:
      - "8080"
    environment:
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: admin
      PROXY_ADDRESS_FORWARDING: "true"

...
```

ここで重要になるのが`keycloak`コンテナの`environment`に`PROXY_ADDRESS_FORWARDING: "true"`を指定すること。
リバースプロキシを経由する場合はこれを設定しないとリダイレクトに失敗します。(standalone.xmlでも設定可能)
また、ボリュームコンテナのファイルの配置はもちろんのこと、データベースのパスワード等も注意すること。

# 実行
これで準備は整いました。
1. `sudo docker-compose up -d`
2. `http://サーバー名/keycloak/admin`にアクセスして、docker-compose.ymlで指定したユーザー名とパスワードでログイン
3. 新しくユーザーを作成し、パスワード設定とadmin権限の付与(どちらも忘れやすいので注意！)を行う。
4. ログアウトした後、作成したユーザーでログインし、2のユーザーを無効化する。

3と4はセキュリティ対策になります。yamlから消してパスワード変更でもいいかと思いますのでお好みで。(試してません)

# 最後に
JDBCが自由に選べる分、設定に時間が掛かったのが難点でした。
あと、書いているうちに疲れてしまって読みにくくなってしまいました。

ただ、見た目が使いやすそうに見えるし、メール認証とかも出来るので今のところかなり好印象です。
今見つかっている問題はメールドメインのホワイトリストが使えるか微妙なところかな？
AD等の連携も出来るのでSSOとかの為だけにも使用できそう？
あとはSSOが設定できるかな。。。(OpenID Connectはできるみたいだけど)

今回は[こちら](https://github.com/tosier/keycloak-mariadb-dc)にコミットしてあります。

# 参考
[KeyCloak Guide Overview] (http://www.keycloak.org/docs/latest/server_installation/index.html)
[nginx - Configure reverse-proxy for Keycloak docker with custom base URL] (https://stackoverflow.com/questions/44624844/configure-reverse-proxy-for-keycloak-docker-with-custom-base-url)