Open Policy Agent(OPA)は、ポリシーによるアクセス制御を疎結合な形で実現できる強力な仕組みである。Regoという宣言的言語でルールを記述し、アプリケーション側からはシンプルな形式でポリシー評価を利用できる。
本記事では、OPAを利用したアクセス制御の代表的なパターンを整理し、それぞれの特徴や適した用途、実装負荷などを比較する。
以下に、あなたが挙げた4つのアクセス制御アプローチをベースに、表形式を調整・拡張した案を示します。もとの3分類を4分類に整理し、SQL生成アプローチとASTアプローチを分離しました。また、責務分離の観点もあわせてアップデートしました。
パターン名 | Regoの役割 | アプリの役割 | 特徴 |
---|---|---|---|
① Allow/Deny 判定(ナイーブアプローチ) | 許可・拒否の真偽値を評価 | 結果に基づいて処理の可否を制御 | 評価が軽量で高速。Rego本来のモデルに忠実 |
② SQL生成アプローチ | 完成されたSQLを生成(テンプレート/埋め込み) | Regoから受け取ったSQLをそのまま実行 | 柔軟だがRegoにSQL依存が生まれる。ポリシーにアプリ依存が混入 |
③ 条件抽出(構造化条件)アプローチ | SQLに使うフィルタ条件(構造体)を返却 | 条件をもとにSQL/ESなどのクエリを生成して実行 | 条件ロジックとデータ処理が明確に分離され、スケーラブル |
④ ASTアプローチ(Partial Evaluation) | 条件式をASTで返却 | ASTをSQL等に変換または別の評価に利用 | 再利用性が高く柔軟だが、実装が複雑で理解コストも高い |
パターン名 | Regoの責務 | アプリの責務 | 責務分離のバランス |
---|---|---|---|
① Allow/Deny 判定(ナイーブアプローチ) | 許可か否かの判定のみ | 許可結果に応じた処理実行 | ✔ 完全に分離。Regoは「Yes/No」だけを返す |
② SQL生成アプローチ | 完成SQLの出力(ロジック+形式を含む) | そのまま実行 | ❌ 責務が混在。RegoにSQL構文知識が必要 |
③ 条件抽出(構造化条件)アプローチ | 許可条件(例:department_id IN [1,2] )の生成 |
条件を元にSQL等を構築・実行 | ✔ 条件ロジックとデータ処理がきれいに分離 |
④ ASTアプローチ(Partial Evaluation) | ポリシーの条件式を抽象構文(AST)で返却 | ASTを解釈・変換してSQL等に適用 | △ 分離はされるが、アプリ側のAST理解・変換実装が必要 |
動的なアクセス制御※を必要とするアプリケーションにおいては、ユーザーが設定した権限情報をどのようにOPAに供給するかが重要な設計ポイントとなる。
※ポリシーだけでアクセス制御が完結する形を静的としたとき、ポリシーと任意の設定情報を扱う形を動的と定義している。
OPAはステートレスなポリシーエンジンであり、評価時に必要なデータを外部から明示的に供給する必要がある。以下に主なアプローチとその特徴を示す。
ポリシー評価に必要なデータ(評価対象ではなく、ポリシー評価のために補足情報など。)をOPAに渡すアプローチについての比較。
アプローチ | 実現性 | メリット | デメリット |
---|---|---|---|
DB保存 → OPA評価 | ◎ | 標準的で柔軟性があり、再利用可能 | 実装がやや複雑 |
OPAに静的データ埋め込み | △ | 実装が単純 | 更新の手間がある |
OPAから都度外部参照 | △ | 動的に取得可能 | 遅延・信頼性の課題があり、推奨されない運用方法 |
動的な権限設定を必要とするユースケースでは、以下の理由からDB保存型のアプローチが最も現実的である:
ユーザー:部署Aと部署Bにアクセス可と設定
↓(保存)
DB:user_role_policies テーブルに保存
↓(評価時)
アプリ or PDP:設定を取得し、input.dataとしてOPAに渡す
↓
OPA:Regoルールで評価
このように、ユーザー設定 → DB保存 → OPA連携という責務の流れを明確に分離することで、柔軟かつ保守性の高いアクセス制御設計を実現できる。
ただ、この記事を書きながら思いついたのだが、DB保存 → OPA評価のアプローチは無駄があるかもしれない。
DBに保存される設定値とポリシーを分けていることがおそらく無駄で、ポリシーにまとめてポリシーとしてデータを保持すれば、アクセス制御フローにおけるロジックがシンプルになる。
sequenceDiagram
participant User as ユーザー
participant App as アプリケーション
participant RoleDB as ストレージ
participant OPA as OPA
participant SQL as SQL構築ロジック
User->>App: APIリクエスト
App->>RoleDB: ロール + 制約情報取得
App->>OPA: ポリシー評価
OPA-->>App: 評価結果
App->>SQL: 評価結果をもとにSQL生成
App->>RoleDB: データ取得
RoleDB-->>App: 結果返却
App-->>User: レスポンス返却
制約情報からSQLを生成すれば良いので、このような場合であればOPAは複雑性の注入を上回るコストメリットを発揮しづらい。
sequenceDiagram
participant User as ユーザー
participant App as アプリケーション
participant RoleDB as ストレージ
participant OPA as OPA
participant SQL as SQL構築ロジック
User->>App: APIリクエスト
App->>RoleDB: ロールと紐づくポリシー取得
App->>OPA: ポリシーを評価
OPA-->>App: 評価結果を返却
App->>SQL: 評価結果からSQLを生成
App->>RoleDB: データ取得
RoleDB-->>App: 結果返却
App-->>User: レスポンス返却
シーケンスとしては一部しか変わっていないので変わらないように見えるが、ポリシーというデータモデルをロールから切り出すことができるため、責務分離がしやすくなる。
つまり、アプリケーション側はSQL生成のロジックのみを持ち、OPAはアクセス制御のための条件を返すことに集中できる。OPAにデータを渡して評価させる基本的なアクセス制御の形式と比べると、SQLフィルタリングのアプローチは結合度がやや高いが、OPAのメリットを活かすことができる。
権限モデルに基づいた全体設計とポリシーというデータモデルをどう設計していくか重要である。
OPAはユーザー設定に基づく(ユーザー設定をinputにする)ようなアクセス制御のユースケースに抜群に相性が良いとは言えないかも知れない。
OPAのinputに期待されるデータはアクセス制御対象の情報で、アクセス制御のためのルールではないような気がしている。
そのようなアプローチではポリシー(rego)ファイルと外部に保存される設定情報が結合するため、ポリシーと設定の両方の変更が必要になってしまう。
それを許容できるかどうかは要件やトレードオフ、解決したいこと次第ではあるが、本来は静的なアクセス制御をする形がOPAのメリットを最大限に活かせる形だとすると最適ではないように思える。
OPAというかポリシーベースのアーキテクチャにおける考慮点かもしれない。
関連書籍