Help us understand the problem. What is going on with this article?

Keycloakのクライアント・アダプターで認可サービスを試してみる(Tomcat編)

More than 1 year has passed since last update.

今日やること

昨年の Keycloak by OpenStandia Advent Calendar 2017 で、Keycloakのクライアント・アダプター(Tomcat)に関する記事を書きましたが、当時はKeycloak側で行える認可サービス(アクセス制御)について言及ができていませんでした。そのため、今回の記事ではこの認可サービスに焦点をあてて続編を書こうと思います。

まず、Keycloakの認可サービスについて簡単に説明しておきます。
Keycloak の認可サービスでは、大きく分けて2つのアクセス制御方式があります。

  • システム管理者によってアクセス制御を集中管理する方式
  • エンド・ユーザー(リソース・オーナー)自身でアクセス制御を行う方式

前者はいわゆる従来型のアクセス制御方式のことで、システム管理者がアクセス制御を集中管理する方式のことです。後者は、システム管理者だけにアクセス制御を任せるのではなく、エンド・ユーザー(リソース・オーナー)自身にもアクセス制御を行えるようにする比較的新しい方式で、UMA(User-Managed Access)と呼ばれます。UMA について解説すると、この記事がそれだけで終わってしまいそうなので、ここでは詳細は割愛させてもらいます。興味がある方は、UMA の標準化団体である Kantara Initiative が公開している仕様をご参照ください。

 参考: User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization

Keycloak では、どちらのアクセス制御方式においても、この UMA の仕様に基づいた実装が行われていますが、前者のアクセス制御方式に関しては、UMA をカスタマイズしたKeycloakの独自の方式になっており、今回の記事ではこのアクセス制御方式について検証しています。クライアント・アダプターに対して認可の設定を行うことにより、Keycloak側から各リソースに対して、以前より細やかなアクセス制御を行えることがわかると思います。

今回も昨年に引き続き、Tomcat 8のサンプルアプリ(examples)に対してクライアント・アダプター(OpenID Connect版)を導入し、どのようにアクセス制御の集中管理を行なうのかを試してみます。

Keycloak の認可サービスについて

認可サービスの関連用語

今回の記事の中で、頻繁に登場する用語の意味を簡単に説明しておきます。

用語(英語表記) 説明
リソース(Resource) 認可サービスによるアクセス制御で保護対象となるURIを定義したもの
ポリシー(Policy) 認可サービスで利用するアクセス制御メカニズムを定義したもの
アクセス権(Permission) 定義したリソースと定義したポリシーを関連付けたもの
RPT(Requesting Party Token) 認可サーバーが発行する認可サービス関連の情報を含んだ OAuth 2.0 アクセス・トークンのこと
認可サーバー(Authorization Server) クライアントの要求に応じて、RPTを発行するサーバーのこと。ここでは Keycloak を指します
リソース・サーバー(Resource Server) リソース要求に答えるサーバー。ここでは Tomcat を指します
クライアント(Client) リソースの要求を代理できるアプリケーション。ここではクライアント・アダプターを指します
リソース・オーナー(Resource Owner) リソースへのアクセスを許可する対象のこと。ここではブラウザ(エンド・ユーザー)を指します

Keycloakで利用できるアクセス制御メカニズム

Keycloakの認可サービスでは以下の7つの方式によるアクセス制御が可能です。

メカニズム 説明 ポリシーとの対応関係
属性ベースのアクセス制御(ABAC) ユーザーの属性値によるアクセス制御 JavaScriptポリシー
ロールベースのアクセス制御(RBAC) ユーザーの所属するロールもしくはグループによるアクセス制御 Role/Group/JavaScriptポリシー
ユーザーベースのアクセス制御(UBAC) 特定のユーザーにのみ適用されるアクセス制御 Userポリシー
コンテキストベースのアクセス制御(CBAC) コンテキスト(ユーザーの OAuth2 アクセス・トークンや実行環境)によるアクセス制御 JavaScriptポリシー
ルールベースのアクセス制御 JavaScript や JBoss Drools(ビジネスルール管理システム)を利用したアクセス制御 JavaScript/Rulesポリシー
時間ベースのアクセス制御 特定の日付・時間によるアクセス制御 Timeポリシー
SPIによるカスタム・アクセス・コントロール機構(ACMs)のサポート 独自のポリシー実装にも対応します
※ この記事では言及しません
-

