概要
OpenFGAという認可システムでは、ReBACという認可モデルが採用されています。
ベースとなっているのはZanzibarというGoogle Driveなどので使われているグローバル認証システムで、Google Driveを使ったことがある人はご存じの通り非常に細かい粒度でのアクセス制御が可能です。
ReBACは従来のRBACやABACが持つ課題を解決し、かつ直感的に表現できる認可モデルです。
今回はこのReBACのイメージがつかめるような説明をします。
ReBACとは
従来の認可ロジックは
ユーザーUがオブジェクトOに対してアクションAを実行できるか?
と直接的に表現するのに対し、ReBACでは
ユーザーUがオブジェクトOに対して関係Rを持っているか?
という関係という抽象概念を挟むことで、認可モデルをACLにでもRBACにでもABACにでもできる柔軟性を実現しています。
ReBACを使ってそれぞれのモデルや、よくあるユースケースを表現してみます。
事前知識
Sandbox
OpenFGAはSandbox環境を提供しています。
ここで
- モデルの登録・可視化
- タプルの登録
- 関係のチェック(=権限のチェック)
などができます。
認可モデルはStore単位で登録できます。IDは右上の三点リーダーから取得できます。
また登録した認可モデルはイミュータブル(バージョン管理されている)なので、バージョン(= Authorization Model ID)を指定していれば誰かが上書きしても期待する挙動が返ります。
タプル
OpenFGAを使う中でタプルという言葉が出てきます。
これはユーザやグループなどの主体(subject)と、保護対象となるリソース(object)、そしてそれらの間に存在する関係(relation)を表すための最小単位のデータ構造のことを言います。
例
document:doc123#reader@user:alice
というタプルは
項目 | 内容 |
---|---|
リソース(object) | document:doc123 |
関係(relation) | reader |
主体(subject) | user:alice |
を示しています。
これは「ユーザ(alice)は、このドキュメント(doc123)においてreader(読み取り)関係を持つ」という意味になります。
CLI
CLIも提供されているのでターミナルからも簡単に動作確認ができます。
$ brew install openfga/tap/fga
でインストールします。
sandboxのAPIを使う場合は
$ export FGA_API_URL=https://api.playground-us1.fga.dev
を環境変数に設定しておきます。その他の環境変数は以下を参照してください。
ReBACでACLを表現する
ACLとは
ACLはユーザと権限が直接的に紐付いた認可モデルです。
ReBACでモデリング
ACLをOpenFGAでモデリングしてみます。
model schema 1.1 type user type document relations define viewer: [user] define editor: [user]
とし、実際のユーザに権限を付けるときは次のようなタプルを登録します。
{"user":"user:bob","relation":"editor","object":"document:meeting_notes.doc"}
CLIで登録する際は次のように実行します。
$ export FGA_STORE_ID=xxx $ fga tuple write --store-id=${FGA_STORE_ID} \ user:bob editor document:meeting_notes.doc { "successful": [ { "object":"document:meeting_notes.doc", "relation":"editor", "user":"user:bob" } ] }
権限が付与されているかもチェックできます。
$ fga query check --store-id=${FGA_STORE_ID} \ user:bob editor document:meeting_notes.doc { "allowed":true, "resolution":"" }
欠点
ACLはシンプルである一方、共通化(複数人に同じような権限を持たせる)には不向きです。
ReBACでRBACを表現する
RBACとは
RBACはユーザと権限の間にロールという枠組みを作り、権限はそのロールに紐付けます。
これによって同じロールを持つユーザは同じ権限を持つといった共通化が可能になります。
ReBACでモデリング
RBACをOpenFGAでモデリングしてみます。
model schema 1.1 type user type trip relations define owner: [user] # role define viewer: [user] # role define booking_adder: owner # permission define booking_viewer: viewer or owner # permission
このように権限であるbooking_adder
やbooking_viewer
に、ロールであるowner
やviewer
を紐付けます。
そしてユーザに対しては、ロールに対して紐付けるタプルを設定します。
{"user":"user:bob","relation":"viewer","object":"trip:Europe"}, {"user":"user:alice","relation":"owner","object":"trip:Europe"}
$ export FGA_STORE_ID=xxx $ fga tuple write --store-id=${FGA_STORE_ID} \ user:bob viewer trip:Europe { "successful": [ { "object":"trip:Europe", "relation":"viewer", "user":"user:bob" } ] }
チェック時はロールでなく権限でチェックできる事を確認します。
$ fga query check --store-id=${FGA_STORE_ID} \ user:bob booking_viewer trip:Europe { "allowed":true, "resolution":"" }
欠点
ACLより複数人での運用が向いている一方で、次のような条件が入ってきたりするとその都度ロールを用意しないといけないため、組み合わせによるロール数増大が起きます。
- エンジニアはパブリッククラウドのEditor権限を持てる
- ただし新卒入社して半年間はViewer権限
ReBACでABACを表現する
ABACとは
RBACで問題となった、ユーザ属性に基づいて条件が組み合わさるようなモデルを表現できるのがABACです。
例えば先ほどのRBACのモデルに対して 「xx部門に所属していれば閲覧権限を与える」 のように、ユーザの属性に関する関係性を持たせます。
ReBACでモデリング
model schema 1.1 type user type department relations # ユーザーが所属する部門 define member: [user] type trip relations define owner: [user] # tripの閲覧可能ユーザーは、直接指定されたユーザーまたは、tripの部門に所属するユーザーとする define viewer: [user, department#member] define booking_adder: owner define booking_viewer: viewer or owner
タプルは
- ユーザと属性の紐付け
- 属性とロールの紐付け
の2つを登録します。
$ export FGA_STORE_ID=xxx $ fga tuple write --store-id=${FGA_STORE_ID} \ user:alice member department:hr { "successful": [ { "object":"department:hr", "relation":"member", "user":"user:alice" } ] } $ fga tuple write --store-id=${FGA_STORE_ID} \ department:hr#member viewer trip:Europe { "successful": [ { "object":"trip:Europe", "relation":"viewer", "user":"department:hr#member" } ] }
こうすることでalice
はロールや直接的な権限は持っていませんが、department:hr
に属していることでbooking_viewer
権限を手に入れる事ができます。
$ fga query check --store-id=${FGA_STORE_ID} \ user:alice booking_viewer trip:Europe { "allowed":true, "resolution":"" }
ReBACで継承関係(親子関係)を表現する
例えば同じバックエンドメンバーだとしても、経験年数であったりから次のようにロールを分けたりすることがあります。
- backend_admin
- backend_editor
- backend_viewer
しかしこの場合新しい権限が増えた際に、一部のロールへの追加が漏れるような事が起きます。
そうならないように権限の継承関係が作れたら良いのですが、通常のRBACでは難しいです。
ReBACでモデリング
しかしReBACならそれも可能です。
model schema 1.1 type user type api relations define backend_admin: [user] define backend_editor: [user] or backend_admin define backend_viewer: [user] or backend_editor
そして次のようにタプルを登録します。
{"user":"user:alice","relation":"backend_editor","object":"api:user"} {"user":"user:bob","relation":"backend_viewer","object":"api:user"}
$ export FGA_STORE_ID=xxx $ fga tuple write --store-id=${FGA_STORE_ID} \ user:alice backend_editor api:user { "successful": [ { "object":"api:user", "relation":"backend_editor", "user":"user:alice" } ] } $ fga tuple write --store-id=${FGA_STORE_ID} \ user:bob backend_viewer api:user { "successful": [ { "object":"api:user", "relation":"backend_viewer", "user":"user:bob" } ] }
これでapi:user
というリソースに対して、bobは閲覧権限のみ、aliceは閲覧権限と編集権限の両方を持つことが可能です。
$ fga query check --store-id=${FGA_STORE_ID} \ user:alice backend_editor api:user { "allowed":true, "resolution":"" } $ fga query check --store-id=${FGA_STORE_ID} \ user:alice backend_viewer api:user { "allowed":true, "resolution":"" }
まとめ
ReBACを使うことで従来の権限モデルをどれも直感的に作ることができます。