Keycloakでリバプロ型構成を組んでみる（mod_auth_openidc編）


# 本記事でやること
本記事ではApacheでOpenID Connect連携を行うモジュール(mod_auth_openidc)を利用して、Keycloakとリバプロ構成を検証する。
※本記事は、Keycloakのバージョンアップに伴い、[Keycloakでリバプロ型構成を組んでみる](https://qiita.com/yagiaoskywalker/items/44a0bc6034083477628f)の記事を最新化したものです。

mod_auth_openidcは、Apache用の認証・認可のモジュールであり、apacheに組み込むことでOpenID Connect Relying Party (RP)として、OpenID Connect Provider(OP)とID連携を可能にする。また、OAuth 2.0 Resource Serverとしても動作することができる。

Apache自体で動作するアプリケーション
Apacheがリバースプロキシしているアプリケーション
のどちらにおいても、コンテンツの保護および、ID連携を可能である。
Apacheの基本機能はそのまま利用可能なので、複数のリバースプロキシ先を指定したり、URL置換やヘッダ調整、アクセス制御などを組み合わせられるメリットがある。

OPとID連携を行ったユーザーIDはApacheのREMOTE_USER環境変数に連携されるようになり、その他のユーザー情報に関しては、IDトークン内claimの中に含まれているものであればHTTPヘッダから取得することが可能である。

今回は、WSLのUbuntu上でApache2とmod_auth_openidcを利用して、OP(Keycloakサーバー)とID連携の動作確認を行う。

# 動作環境・構成
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/bd7b5768-ff3e-bab7-ea83-55f73161ccfb.png)


| 対象 | FQDN | OS | 構成 |
|:-:|:-:|:-:|:-:|
|  リバースプロキシサーバ |  apache.test.com |  Ubuntu 20.04.6 LTS | 	Apache2 2.4.41  |
|  Keycloakサーバ |  kc-host | Windows10  |  Keycloak 21.1.2 |
|  Webアプリケーションサーバ |  localhost | Windows10  | actix-web  |

