この記事は、chota60 ひとりアドベントカレンダーの二日目の記事です。
寝るまでは二日目です(つよいきもち)
今回もニッチなハマりごとの話です。こんなんばっかですね
Keycloak の管理 REST API
Keycloak には API で内部の設定やリソースを変更するための管理用 REST API が用意されています、
実行するためには、アクセストークンの取得、および API へのリクエスト時に Header にアクセストークンを設定することの二点が必要になります。
アクセストークンの取得
ざっくりいえば、権限を持ったユーザーの ID/password もしくは client の client_id/client_secret
を使用することでアクセストークンを取得できます。
curl \
-d "client_id=admin-rest-api" \
-d "client_secret=gGYwFNxwwiv30iSiIjxwQQkpsE8LcSUU" \
-d "grant_type=client_credentials" \
"http://localhost:8080/realms/master/protocol/openid-connect/token"
response はこんな形式です。
{
"access_token": "eyJhbGc{中略}0.eyJleHA{中略}.aCcx{中略}FXg",
"expires_in": 60,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "email profile"
}
管理 REST API の実行
前項目の access_token で取得できた文字列を、REST API 実行時の Authorization Header に格納し、リクエストを組み立ててリクエストを送信します。
curl \
-H "Authorization: Bearer eyJhbGc{中略}0.eyJleHA{中略}.aCcx{中略}FXg" \
"http://localhost:8080/admin/realms/master"
アクセストークンの肥大化
さて、このアクセストークンですが、中身は JWT なのでデコードすると中身を見ることができます。
下記のような構造になっているようです
{
"exp": 1764695747,
"iat": 1764695687,
"jti": "0a679d1d-d40f-4cd1-9fba-0e5913e55bb3",
"iss": "http://localhost:8080/realms/master",
"aud": [
"test-realm",
...
"account"
],
"sub": "563de3db-4e14-468e-b0b6-7766bf79162a",
"typ": "Bearer",
"azp": "admin-rest-api",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"create-realm",
"default-roles-master",
"offline_access",
"admin",
"uma_authorization"
]
},
"resource_access": {
"{realm name}-realm": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
},
...
},
"scope": "email profile",
"email_verified": false,
"clientHost": "192.0.2.1",
"preferred_username": "service-account-admin-rest-api",
"clientAddress": "192.0.2.1",
"client_id": "admin-rest-api"
}
管理権限をまとめて渡していると、resource_access がどんどん増えていく
resource_access には、realm レベルの role も含まれます。
そして、admin の権限を付与していると、ここには "全ての" realm における realm レベルの role が含まれます。
つまりこの単位で増殖・肥大化していくことになります。
"resource_access": {
"{realm 1}-realm": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
},
"{realm 2}-realm": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
},
...
発生してしまった問題
アクセストークンが肥大化するにつれ、リクエストヘッダーが肥大化する
先述した通り、バッチスクリプトのような機構では、取得したアクセストークンを Authorization Header に含めてリクエストを送信します。
したがって、例えば複数の realm に同じ操作を行う場合など、非常に大きなリクエストヘッダーで特定のリクエストが送信されます。
肥大化したリクエストヘッダーは、AWS ALB によってリクエストが弾かれるようになることがある
弾かれるみたいです(敗北)。
滅多に起こらないような事象だと思いますが、特定の Keycloak で多くの realm が必要になったことがあり、今回の問題が発生しました。
AWS の ALB にはリクエストヘッダーのサイズに 16 KB までという制限があります。
この制限に引っかかる場合、どうやら 400 エラーの Request Header Or Cookie Too Large として返却されてしまうようです。
それならどうすべきなのか
とはいえまとめて設定を変更したい場合どうすべきか?という疑問が残ります。
結論はシンプルで、実行したい API ごとに権限の範囲を制限する、ということになるのかなと考えています ![]()
admin の realm role が便利なのは例えば下記の操作をまとめて複数 realm 分やりたい場合です。
- 新規 realm 作成
- 作成した realm 内で client 作成
- 新規 realm についての権限を master realm 上の User Group に付与
この場合、それぞれの工程で必要な権限は分割できるはずです ![]()
結局横着をせず、最小権限の法則に則って対応できるよう、工程も分割するのがよいのかな・・・という所感です。
次にまとまった作業をやるときに考慮しようと思いました。
思いもしない副作用が発生してしまいましたが、スクリプトを活用して楽をするということ自体は正しいはずなので、適宜調整しながらやっていきたいですね。
それでは〜ノシ