keycloakをcloudflare tunnelで使えるようにする

# この記事は
- keycloakをcloudflare tunnelを使って，証明書の設定なしに自宅サーバーで運用する方法
- Docker imageのkeycloakをSSLなしに運用する方法

を記す

# あらすじ
自前で作成したサービスに認証を追加するにあたって，ロールを用いてアクセスの許可・不許可を制御する仕組みを整えたかったため，keycloakを採用した．そこでkeycloakをdocker-composeで起動できるように色々試行錯誤したので，それをまとめる．また，認証基盤を自身が所有しているドメインで公開するために，cloudflare tunnelを使うための設定もまとめる．

# 問題点
### まず何が困るか
開発段階で，実際に運用するサーバーに設置して，VPNでアクセスできるようにしたかったのだが，quay.ioのimageでは，初期設定でSSLがExternalのアクセスには必須となっており，アクセスができなかった．

```Dockerfile
FROM quay.io/keycloak/keycloak:latest

```

```yaml
services:
  keycloak:
    build: 
      context: ./keycloak
      dockerfile: Dockerfile
    tty: true
    stdin_open: true
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: postgres
      DB_DATABASE: ${POSTGRES_DB_NAME}
      DB_USER: ${POSTGRES_USER}
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      KEYCLOAK_ADMIN: ${KC_ADMIN_NAME}
      KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
      TZ: "Asia/Tokyo"
    ports:
      - "8010:8080"
    depends_on:
      - postgres
    volumes:
      - ./keycloak/.data:/opt/keycloak/data

  postgres:
    image: postgres:latest
    environment:
      POSTGRES_DB: ${POSTGRES_DB_NAME}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - ./postgres/.data:/var/lib/postgresql/data
```

記事( https://qiita.com/ktaka1104/items/d15d5bbf87d5aadbe775 )を参考にしつつ，Dockerfileとyamlを作成し，起動すると起動はするが，外部からアクセスできない．ただし，各サービスには，Volumeでホストのディレクトリをマッピングしており，設定およびデータが永続化されるようにしている．（参考: https://blog.nybeyond.com/tech/article/launching-keycloak-with-docker-compose/ )

### 解決方法
keycloakには，`kcadm.sh`というファイルが用意されており，これに適切なパラメタを渡せば，コンソール上でレルムにアクセスし，設定を変更できる．

まず，レルムにログインするコマンドが以下のコマンドである．
```shell
./kcadm.sh config credentials --server <keycloakのURL> --realm master --user <adminのUser Name> --password <adminのパスワード>
```

そして，以下のコマンドを実行すると，SSLなしでアクセスできるようになる．
```shell
./kcadm.sh update realms/master -s sslRequired=NONE
```

(参考: https://www.mtioutput.com/entry/keycloak-admin-httpserror)

:::note warn
セキュリティ的には問題のある操作ですので，リスクがある場合はこの設定を避けてください
:::

今回の場合は，個人の趣味の範囲なので無視しました．

### `docker compose up`のたびにこの設定をしてほしい．
次に，これを`docker compose up`で起動させるたびに自動でしてくれるようにしたいという需要が出てくる．上記で，Volumeをマッピングしてはいるが，実際の環境で初めて実行した際などに，この設定を`docker exec -it <ID> /bin/bash`を叩いて，手動で設定するのは面倒なので，一応設定しておく．

まず，`quay.io/keycloak/keycloak`では，ENTRYPOINTに/opt/keycloak/bin/kc.shがすでに指定されている．これだと，commandで別のshellスクリプトを実行することができない．そのため，DockerfileでENTRYPOINTを独自のshファイルに上書きする．

```Dockerfile:Dockerfile
FROM quay.io/keycloak/keycloak:latest

WORKDIR /opt/keycloak/bin
COPY ./setup.sh .

ENTRYPOINT ["/opt/keycloak/bin/setup.sh"]
```

WORKDIRに下記のシェルスクリプトをコピーし，それをENTRYPOINTとする．

```shell: setup.sh
#!/bin/bash

cd /opt/keycloak/bin

./kc.sh start-dev &

sleep 30
./kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin

./kcadm.sh update realms/master -s sslRequired=NONE

while true
do
sleep 1000
done
```

このシェルスクリプトでは，元のENTRYPOINTで指定されていたshファイルを呼び出して，keycloakを起動するというコマンドをバックグラウンドで実行されるようにしている．


その後，30秒待機し，バックグラウンドでkeycloakが起動するのを待った後，上記に記したコマンドを実行している．ただし，userとpasswordはサンプルである．ここには，yamlで指定したKEYCLOAK_ADMINとKEYCLOAK_ADMIN_PASSWORDと同じものを入力すれば良い．


また，その後の無限ループは，このシェルスクリプトをENTRYPOINTとしているため，これが終了すると，コンテナの実行も終了してしまうのを避けるためである．

これで起動時から自動でSSLなしで接続できるようになる．

# cloudflare tunnelでアクセスできるようにするには
あとは，cloudflaredでアクセスできるようにすれば良い．ただし，上記の設定をしたあとなので，public hostのserviceはhttpを指定するようにする．

cloudflareのDashboardでの設定は省略するが，Zero Trustからtuunelを設定できる．そこで，トークンを取得できるので，それを控えておく．

今回は，Dockerを使用しているので，cloudflaredもDockerのものを使っていく．

```yaml:docker-compose.yaml
services:
  clouflared:
    image: cloudflare/cloudflared:latest
    command: tunnel run
    restart: always
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN}
    network_mode: host
```

yamlは上記のように設定すれば良い．ただし，`network_mode`は他のdocker composeで動いているサービスも公開しようとしていたため，hostのネットワークを使用するように追加している．

:::note info
EXPOSEやportsは設定しなくても，tunnelの通信はできるのでそのままで良いです
:::

また，`TUNNEL_TOKEN`は先ほど控えたトークンを.envに設定しておく．

これで，`docker compose up`し，Dashboardで設定したドメインを使ってアクセスできるようになっていれば，設定完了である．

:::note warn
外からはhttpsでアクセスできるようになっていますが，keycloak自体はhttpでの通信を想定しているため，oauth2-proxyなどを用いる際は，keycloakのURLにはhttpを指定するようにしましょう

整合性が取れないため，oauth2-proxyでエラーになります
:::

# まとめ
以上の設定で，keycloakを証明書をサーバーにインストールすることなく，（見かけは）httpsで運用できるようになりました！