既存のEntra IDをKong Managerの認証に使う

会社のEntra IDをKong Managerの認証に使ったところ、権限の設定が足りなくてdefaultのWorkspaceすら表示されず困ったことがあった。
![20240910165448.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/62d23fcc-9370-9a61-b3cf-a37c10178fa5.png) 

ここでは、これでハマらないためのEntra IDとKong Managerの連携方法をメモしておく。

## 前提
ここでは以下を前提として進める。
- Entra IDが使えてアプリの登録権限がある
- Kong Gatewayは3.7を利用し、ライセンスも保有済み
- Kong GatewayはDockerでlocalhostに起動する
- Kong GatewayはDBありモードで起動する
- ロールはカスタムロールを使用

## 連携手順の確認
連携手順としては以下の手順で行う。
1. Entra IDの設定（アプリの登録、ロールの作成、ロールの紐づけ）
1. Kong GatewayをRBACなしで起動し、ロールを作成
1. Kong GatewayをRBACありで起動し、Entra IDと連携

### EntraID設定
ここでは以下を行う。
1. アプリの登録
2. アプリ内でロールを作成
3. ロールをログインするユーザに紐づけ

まずアプリを登録する。
登録手順については過去の[こちら](https://qiita.com/ipppppei/items/7da1349bb50532b94f54#azure%E5%81%B4%E3%81%AE%E8%A8%AD%E5%AE%9A)の記事で詳細手順を書いているためここでは省略する。
上記手順で実施する場合、リダイレクトURIは`http://localhost:8001/auth`となる点に注意。
グループ要求の設定まで終わったら、以下のようにIssuerやクライアントID、クライアントシークレットの値を環境変数に設定する。
```sh
export AZURE_ISSUER="https://login.microsoftonline.com/f177c1d6-50cf-49e0-818a-a0585cbaxxxx/v2.0"
export AZURE_TENANT_ID=$(sed "s@.*.com/\(.*\)/v2.0@\1@g" <<< "$AZURE_ISSUER")
export AZURE_CLIENT_ID="28563a31-11d2-49ed-90a7-5e5a5753xxxx"
export AZURE_CLIENT_SECRET="BdB8Q~O8ed3yfGzycyqsLIKoDnKFGta9rcxixxxx"
```
クライアントシークレットについてはIDではなく値である点に注意。
またリダイレクトに関するURIも合わせて設定しておく。
```sh
export AZURE_REDIRECT_URI="http://localhost:8001/auth"
export AZURE_LOGIN_REDIRECT_URI="http://localhost:8002"
export AZURE_LOGOUT_REDIRECT_URI="http://localhost:8002"
```
ここで設定した環境変数はDocker起動時にOIDCの設定値として利用する。
アプリの登録が終わったら、アプリ内にロールを作成する。
作成したアプリを選択後、`アプリロール`->`アプリロールの作成`をクリックする。
![20240910170930.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/949b32d6-fe48-bda7-8ff4-8494477f4ab6.png)

ロールの作成画面が出てくるので、全ての項目を埋める。
![20240910170827.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/220fee99-fb88-6640-8c34-2ece0c540bcb.png)
値のところが要注意で、[公式ドキュメント](https://docs.konghq.com/gateway/latest/kong-manager/auth/oidc/mapping/#review-important-values)にも記載があるが、`<Kongのworkspace名>:<Kongのrole名>`で指定しなければならない。
デフォルトでは`super-admin`、`admin`、`read-only`が用意されていて、それと紐づけるのであればKong側でロールを用意する必要がないが、ここでは独自のロールを作るものとして進める。
上記のスクリーンショットの例では値に`default:myrole`と指定することで`default`のWorkspaceにある`myrole`というKongのロールをこのアプリのロールとマッピングさせる設定となる。

次にロールを自身のアカウントに割り当てる。
EntraIDの`エンタープライズアプリケーション`から作成したアプリケーションを選択する。（`アプリの登録`からではない点に注意）
![20240910153051.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/b7ecad4d-be06-434b-112c-64811565c1ff.png)

`ユーザーとグループ`->`ユーザーまたはグループの追加`をクリックし、ユーザーを選択して`割り当て`をクリックする。
`ユーザーとグループ`の画面をリロードすると、作成したロールが表示され、割り当てられていることが分かる。
![20240910172137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/0c1214c8-14b3-1196-d984-a8c9f80d28ed.png)

以上でEntraID側の設定は終了となる。
なお、Kong Managerの認証には3.5以降でOIDC Pluginを使って認証しているようだが、[OIDC Pluginの説明](https://docs.konghq.com/hub/kong-inc/openid-connect/how-to/third-party/azure-ad/#azure-ad-idp-configuration)にある`accessTokenAcceptedVersion`を`2`に変更する設定はなくても動作したのでここでは実施していない。

### Kong Gatewayの設定
ここでは以下を行う。
1. ロールを作成するためにKong GatewayをRBACなしで起動
2. ロールを作成
3. Kong GatewayをRBACありで起動

#### Kong Gatewayの起動（RBACなし)
以下の手順でネットワークの作成とDBの起動・初期化を実施する。
```sh
docker network create kong-net
docker run -d --name kong-database \
  --network=kong-net \
  -p 5432:5432 \
  -e "POSTGRES_USER=kong" \
  -e "POSTGRES_DB=kong" \
  -e "POSTGRES_PASSWORD=kongpass" \
  postgres:13
docker run --rm --network=kong-net \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  -e "KONG_PG_PASSWORD=kongpass" \
  -e "KONG_PASSWORD=test" \
  kong/kong-gateway:3.7.1.2 kong migrations bootstrap
```
ライセンスファイルを環境変数に読み込む。
```sh
export KONG_LICENSE_DATA=$(cat license.json)
```
Kong Gatewayを起動する。
```sh
docker run -d --name kong-gateway \
  --network=kong-net \
  -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" \
  -e "KONG_PG_USER=kong" -e "KONG_PG_PASSWORD=kongpass" \
  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
  -e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
  -e KONG_LICENSE_DATA \
  -p 8000:8000 \
  -p 8001:8001 \
  -p 8002:8002 \
  kong/kong-gateway:3.7.1.2
```
#### ロールの作成
ロールの作成はAPIでも可能だが、ここではKong Managerから行う。
今時点ではRBACを設定していないので、Kong Managerには認証なしでログインできる。
Kong Managerに入り、`Teams`->`Roles`でWorkspaceが表示されるので`default`を選択する。
（ライセンスがない場合は表示されない）
![20240910174011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/af23fb89-2db2-a8e5-440b-c298090da574.png)
右上の`Add Role`を選択し、NameにEntraIDで作成した値（先程の例だと`myrole`）を入力する。
![20240910174131.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5a295fa1-5ab1-7b9c-de50-00506902ba7b.png)

下の`Add Permission`でこのロールに許可される操作を追加する。
ここでは動作検証用に以下のように設定した（他はデフォルト値のまま）。
- Endpoint：`*`
- Actions：`create`,`read`,`update`

`Add Permission to Role`をクリックし、`Create`をクリックして作成する。
作成したロールは以下のようになった。
![20240910174420.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/96dd749e-cf33-d86a-fdad-b99fcc4c974a.png)

ロールの作成が終わったら、Kong Gatewayのみ停止する。
```sh
docker stop kong-gateway
docker rm kong-gateway
```
DBは削除していないため先ほど設定したロールの設定は残っており、後ほどDBに接続して改めて利用する。

#### Kong Gatewayの起動（RBACあり)
次にKong GatewayをRBACありで起動する。
ここでは[こちら](https://docs.konghq.com/gateway/3.7.x/kong-manager/auth/oidc/configure/)のドキュメントに従って設定する。
必要なパラメータは以下の3つである。
|パラメータ|意味|
|---|---|
|`enforce_rbac`|RBACの有効化指定(例：`on`)|
|`admin_gui_auth`|認証方法の指定（例：`openid-connect`）|
|`admin_gui_auth_conf`|認証方法固有の設定を指定|

`admin_gui_auth_conf`はここでは以下のように設定した。
```sh
export KONG_ADMIN_GUI_AUTH_CONF=$(echo -n '{
  "issuer": "'"$AZURE_ISSUER"'",
  "issuers_allowed": ["'"https://sts.windows.net/$AZURE_TENANT_ID/"'"],
  "client_id": ["'"$AZURE_CLIENT_ID"'"],
  "client_secret": ["'"$AZURE_CLIENT_SECRET"'"],
  "redirect_uri": ["'"$AZURE_REDIRECT_URI"'"],
  "scopes": ["openid", "email", "offline_access","'"${AZURE_CLIENT_ID}/.default"'"],
  "login_redirect_uri": ["'"$AZURE_LOGIN_REDIRECT_URI"'"],
  "logout_redirect_uri": ["'"$AZURE_LOGOUT_REDIRECT_URI"'"],
  "admin_claim": "email",
  "authenticated_groups_claim": ["roles"]
}')
```
設定値についていくつか補足する。
`issuers_allowed`は[こちら](https://support.konghq.com/support/s/article/Open-ID-Connect-plugin-fails-with-invalid-issuer-error-when-authenticating-with-Azure-AD)のナレッジベースに従って設定している。設定しないとナレッジベースと同じエラーが出る。
`${AZURE_CLIENT_ID}/.default`をscopeに指定しているのは[こちら](https://docs.konghq.com/hub/kong-inc/openid-connect/how-to/third-party/azure-ad/)のドキュメントを参考に指定している。

Kong GatewayをRBACありで起動する。
```sh
docker run -d --name kong-gateway \
  --network=kong-net \
  -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" \
  -e "KONG_PG_USER=kong" -e "KONG_PG_PASSWORD=kongpass" \
  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
  -e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
  -e KONG_ENFORCE_RBAC=on \
  -e KONG_ADMIN_GUI_AUTH=openid-connect \
  -e KONG_ADMIN_GUI_AUTH_CONF="${KONG_ADMIN_GUI_AUTH_CONF}" \
  -e KONG_LICENSE_DATA \
  -p 8000:8000 \
  -p 8001:8001 \
  -p 8002:8002 \
  kong/kong-gateway:3.7.1.2
```
`localhost:8002`にアクセスすると、OIDCの認証が有効になっていることが分かる。
![20240910180520.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/97e96c1c-dc36-c775-6723-f470acbfe6f0.png)
クリックすると組織の認証画面に移動する。
![20240910180624.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/c682ac6d-6ece-8047-6873-d1027ff3662f.png)
認証突破後、defaultのWorkspaceが表示されればOK。
![20240910180705.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/fa81c7d9-1032-96b3-4f31-b0271b84b752.png)
ロールが上手く当たっていない場合は、冒頭で紹介したようなWorkspaceが何も表示されないような画面になるので、その場合は設定を見直す必要がある。

ロールが上手く当たっているか確認する。
`Teams`->`Admins`からログインしたユーザを選択すると、`myrole`がアタッチされていることが確認できる。
![20240910181211.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/751f8306-aea9-d225-cfc3-9a5bf3b289c2.png)

先ほど設定した権限だと、削除は出来ないため作成→削除と行って作成に成功して削除に失敗することを確認する。
適当にServiceを作成したところ、作成に成功した。
![20240910180906.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/da54fefd-9258-8f4c-821a-1ea0686a0d08.png)

これを削除してみる。
削除しようと`Gateway Service actions`を選択するが、`Delete`が表示されない。

![20240910181019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/c98d1da3-486d-866f-a24e-5d94b2402075.png)

問題なさそうだ。

## まとめ
ロールのマッピングを適切にしないとKong ManagerのOIDC認証は上手く動かず、特にRBACがない状態でカスタムロールとマッピングさせようと思うと、一度RBACなしでロールを作らないと上手く行かない点はハマりそうなので利用時は気をつけると良さそう。