各メカニズムを利用したアクセス制御を実現する場合には、対応するポリシーを定義することで、利用が可能になります。
各リソース(URI)に対して、

  • 単一もしくは複数のポリシーを利用
  • 複数のポリシーがある場合、その AND/OR 条件にするか、多数決にするか
  • ポリシーの判定結果の反転

などを自由に組み合わせることが可能なので、web.xmlベースのものより遥かに複雑なアクセス制御が実現可能です。

今回のアクセス制御方式の構成

今回のアクセス制御方式の構成は以下のようになっております。

今回の構成.png

アクセス制御の処理の流れは以下のようになります。

  1. 認証済みのエンド・ユーザーが、/examples アプリの保護リソースのいずれかにアクセスします。
  2. クライアント・アダプターに同梱されている Policy Enforcerは、エンド・ユーザーが該当リソースのアクセス権(RPT)を持っているかチェックします。RPT がまだない場合は、Policy Enforcer は Keycloak のトークンエンドポイントに対して RPT の要求を行います。
  3. Keycloak は、システム管理者により定義された認可設定を参照して、アクセスしてきたエンド・ユーザーに当該リソースへのアクセス権があるかどうかの結果を返します。(許可の場合は、RPTが発行されます)
  4. Policy Enforcer は、Keycloak からのアクセス可否の結果を元に、エンド・ユーザーが要求したリソースを返すか、403 応答を返します。

Keycloak による認可サービスの主なシーケンス

認可サービスによるアクセス制御に関連するシーケンス図は以下のとおりとなります。

Keycloak による認可サービスの主なシーケンス.png

項番 サービスエンドポイント タイミング 用途
(1) /auth/realms/{レルム名}/.well-known/uma2-configuration アダプター起動時 認可サービスで使用される各種エンドポイントを取得
(2) /auth/realms/{レルム名}/protocol/openid-connect/token

POST パラメータ:
 ・grant_type=client_credentials
アダプター起動時 クライアント認証によるアクセス・トークンの取得
(3) /auth/realms/{レルム名}/authz/protection/resource_set アダプター起動時 リソースの一覧(リソースIDの配列)を取得
(4) /auth/realms/{レルム名}/authz/protection/resource_set/{リソースID} アダプター起動時 リソースの詳細(名前、URI、タイプなど)を取得
(5) /auth/realms/{レルム名}/protocol/openid-connect/token

POST パラメータ(抜粋):
 ・audience={クライアントID}
 ・grant_type=urn:ietf:params:oauth:grant-type:uma-ticket
 ・permission={チェックを行うアクセス権ID}
 ・rpt={取得済みのRPT}
ユーザーアクセス時 リソースに応じたアクセス可否の問い合わせ。
PERMIT(許可)であれば、200応答で RPT(Requesting Party Token) が得られます。
DENY(拒否)であれば、403応答になります。

※ 初回アクセス時は、permission や、rpt パラメータは指定されず、すべてのアクセス権に対する可否が事前にチェックされる動きになります

この認可サービスのシーケンス図を見ると

  • クライアント・アダプターの初期化時に認可サービス関連の設定や保護されるリソース情報を取得する
  • リソース・オーナーがリソースにアクセスした時にクライアント・アダプターが認可サーバーにアクセス可否を問い合わせる

という処理が増えていることがわかります。

:information_source: (5) の RPT を取得するための認可リクエストですが、UMA の仕様と Keycloak の実装では若干差があります。UMA では、grant_type と ticket が必須属性ですが、Keycloak では grant_type のみが必須で、ticket は必須ではありません。また、Keycloak では、audience や permission といった UMA の仕様にはないパラメータも使われています。詳細につきましては、以下の双方の認可リクエストの仕様をご参照ください。

 参考: Keycloakの認可リクエストの仕様
 参考: UMAの認可リクエストの仕様

:information_source: (5) のリクエストは初回アクセス時には必ず発生しますが、PERMITの結果が得られたリソースに関しては、一定時間再呼び出しが行われなくなります。そのため、タイミングにより評価結果がPERMITからDENYに切り替わるようなポリシーを利用している場合、拒否されるまでに若干のタイミングのずれが生じることがあります。この点については後述します。

