Chapter 05

📝 宣言的ポリシー記述言語 Rego

Masayoshi MIZUTANI
Masayoshi MIZUTANI
2021.12.31に更新

RegoはPrologの一部の文法によって構成されるDatalogに着想を得て作られた言語です。構造化データを扱えるように拡張されつつ、構文もDatalogに比べるとかなり読みやすくなっています。

しかし近年メジャーに使われるプログラミング言語のほとんどは手続き型であり、宣言型の言語に馴染みがない人も多いかと思います。手続き型と比べると、以下のような点にギャップを感じるのではないかと思います。

  • 記述の順序に意味がない:手続き型の場合、命令文が実行された順序によって状態を維持しますが、Regoでは1つのルール内に記述された命令文(式)の順序に意味がありません。そのため、次の行で代入している変数を前の行で評価することもできます
  • 変数に再代入できない: 一度値を割り当てた変数は値を変えることができません(厳密には違う値を再代入できない)
  • 条件を満たす組み合わせを検証する、という書き方をする: 例えば data = {"a": 0, "b": 1, "c": 2} という構造データから数値が 2 のものがあるかを検証する場合、「キーをiterationさせて値を比較する」という処理はせず、data[_] == 2 という記述になります。(厳密にはちょっと違うのですが) _ がすべてのキーを意味し、式を満たすキーが存在するかどうかを検証します。

この他にも近代的なプログラミング言語からすると直感に反する記述方法(例えば集合への要素の追加)があったりします。そのため、手続き型のプログラミング言語に慣れている人ほど、一歩引いた視点から習得するという姿勢が必要になりそうです。

ポリシーの構成

https://play.openpolicyagent.org/p/mm5faX5bz8
package play

meta = "test policy"

allow {
	input.user == "bob"
	input.path == "/api/test"
}

それではポリシーの具体例を見ていきたいと思います。上記のポリシーは {"user":"alice", "path":"/api/xxx"} のような入力をチェックして操作の許可・不許可を判定するものです。順番に意味を解説します。

package play

ポリシーは必ず package 句が必要です。詳しくは別の記事で紹介しますが、名前空間を分割するために任意のパッケージ名をつけることができます[1]。

meta = "test policy"

(解説のために記述した行なのでポリシーとしての意味はないです)このような形で meta という変数に test policy という文字列を代入できます。こように値を代入されたトップレベルの変数が「判断結果(decision)」として出力されます。

OPAやRegoでは出力に使える・使うべき変数は定まっておらず、連携するツールやシステム側で自由に決めることができます。これがOPAが「汎用的なポリシーエンジン」と言われる所以です。

allow {
	input.user == "bob"
	input.path == "/api/test"
}

この {...} で囲われた部分が「ルール」になります。ルール内には評価式や命令を記述できます。1つのルール内に複数の評価式を持つことができ、それらは AND で評価されます(すべての式が true になればルールは true 扱いになる)。

OPAは基本的にポリシーおよび入力(input)を組み合わせて判定をします。入力は構造データで渡すことができ(OPAサーバを使う場合はJSON)input.<キー名> という形で参照できます。構造データの参照はPythonやRubyに近い形でアクセスできます。上記の例では入力で user と path というキーが渡されることが期待されています。

上記の例では user が bob かつ path が /api/test だった場合のみ、 allow へ true が代入されます。

このルールに対して {"user":"bob", "path":"/api/test"} という入力を渡した場合、以下のような結果が出力されます。

{
	"meta": "test policy",
	"allow": true
}

注意点:スキーマの整合性

先述したとおり、OPAやRegoでは入力と出力の内容についての制約を持ちません[2]。そのため、連携するシステムやツール側で扱うデータのスキーマを定義・管理しつつポリシーの内容と一致させる必要があります。特にOPAをサーバなどの形で連携するツール・システムとは別にデプロイされている場合、ツール・システム側に後方互換性がない変更が入ると全体が正しく機能しなくなることが考えられます。

スキーマを自由に扱える代わりに、連携側とのスキーマの整合性維持が必要なのは疎結合ならではのデメリットであり、注意が必要です。

脚注
  1. 連携する実装によっては決まったパッケージ名を指定しないといけない場合もあります ↩︎

  2. 入力されるデータの型をスキーマとして事前に定義しておく機能は実装されつつあります https://www.openpolicyagent.org/docs/edge/schemas/ ↩︎