# Keycloakサーバ側の設定
Keycloakは、こちらの[セットアップ](https://qiita.com/l_katayose/items/cbaff1395924cf13218c)と[ロール設定](https://qiita.com/tamura__246/items/4290a1035e1adcab733b#13-%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)方法を参考にして、
以下のdemoレルムおよびユーザーが作成されていることを前提とする。

| ユーザー | ロール |
|:-:|:-:|
| user001  | user  |
| admin001  |  user,admin |


## クライアント追加・設定
1.Keycloakの管理コンソールにログインする。
2.左メニューバーから`demoレルム`を選択する。
3.左メニューバーで`クライアント`をクリックすると、クライアント一覧が表示される。
![1-クライアント追加①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/81083827-505b-9d69-602a-e97e1392ceff.png)
4.一覧の上にある`Create Client`ボタンを押下する。
5.各画面で以下のようなクライアント設定値を入力し、`保存`ボタンを押下する。

![1-クライアント追加②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/0daf1874-ad3b-efa9-5b47-edf61c263c43.png)
・Client Type：OpenID Connect
・クライアントID：mod-auth-openidc-test

![1-クライアント追加③.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/eb881148-ecb3-5d71-8023-340046ec70e9.png)
・クライアント認証：ON

![1-クライアント追加④.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/ca4498a6-63ff-e757-a8f6-31c3970b1f5c.png)
・ルートURL：http://apache.test.com
・有効なリダイレクトURI：http://apache.test.com/redirect_uri
・Webオリジン：http://apache.test.com

:::note info
mod_auth_openidcを使う場合、有効なリダイレクトURIは、mod_auth_openidc側の設定のOIDCRedirectURIと同一のものを1つのみを設定する。
mod_auth_openidcでは、OIDCRedirectURIのURLを経由してユーザーが本来アクセスしていたURLに再度リダイレクトを行うため、１つのURLを許可するだけでよい。
:::

6.`クレデンシャル`タブを押下する。
7.mod_auth_openidcの設定で利用するため、`クライアント・シークレット`として表示されている値を控える。
![1-クライアント追加⑤.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/4a450ed6-d67a-fce9-9a9c-1f4ca63ca740.png)


## マッパー追加
上記の操作に引き続き、マッパーの設定を行う。
本設定によりIDトークンへ各ユーザーにアサインされているロールの値が追加され、mod_auth_openidc側で取得できるようになるため、後述のロールによるアクセス制御が可能となる。

1. 作成したクライアントの`クライアント・スコープ`タブを押下する。
![1-マッパ追加①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/016f0ce5-bbde-0d25-2049-f504a7f89ba4.png)
2. スコープ一覧の`mod-auth-openidc-test-ded
icated`のリンクを押下する。
3. `Configure a new mapper`ボタンを押下する。
![1-マッパ追加①-2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/b03f4716-09e7-1afe-ecca-f9e25f054cb9.png)
4. 表示されるマッパータイプの一覧から`User Realm Role`を選択する。
5. 以下のような設定値を入力し、保存 ボタンを押下する。
![1-マッパ追加②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/8014ab2c-6aff-97dc-487d-961c2e72461f.png)
・Name：role
・マルチバリュー：オン
・トークンクレーム名：role
・IDトークンに追加：オン
・アクセストークンに追加：オン
・UserInfoに追加：オン

# Webアプリ設定
特にKeycloakやmod_auth_openidcを意識することなく好きなようにWebアプリケーションをを作成する。今回はRustのWebフレームワークであるactix-webを利用して、以下の3画面があるWebアプリケーションを準備した。

| 画面名 | パス | 補足 |
|:-:|:-:|:-:|
|  ホーム画面 |  / |  認証不要でアクセス可能な画面 |
|  認証済専用画面 | /auth  |  Keycloakによる認証済ユーザーがアクセス可能な画面 |
|  管理者ロール専用画面 | /auth/role  |  adminロールを保持したユーザーのみがアクセス可能な画面 |


# mod_auth_openidcの設定
## Apache インストール
次のコマンドを実行してapacheをインストールする。
```shell:shell
sudo apt install apache2
```
実行後、次のコマンドを実行してサービスを起動する。
```shell:shell
sudo service apache2 start
```
サービス起動後、`curl`等で`http://localhost`へアクセスしてApacheのデフォルトページが表示されればインストール成功。

## mod_proxyを有効化
今回apaheのプロキシ転送機能を利用するため、次のコマンドで`mod_proxy`を有効化する。

```shell:shell
sudo a2enmod proxy_http
```


## /etc/apache2/sites-available/000-default.confの設定
### 設定ファイル全体
```conf:000-default.conf

<VirtualHost *:80>
	ServerName apache.test.com

	ServerAdmin webmaster@apache.test.com
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

        <Location />
                ProxyPass http://webapp-host/
                ProxyPassReverse http://webapp-host/
        </Location>

        <Location /auth>
                AuthType openid-connect
                Require valid-user
        </Location>

        <Location /auth/role>
                AuthType openid-connect
                Require claim "role~.*admin.*$"
        </Location>
</VirtualHost>
```

以下の部分は、プロキシ転送として、`/`のリクエストをすべて`http://webapp-host/`へ転送する設定。

```
        <Location />
                ProxyPass http://webapp-host/
                ProxyPassReverse http://webapp-host/
        </Location>
```

以下の部分は、`/auth`配下のパスへのアクセスをOIDC認証を必要とするよう設定。
```
        <Location /auth>
                AuthType openid-connect
                Require valid-user
        </Location>
```

以下の部分は、`/auth/role`配下のパスへのアクセスを`admin`ロール保持が必要とするよう設定。
```
        <Location /auth/role>
                AuthType openid-connect
                Require claim "role~.*admin.*$"
        </Location>
```

## mod_auth_openidcインストール
Ubuntu 20.04 ではOSで提供されているパッケージがあるため、次のコマンドでインストールする。

```shell:shell
sudo apt install libapache2-mod-auth-openidc
```

## /etc/apache2/mods-available/auth_openidc.confの設定
### 設定ファイル全体
```conf:auth_openidc.conf
OIDCRedirectURI http://apache.test.com/redirect_uri
OIDCCryptoPassphrase {任意のパスフレーズ}
OIDCProviderMetadataURL http://kc-host:8080/realms/demo/.well-known/openid-configuration

OIDCSSLValidateServer Off

OIDCResponseType "code"
OIDCClientID mod-auth-openidc-test
OIDCClientSecret {クライアント・シークレット}

OIDCCookiePath /auth/

OIDCRemoteUserClaim preferred_username
```


### OIDCRedirectURI
リダイレクトURLを設定する。Keycloakで設定したものと同じ内容で設定する。
```
OIDCRedirectURI http://apache.test.com/redirect_uri
```
:::note warn
mod_auth_openidcで保護されているパス上のURLを指定する必要があるが、実際にコンテンツがあるURLを指定しないこと。このURLがOIDCの動作中に利用される予約URLとなり、ユーザーが直接呼び出すことができなくなるため。なお、直接呼び出した場合は、パラメーター不足で500エラーが発生する。
:::

### OIDCCryptoPassphrase
Cookieやキャッシュの暗号化で使用するパスフレーズ。任意の値を設定する。
```
OIDCCryptoPassphrase {任意のパスフレーズ}
```

### OIDCProviderMetadataURL
OpenID Provider(OP)の.well-knownのURLを設定する。今回はKeycloakの.well-knownのURLを設定する。
```
http://kc-host:8080/realms/demo/.well-known/openid-configuration
```

### OIDCSSLValidateServer
SSLを使用する場合に有効なサーバー証明書かどうかをチェックするかどうかを指定する。今回は検証用でチェック不要とするためOffにする。本番環境では、On(デフォルト)とすべき項目となる。

```
OIDCSSLValidateServer Off
```

### OIDCResponseType
OpenID Connectのレスポンスタイプ設定する。今回は認可コードフローとしたいため、`code`を設定する。
```
OIDCResponseType "code"
```

### OIDCClientID
Keycloakで設定したクライアントのクライアントID(client-id)を設定する。
```
OIDCClientID mod-auth-openidc-test
```

### OIDCClientSecret
Keycloak側で設定したクライアントのクライアント・シークレットを設定する。
```
OIDCClientSecret {クライアント・シークレット}
```

### OIDCCookiePath 
mod_auth_openidcで使用するCookieのパスを設定する。今回は認証を必要とするパスを`/auth/`とするため、次のように設定する。
```
OIDCCookiePath /auth/
```

### OIDCRemoteUserClaim
ApacheのREMOTE_USER環境変数に設定したいIDトークンのclaim名を指定する。今回は`preferred_username`を利用するため次のように設定する。
```
OIDCRemoteUserClaim preferred_username
```

### 設定反映
以下のコマンドを実行してOKで表示されれば、設定内容が正しいことを確認できる。
```
sudo apache2ctl configtest
```

以下のコマンドを実行して、サービスを再起動し、設定を反映する。
```
sudo service apache2 restart
```

# 動作検証

## 未ログインでアクセス
未ログイン状態でホーム画面(`/`)へアクセスする。
以下のように特に認証等不要でアクセス可能なことが確認できる。
![2-未ログイン①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/8cd70323-2dbc-ce6e-7182-b168188d56bf.png)

次に、画面上の認証済専用画面(`/auth`)と管理者ロール専用画面('/auth/role')へアクセスする。
認証済ではないため、どちらの画面でも以下のようにKeycloakの認証画面へリダイレクトされ、画面が表示できないこと確認できる。
![2-未ログイン②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/81834092-906c-bdb2-3874-1a816eb9a866.png)

## User001(admin権限なしユーザー)でアクセス
admin権限のないユーザーで認証して制限のかかっているページへアクセスする。
まず、以下のようにKeycloakの認証画面でID、PWを入力してuser001でログインする。
![2-User①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/f94d6c33-1c35-9728-1032-2ba1f277b779.png)
ログイン後、認証済専用画面(`/auth`)へアクセスすると、以下のように認証が必要な画面を表示することができ、リバースプロキシを介して認証状態によるアクセス制御ができることが確認できた。
![2-User②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/50a0ffcb-b0fd-d55c-b05f-3a3eb478ff97.png)

また、この状態で管理者ロール専用画面('/auth/role')へアクセスする。
今度はadminロールを保持していないため、401エラーの画面が表示される。これによりロールがない場合にアクセスが拒否されていることが確認できる。
![2-User③.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/ade766fc-64fb-5cd0-72b8-6d9a6c44a80a.png)

## admin001(admin権限保持ユーザー)でアクセス
最後に、admin001(admin権限ありユーザー)でロール制限されているページへアクセスする。
まず、以下のようにKeycloakの認証画面でID、PWを入力してadmin001でログインする。
![2-Admin①.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/ca32289e-befa-5e75-32eb-e9f0906b36b8.png)

ログイン後、管理者ロール専用画面('/auth/role')へアクセスすると、以下のように認証が必要な画面を表示することができ、リバースプロキシを介してロール保持によるアクセス制御ができることが確認できた。
![2-Admin②.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/21e257ec-5546-4b17-fe37-f33760589881.png)

# 最後に
今回、Ubuntu環境でmod_auth_openidcを利用して、KeycloakとOpenID ConnectでID連携させる方法が確認できた。

基本的なmod_auth_openidcの設定方法に関しては、[Keyclaok公式ドキュメント](https://www.keycloak.org/docs/latest/securing_apps/index.html#_mod_auth_openidc)に記載されている通りであり、あまりハマりポイントはなく設定することができた。

今回はリバースプロキシとして、mod_auth_openidcを検証したが、Keycloakの公式ドキュメントでは、Keycloakプロジェクトで開発されていたLouketo Proxy（旧Keycloak Gatekeeper）の代替OSSとしてOAuth2 Proxyの利用が推奨されているため、そちらもそのうち試してみたい。

# 参考資料
- https://www.keycloak.org/documentation.html
    - https://www.keycloak.org/docs/latest/securing_apps/index.html#_mod_auth_openidc
- https://keycloak-documentation.openstandia.jp/21.1/ja_JP/securing_apps/index.html#_mod_auth_openidc
- https://github.com/OpenIDC/mod_auth_openidc