動作確認用のサーバー構成

去年の記事をベースにミドルウェアのバージョンを上げた環境で動作確認しました。去年の古いバージョンでは認可サービスが正しく動かない箇所があったため、実機で確認される方はここに記載したバージョン以上の環境で確認していただくことを推奨します。

環境情報

FQDN OS JDK 構成
kc-server.example.com CentOS 7.5.1804 OpenJDK 1.8.0_191 ・Keycloakサーバー 4.5.0.Final
kc-tomcat.example.com CentOS 7.5.1804 OpenJDK 1.8.0_191 ・Tomcat 8.5.35(examplesアプリを利用)
・Tomcat 8用クライアント・アダプター(OpenID Connect版) 4.5.0.Final

ユーザー定義

ユーザー メール ロール グループ
user001 user001@example.com user openstandia
user002 user002@example.com user nri
user003 user003@test.example.com user top
admin001 admin001@example.com admin openstandia
admin002 admin002@example.com admin nri
admin003 admin003@example.com admin top

グループ定義

  • top
    • nri
      • openstandia

アクセス制御設計

Tomcatに付属するexamplesアプリケーションで、以下のようなアクセス制御をかけることを想定します。この表を見てわかるとおり、認可サービスを利用すると、より複雑なポリシーが定義できることが分かりますね。

