OPA/Rego概論
この記事はOPA/Regoアドベントカレンダーの1日目です。
このアドベントカレンダーでは、最近セキュリティエンジニア界隈で注目されつつある汎用的なポリシーエンジンOPA(読み:オーパ)と、OPAで利用するポリシー記述言語Rego(読み:レゴ)について解説していきます。
初日はOPA/Regoについてのざっくりどういうものかを理解してもらうための概論になります。
OPA/Regoとは
OPAはサービスやソフトウェアの意思決定機能を本体と分離するために作られたエンジンです。ここで言う「意思決定」とはいわゆる認可の話だけではなく、様々なことに応用できます。
- Infrastructure as Code で記述された設定に危険な構成(例えばアクセス制御が適切でない)のチェック
- デプロイされたリソースがポリシーに準拠した設定・構成になっているかのチェック
- セキュリティスキャンで検出された結果に対して、独自の深刻度を判定する
- Webサービスなどで、どのロールのユーザがどのリソースにアクセスできるか判定する
- 作成したリソースの種類、設定、所有者などのメタ情報から、リソースの重要度を判定する
上記の通り「あるひとかたまりの入力データに対して、1つの判断結果を出力する」というようなシンプルな判定処理であればほとんどのユースケースに対応できます。情報セキュリティ以外の分野にも利用できると思いますが、こういった「判断」が情報セキュリティの分野でよく求められるため、その領域で注目されています。
逆にインタラクティブややりとりが発生するような処理や、状態を維持しながら処理するなどはOPAの苦手とする領域であり、一般的な手続き型プログラミング言語を用いて実現するほうが良いです。
具体的な機能
OPAができることはとてもシンプルです。事前に用意されたポリシーおよびデータを元に、受け取ったクエリから何らかの判定結果を出力する、というのが主な機能になります。判定結果はOK/NGのような2値ではなく、文字列や数値、さらに構造化データを出力することもできます。
- クエリ: 判定結果を求めるために都度異なる情報を「クエリ」としてOPAに入力します。具体的にはリソースへのアクセスに関するリクエストや設定不備のチェックをしたい設定のデータなどになります。自由なスキーマのJSONで入力することができます。
- ポリシー: Regoで記述された判定結果を導くためのロジックです。
- データ(任意): 都度変更されるものではないが判定するために必要な情報になります。例えばIPアドレスやユーザIDをまとめた許可リストのようなものになります。
- 判定結果:クエリをポリシーに従って処理された結果が出力されます。スキーマはポリシーの内容に依存します。
すごく乱暴に説明すると、「ポリシー(Rego)に従って入力されたJSONを別のJSONで出力するためのエンジン」と理解してもらえればと思います。この自由な入力と出力ができることで、OPAを汎用的なポリシーエンジンとして利用することができます。
OPAを使う方法はいくつか用意されており、大きく以下の3つに分類できます。
-
opa
コマンドによってクエリを指定して評価し、判定結果をCLIで出力する -
opa
コマンドによってHTTPサーバを起動し、クエリをHTTPで送信・判定結果を受信する -
github.com/open-policy-agent/opa/rego
パッケージを使った自作のGoプログラムを使う
詳しくは今後の記事で紹介していきますが、状況に合わせて柔軟に利用することができます。逆に言うとOPAを単独で利用するというユースケースはあまり考えられず、基本的にはなんらかの実装と連携させて利用する形になります。
Regoによるポリシー記述の具体例
シンプルな例として「ロールに admin
が入っていたら allow = true
を返す」というポリシーを以下に示します。(playground で実際に実行することもできます)
ポリシー
default allow = false
allow {
input.roles[_] == "admin"
}
クエリ
{
"user": "m-mizutani",
"roles": ["admin", "developer"]
}
判定結果
{
"allow": true
}
この例は非常にシンプルですが、例えばこれに加えて「どの種類のリソースにアクセスしているか」「どのような操作をしようとしているか」「所属しているグループにどのような権限が付与されているか」などの項目を見るようなロジックを追加できます。こちらも詳しくは今後の記事で紹介していきたいと思います。
OPA・Regoを使うメリット・デメリット
OPA・Regoは便利なツールではありますが、やはりメリット・デメリットはあるため利用する場面は選ぶ必要があると思います。筆者が現状感じているメリット・デメリットを簡単に紹介します。
メリット
- ポリシーと実装を分離できる: 実装に組み込む場合でもサーバとして実行する場合でも、ポリシーと実装を分けた場所で管理できます。多くのソフトウェアではポリシーが実装にハードコーディングで埋め込まれており、その場合はポリシーを変更するためにソフトウェアを再デプロイする必要があります。ポリシーが分離していることで実装全体の再デプロイはせず、ポリシーを提供するOPAサーバや実装が参照するポリシーだけを更新することで、運用が楽になるケースがあると期待されます。また、ポリシーの実行環境がsandbox化されるため、ポリシー変更によってポリシーに関係しない箇所にまで破壊的な影響を及ぼす可能性が少なくなる、というメリットもあります。
- 意思決定に必要な機能だけが提供されている: Regoを端的に説明すると「一般的なプログラミング言語のサブセット、あるいは下位互換」となります。入力されたクエリに対して判定をするロジックを記述するための機能に特化して[1]おり、これによって記述方法が限定されて書き方がある程度は一定になったり、判定ロジック内にシステム全体へ影響がある脆弱性が入り込みにくくなる、という効果が期待できます。
- 自分でポリシーエンジンを実装しなくて良い: 自分が作ったソフトウェアに何らか自由度のある判定ルールをもたせる場合、エンジンを実装しつつそれが正しく動作するかを検証し続ける必要があります。これを一定信頼できる外部モジュールに委託することで、関連する実装を用意する必要がなくなり、開発者が本当にやるべき機能に集中できます
デメリット
- Regoの学習コストが高め: Regoはprologの系譜を持つ宣言型プログラミング言語になります。今日、広く使われているプログラミング言語は大部分が手続き型言語であり、手続き型のみに慣れている開発者にとって宣言型のプログラムを習得するためには一定コストが高くなってしまいがちである、という課題は意識する必要があります。
- ポリシーと実装が疎結合ゆえのミスが起こりうる: ポリシーを疎結合にすることでサービスのデプロイとの依存関係を弱めることができますが、一方で実装と同期がとれずに意図せぬ判定結果を出力してしまうということが起こりえます。例えば新しい種類のロールを実装に追加していないのにポリシー側にそのロールに関する記述がないと予期せぬ許可・拒否が発生しうる可能性があります。これは実装の工夫などで一定回避できますが、実装とポリシーの整合は常に気を配る必要があります。
まとめ
OPA/Regoは汎用性が高く様々な場面で利用できますが、不得意とする場面や他の処理系を使ったほうがよい場面も当然ながらあります。これからOPA/Regoで具体的にどのようなことができ、どのようなユースケースに刺さるのか、ということを紹介していければと思います。
-
実はhttpリクエストを送信する機能もあったりはするので、完全に外部に影響を及ぼさないことは保証できません。 ↩︎
Discussion