URI アクセス制御の設計 利用ポリシー
/* フルアクセス(デフォルト) JavaScriptポリシー(デフォルト)
/jsp/* ・サービス時間(9時~18時)内
もしくは
・adminロールのみ
Time/Roleポリシー
/jsp/jsp2/el/* user001のみ Userポリシー
/jsp/jsp2/simpletag/* 属性に特定の値を持つユーザー JavaScriptポリシー
/jsp/tagplugin/* ・ランチタイム(12時~13時)以外
かつ
・nri グループ配下
Time/Groupポリシー
/websocket/* ・サービス時間(9時~18時)内
・openstandiaグループ
・userロール
での多数決
Time/Group/Role/Aggregatedポリシー

Keycloakサーバー側の設定

以下、去年の環境からの変更点のみ記載します。

認可制御の有効化

  1. Keycloakの管理コンソールにログインします。
  2. 左メニューバーから、demoレルムを選択します。
  3. 左メニューバーで クライアントをクリックします。クライアント一覧が表示されます。
  4. クライアント一覧から kc-tomcatを選択します。
  5. 認可の有効のトグルスイッチをオンにします。
  6. 保存ボタンを押下します。
  7. 認可タブを押下します。

setting_01.PNG

リソースの定義

認可サービスにより、アクセス制御を行うリソース群をすべて定義します。

  1. リソースタブを押下します。
  2. 作成ボタンを押下します。
  3. 以下の表の設定に従い、7つのリソース定義を行います。
名前 タイプ URI
Default Resource urn:kc-tomcat:resources:default /*
jsp Resource /jsp/*
el Resource /jsp/jsp2/el/*
simpletag Resource /jsp/jsp2/simpletag/*
tagplugin Resource /jsp/tagplugin/*
websocket Resource /websocket/*

:information_source: Default Resource はデフォルトで作成されるリソースです。
:information_source: URIはコンテキストパス以降のパスを指定します。複数指定することも可能です。
:information_source: タイプを指定してリソースをグループ化することができます。同じポリシーを適用したいリソースが多数ある場合などは、タイプを設定しておく方がのちのちの設定が楽になります。

jsp Resource の設定例:
setting_02.PNG

ポリシーの定義

各リソースに適用するためのポリシー群をすべて定義します。

Roleポリシーの定義

特定のロールを持つユーザーだけにアクセスを限定するためのポリシーです。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、Roleを選択します。
  3. 以下の表の設定に従い、2つのRoleポリシー定義を行います。
名前 レルムロール 必須
rp_user user チェックあり
rp_admin admin チェックあり

rp_user の設定例:
setting_03.PNG

JavaScriptポリシーの定義

JavaScriptコードによりコンテキストの内容を判定し、アクセスを限定するためのポリシーです。
今回はメールアドレスのドメイン部に "@test.example.com" が含まれるかどうかをチェックしようと思います。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、JavaScriptを選択します。
  3. 名前jp_testuserを設定し、以下のコードを入力して保存します。
var context = $evaluation.getContext();
var identity = context.getIdentity();
var attributes = identity.getAttributes();
var email = attributes.getValue('email').asString(0);

// ロギング
print("email : " + email);

// email に "@test.example.com" という値が入っていれば許可
if (email.contains("@test.example.com")) {
    print("JavaScript Policy Grant!");
    $evaluation.grant();
} else {
    print("JavaScript Policy No Grant!");
}

:information_source: $evaluation.grant() が実行されなかった場合には、DENY の判定になります。
:information_source: print 文により、ロギングできます。処理の流れを確認するためのデバッグなどで有用です。

jp_testuser の設定例:
setting_04.PNG

Timeポリシーの定義

特定の時間帯だけにアクセスを限定するためのポリシーです。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、Timeを選択します。
  3. 以下の表の設定に従い、2つのTimeポリシー定義を行います。
名前 時(from) 時(to) 分(from) 分(to) ロジック
tp_not_lunch-time 12 12 0 59 Negative
tp_service-time 9 17 Positive

tp_not_lunch-time は、ランチタイム以外という条件にしたいので、ロジックNegativeを選んで条件を反転させます。

tp_not_lunch-time の設定例:
setting_05.PNG

Userポリシーの定義

特定のユーザーだけにアクセスを限定するためのポリシーです。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、Userを選択します。
  3. 以下の表の設定に従い、2つのUserポリシー定義を行います。
名前 ユーザー
up_user001 user001
up_admin001 admin001

up_user001 の設定例:
setting_06.PNG

Groupポリシーの定義

特定のグループだけにアクセスを限定するためのポリシーです。
Extend to Children をチェックすれば、グループの親子関係も考慮されます。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、Groupを選択します。
  3. 以下の表の設定に従い、2つのGroupポリシー定義を行います。
名前 グループ Extend to Children
gp_nri nri チェックあり
gp_openstandia openstandia チェックなし

gp_nri の設定例:
setting_07.PNG

Aggregatedポリシーの定義

複数のポリシーを集約するポリシーためのポリシーです。

  1. ポリシータブを押下します。
  2. ポリシーを作成...のプルダウンから、Aggregatedを選択します。
  3. 以下の表の設定に従い、1つのAggregatedポリシー定義を行います。
名前 ポリシーの適用 判定戦略
ap_consensus ・tp_service-time
・gp_openstandia
・rp_user
Consensus

Aggregatedポリシーでは複数ポリシーを組み合わせるため、以下の判定戦略の中から1つを選択する必要があります。ここでは、多数決の動作にしたいので、Consensusを選択します。

判定戦略 説明
Affirmative 個々のポリシー判定のOR条件
Unanimous 個々のポリシー判定のAND条件
Consensus 個々のポリシー判定の多数決。PERMIT/DENYが同数の場合は許可されません。

ap_consensus の設定例:
setting_08.PNG

アクセス権の定義

定義したリソースと定義したポリシーを組み合わせて、実際の挙動に反映されるアクセス権を定義します。

  1. アクセス権タブを押下します。
  2. ポリシーを作成...のプルダウンから、Resource-Basedを選択します。
  3. 以下の表の設定に従い、7つのアクセス権の定義を行います。
名前 リソースタイプに適用 リソースorタイプ ポリシーの適用 判定戦略
Default Permission ON urn:kc-tomcat:resources:default Default Policy Unanimous
jsp Permission OFF jsp Resource ・tp_service-time
・rp_admin
Affirmative
el Permission OFF el Resource up_user001 Unanimous
simpletag Permission OFF simpletag Resource jp_testuser Unanimous
tagplugin Permission OFF tagplugin Resource ・tp_not_lunch-time
・gp_nri
Unanimous
websocket Permission OFF websocket Resource ap_consensus Unanimous

jsp Permission は、2つのポリシーのOR条件で判定したいので、判定戦略はAffirmativeを指定します。

:information_source: Default PolicyDefault Permission と、はデフォルトで作成されているポリシーパーミッションです。Default Policyは、JavaScriptポリシーになっており、無条件でアクセスを許可するコードになっています。

jsp Permission の設定例:
setting_09.PNG

クライアント・アダプター側の設定

WEB-INF/keycloak.json の変更

Keycloakによる認可サービスを有効にするため、WEB-INF/keycloak.json に policy-enforcer の設定を追加します。

/usr/local/apache-tomcat-8.5.35/webapps/examples/WEB-INF/keycloak.json
{
  "realm": "demo",
  "auth-server-url": "https://kc-server.example.com:8443/auth",
  "ssl-required": "none",
  "resource": "kc-tomcat",
  "credentials": {
    "secret": "{Keycloakサーバー側のシークレットの値}"
  },
  "policy-enforcer": {}
}

:information_source: ここでは、policy-enforcer に何も設定を足していませんが、たとえば、Claim Information Point の設定を追加すれば、リソース・オーナーからリクエストされた際のHTTPメソッド、URI、HTTPヘッダー(クッキーやユーザーエージェントなど)、パラメータ、ボディーなど様々な値をポリシーの判定に利用できます。

WEB-INF/web.xmlの変更

今回はKeycloakによる認可サービスを実施するため、以前実施していた web.xml でのロールによるアクセス制御部分はすべて解除して、認証済みユーザーはすべてのURLにフルアクセスできるように設定を変更します。

/usr/local/apache-tomcat-8.5.35/webapps/examples/WEB-INF/web.xml
    ・
    ・
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Area</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>*</role-name>
    </security-role>
    ・
    ・

Tomcatの起動

クライアント・アダプターも準備が整ったので、Tomcat再起動します。

cd /usr/local/apache-tomcat-8.5.35/
./bin/shutdown.sh
./bin/startup.sh

動作確認

実際にTomcatにアクセスして動作を確認してもよいですが、Keycloak 管理コンソールには、評価タブという便利な機能があるので、こちらでポリシー評価の結果を確認したいと思います。

  1. Keycloakの管理コンソールにログインします。
  2. 左メニューバーから、demoレルムを選択します。
  3. 左メニューバーで クライアントをクリックします。クライアント一覧が表示されます。
  4. クライアント一覧から kc-tomcatを選択します。
  5. 認可タブを押下します。
  6. 評価タブを押下します。

setting_10.PNG

:information_source: 評価タブでの注意事項

  • これ以降のアクセス制御の判定結果の表では、複数のユーザーの評価結果をまとめて書いてますが、実際の評価タブでは1ユーザーずつしか確認できません。複数のリソースに対する評価を同時に判定することは可能です。
  • 評価を行うリソース はプルダウンから選択後に、Addボタンを押下して、評価対象として追加する必要があります。

jsp Resource が Add される前の表示例:
setting_11.PNG

jsp Resource が Add された後の表示例:
setting_12.PNG

jsp Resource へのアクセス

■ 設計したアクセス制御ロジック
・サービス時間(9時~18時)内
もしくは
・adminロールのみ

  1. 以下の条件を入力して、評価ボタンを押下します。
  • 10:00(サービス時間内)に評価した場合
ユーザー リソース 判定
user001 jsp Resource => PERMIT
user002 jsp Resource => PERMIT
user003 jsp Resource => PERMIT
admin001 jsp Resource => PERMIT
admin002 jsp Resource => PERMIT
admin003 jsp Resource => PERMIT

判定戦略が Affirmative(ポリシーのOR条件) なので、サービス時間内もしくは、adminロールを持っていればアクセスが許可されます。そのため、サービス時間内であれば、どのユーザーであってもアクセスが許可されます。

user001 で評価した場合の例:
jspPERMIT_user001.PNG

  • 19:00(サービス時間外)に評価した場合
ユーザー リソース 判定
user001 jsp Resource => DENY
user002 jsp Resource => DENY
user003 jsp Resource => DENY
admin001 jsp Resource => PERMIT
admin002 jsp Resource => PERMIT
admin003 jsp Resource => PERMIT

サービス時間外では、adminロールをもっていないユーザーはアクセスが拒否されます。

user001 で評価した場合の例:
jspDENY_user001.png

el Resource へのアクセス

■ 設計したアクセス制御ロジック
・user001のみ

  1. 以下の条件で、評価ボタンを押下します。
  • 10:00(サービス時間内)に評価した場合
ユーザー リソース 判定
user001 el Resource => PERMIT
user002 el Resource => DENY
user003 el Resource => DENY
admin001 el Resource => DENY
admin002 el Resource => DENY
admin003 el Resource => DENY

user001のみがアクセスが許可されます。
評価結果をみると、上位リソース(/* や /jsp/*)のポリシーについては全く判定されていないことがわかります。

user001 で評価した場合の例:
elPERMIT_user001.PNG

  • 19:00(サービス時間外)に評価した場合
ユーザー リソース 判定
user001 el Resource => PERMIT
user002 el Resource => DENY
user003 el Resource => DENY
admin001 el Resource => DENY
admin002 el Resource => DENY
admin003 el Resource => DENY

アクセス時刻によって、評価結果が変わってないことがわかります。しつこいようですが、/jsp/jsp2/el/* にアクセスした場合は、上位リソース(/* や /jsp/*)のポリシーについては評価の対象になりません。上位のポリシーも考慮されると勘違いしてしまうと、誤ったアクセス制御になってしまうことがあるので注意が必要です。

user001 で評価した場合の例:
elPERMIT_user001.PNG

simpletag Resource へのアクセス

■ 設計したアクセス制御ロジック
属性に特定の値を持つユーザー

ユーザー リソース 判定
user001 simpletag Resource => DENY
user002 simpletag Resource => DENY
user003 simpletag Resource => PERMIT
admin001 simpletag Resource => DENY
admin002 simpletag Resource => DENY
admin003 simpletag Resource => DENY

email 属性に "@test.examle.com" が含まれる user003 だけアクセスが許可されます。

user003 で評価した場合の例:
simplePERMIT_user003.PNG

tagplugin Resource へのアクセス

■ 設計したアクセス制御ロジック
・ランチタイム(12時~13時)以外
かつ
・nri グループ配下

  1. 以下の条件で、評価ボタンを押下します。
  • 10:00(ランチタイム以外)に評価した場合
ユーザー リソース 判定
user001 tagplugin Resource => PERMIT
user002 tagplugin Resource => PERMIT
user003 tagplugin Resource => DENY
admin001 tagplugin Resource => PERMIT
admin002 tagplugin Resource => PERMIT
admin003 tagplugin Resource => DENY

判定戦略が Unanimous(ポリシーのAND条件) なので、ランチタイム以外かつ、nriグループ(子グループも含む)のユーザーだけがアクセスが許可されます。
user001/admin001 は、openstandiaグループですが、openstandiaグループはnriグループの子グループなのでアクセスが許可されます。
user003/admin003 は、nriグループではないので、ランチタイム以外であっても、アクセスが拒否されます。

user003 で評価した場合の例:
tagpluginDENY_user003.PNG

  • 12:30(ランチタイム内)に評価した場合
ユーザー リソース 判定
user001 tagplugin Resource => DENY
user002 tagplugin Resource => DENY
user003 tagplugin Resource => DENY
admin001 tagplugin Resource => DENY
admin002 tagplugin Resource => DENY
admin003 tagplugin Resource => DENY

判定戦略が Unanimousなので、ランチタイム内だとどのようなユーザーであっても、アクセスが拒否されます。

user001 で評価した場合の例:
tagpluginDENY_user001.PNG

websocket Resource へのアクセス

■ 設計したアクセス制御ロジック
・サービス時間(9時~18時)内
・openstandiaグループ
・userロール
での多数決

  1. 以下の条件で、評価ボタンを押下します。
  • 10:00(サービス時間内)に評価した場合
ユーザー リソース 判定
user001 websocket Resource => PERMIT
user002 websocket Resource => PERMIT
user003 websocket Resource => PERMIT
admin001 websocket Resource => PERMIT
admin002 websocket Resource => DENY
admin003 websocket Resource => DENY

Aggregated ポリシーの判定戦略が Consensus(ポリシーの多数決) なので、今回の設定では3つのポリシーのうち2つ以上が PERMITとなれば、アクセスが許可されます。そのため、サービス時間内であれば、userロールを持っている user00* は、全員アクセスが許可されます。
admin00* では、openstandiaグループである admin001 だけがアクセスが許可されます。

admin001 で評価した場合の例:
websocketPERMIT.PNG

  • 19:00(サービス時間外)に評価した場合
ユーザー リソース 判定
user001 websocket Resource => PERMIT
user002 websocket Resource => DENY
user003 websocket Resource => DENY
admin001 websocket Resource => DENY
admin002 websocket Resource => DENY
admin003 websocket Resource => DENY

サービス時間外の場合は、userロールかつopenstandiaグループである user001 しかアクセスが許可されません。
サービス時間内は許可されていた admin001 は拒否されます。

admin001 で評価した場合の例:
websocketDENY.PNG

:information_source: Aggregated ポリシーを使った場合、個々のポリシーの判定結果がどうであったかは出力されないので、ぱっと見ではなぜそうなったのかの判断が難しくなります。

Default Resource へのアクセス

リソースに定義した以外の URI(/servlets/* など)にアクセスした場合は、Default Resource へのアクセスとみなされるので、Default Permission により評価される動きになります。

■ 設計したアクセス制御ロジック
フルアクセス(デフォルト)

  1. 以下の条件で、評価ボタンを押下します。
ユーザー リソースタイプ 判定
user001 Default Resource => PERMIT
user002 Default Resource => PERMIT
user003 Default Resource => PERMIT
admin001 Default Resource => PERMIT
admin002 Default Resource => PERMIT
admin003 Default Resource => PERMIT

特に条件なく、すべてのユーザーのアクセスが許可されます(Keycloakへのログインは必要です)。

user001 で評価した場合の例:
defaultPERMIT_user001.PNG

アクセス制御の拒否のタイミングが遅延するパターン

上記の評価タブでは適切な応答を返してくることが確認できますが、実際にクライアント・アダプターを使った動作ではアクセス拒否のタイミングが遅延するパターンがあったので、ここに書いておきます。

前にも少し触れましたが、認可サービスのシーケンス図で示した (5) のアクセス可否を判定するためのリクエストは、毎回発行されるわけではありません。(5) の応答が DENY になったアクセス権に関しては、都度、再問い合わせが行われる動きになりますが、一度、PERMIT になったアクセス権については、5分間は再問い合わせを行いません。これは、アクセス許可となった際に発行されるRPT(Requesting Party Token)の有効期限がデフォルトでは5分間になっているためです(これはアクセス・トークンの有効期限と同様です)
そのため、Timeポリシーを使っている場合に、時間経過によりポリシーの評価がPERMITからDENYに切り替わったとしても、最大で5分間は引き続きアクセスできてしまう可能性があります。判定に遅延が起きる場合と、起きない場合のシーケンス図を以下に記載しました。

アクセス拒否が遅延するパターン.png

アクセス拒否が遅延しないパターン.png

この動き自体は、Timeポリシー以外のポリシーでも発生します。ユーザーの所属するロールやグループなどが変わって、ポリシーの判定がPERMITからDENYに変化することもあるからです。
逆に、ポリシーの判定がDENYからPERMITに変わるようなパターンではこの遅延は起きません。

RPT の有効期限を短くすればこの現象は緩和できそうです。ですがその場合、アクセス可否のチェックが増えるため、パフォーマンスに影響がでます。このあたりはどちらを優先するかのトレードオフになりそうです。
(OpenAM のWebエージェントでもポリシーキャッシュの有効期限があり似たような問題がありました)

まとめ

Keycloak側のクライアント・アダプターの認可設定により、リソースに対して複雑なアクセス制御かける方法をおおまかに理解できました。正直なところ、ここまで複雑なアクセス制御が必要になるパターンはそれほど多くない気がしますが、アクセス制御を集中管理できる点や動的に条件を変更できる点など、いくつかのメリットを感じることができました。アプリケーションにクライアント・アダプターを導入できる環境であれば、一度試してみる価値はあると思いました。

今回、動作確認してみて少し混乱したのが、リソースのURIに親子関係がある場合の動作です。
Keycloak の認可サービスでは、リソースの URI の親子関係は考慮しません。”上位URIに設定されているポリシーは下位の URI のポリシーには影響を与えない(個々で判断される)”と理解しておく必要があります。

また、今回の記事では、認可サービスの中でも、管理者によるアクセス制御の集中管理の部分のみの検証になってしまいました。先に述べたように認可サービスには、エンド・ユーザー自身による認可ワークフローおよび、アクセス制御の管理である UMA(User-Managed Access)の機能があります。次回はこの部分について検証してみたいです。

参考資料

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away