Skip to main content

汎用ポリシー言語としてRegoを使う

著者
Headshot of Dickson Boateng

Dickson Boateng

feature iac drift blue

2022年6月3日

0 分で読めます

ポリシーはあらゆる組織にとって重要な役割を果たしますが、文脈によって意味は大きく異なります。ここでは、組織が意思決定に用いる原則や考え方をポリシーと呼びます。

この記事では、Open Policy Agent(OPA)とそのルール言語であるRegoについて解説し、給与計算マイクロサービス向けのシンプルなポリシーを作成する方法をご紹介します。

ポリシーとは?

ソフトウェアシステムにおいて、ポリシーとはシステムの動作を制御するルールです。コンピューターや人は、次のような疑問に答えるためにポリシーを利用します。

  • ユーザーAはサービスXの設定を変更できるか?

  • アプリケーションXはどのドメインにインストールすべきか?

  • 地理的に誤ったリージョンにあるオペレーションはどれか?

ポリシーの定義方法や適用方法は、ポリシーが適用されるテクノロジーや、ポリシーの明確さなど、いくつかの要因によって異なります。ポリシーが暗黙知となっている場合もあります。そのため、システムを変更したり、本来の動作を理解したりするには、誰かに確認しなければなりません。しばらくすると回答を文書化しますが、その文書もいずれ古くなります。

別の方法として、ポリシーをソフトウェアシステムにハードコードする方法があります。しかし、ポリシーは時間とともに変化します。ほぼ絶え間ない更新や再教育が必要になるだけでなく、ハードコードされたポリシーを変更するには、何を変更すべきか理解するためにコード全体を詳しく確認しなければなりません。ハードコードによって、ポリシーの利用や変更にかかる時間が増えてしまいます。

Open Policy Agentの紹介

従来のポリシー管理手法では、ポリシーが確実に適用される保証がほとんどなく、維持にもコストがかかります。この課題に対する最新のソリューションが、Open Policy Agent(OPA、オーパと発音)です。

OPAは、マイクロサービス、APIゲートウェイ、CI/CDパイプライン、Kubernetesなど、さまざまなシステムでポリシーを定義するためのオープンソースの汎用ポリシーエンジンです。ポリシーをコードとして記述し、意思決定プロセスの一部として利用できます。

OPAでは、システムの動作を制御するルールを定義します。ルールは、次のような疑問に答えるために使われます。

  • ユーザーAはこのサービスにGETリクエストを送信できるか?

  • ユーザーBが閲覧できるレコードはどれか?

  • アプリケーションXはどのサーバーにデプロイすべきか?

ポリシーの判断をリクエストすると、OPAは提供されたルールとデータを分析して応答を生成します。その判断は、リクエスト元のサービスによって適用されます。つまり、ポリシーの判断を下すのはOPAであり、その判断を実行するのはOPAと連携するサービスです。以下の図は、OPAの一般的なワークフローを示しています。

ユーザーのリクエストがサービスに届き、サービスがOPA EngineにJSONポリシークエリを送信し、JSON形式のポリシー判定を受け取る様子を示す図。

OPAのワークフロー全体を理解するために、特定のAPIサービスへのアクセスを許可または拒否するルールを定義する、シンプルなAPI認可のユースケースで、リクエストをどのように処理するか見てみましょう。サービスがAPIリクエストを受信すると、OPAにクエリを送信します。OPAはクエリを既存のポリシーとデータに照らし合わせ、「許可」または「拒否」の判断を返します。最後に、サービスがOPAの判断を適用し、APIリクエストを承認または拒否します。

OPAのポリシー言語:Rego

OPAポリシーは、Rego(レイゴと発音)という高水準の宣言型言語で記述します。Regoを使えば、さまざまな種類のサービス向けに、容易に拡張できるポリシー判断を記述できます。Regoでは、入力として提供されたデータを評価し、それに基づいてポリシーを判断します。Regoはソフトウェアプログラムを作成するためのプログラミング言語ではなく、SQLのようなクエリ言語に似た、ルールを記述する宣言型言語です。

Regoは汎用ポリシー言語であり、さまざまなシステムで利用できます。Regoが扱うのはJSONデータのみなので、必要なデータをJSON形式にすれば、どのサービス向けのポリシーでも記述できます。そのため、システムをまたいだポリシーをRegoで作成できます。

初めてのOPAポリシーを書く

それでは、Regoを使ってシンプルなポリシーを作成し、オンラインの対話型環境であるRego Playgroundでテストしてみましょう。作成するポリシーは、給与計算マイクロサービスで給与情報にアクセスできるユーザーを判定します。

まず、Rego playgroundのメインパネルにある既存のコードをすべて削除し、次のコードに置き換えます。

package play 
default allow = false
# Users can only view their salary info
allow = true { 
    input.method = "GET" 
    input.path = ["getSalary", user_id] 
    input.user = user_id 
}

それでは、上記のコードを順に見て、何が行われているか確認しましょう。

  • ポリシーの1行目はパッケージ名です。すべてのRegoポリシーには、そのポリシーの適用範囲を定義するパッケージ名があります。

  • 次の行は、allowの値がデフォルトでfalseであることを示しています。

  • ハッシュ記号(#)はコメントの開始を表し、コードに関する簡単な説明を記述します。

  • allow = trueは、角括弧内のすべての式がtrueの場合にallowがtrueになることを意味します。

  • 最後に、角括弧内の式は、入力メソッドがGETで、パスが/getSalary/user_idであり、ユーザーがuser_idと一致する場合にリクエストを許可することを意味します。input変数は、Regoに渡すJSONデータを表します。

Rego Playgroundでは、コードを評価してポリシーが想定どおりに動作するか確認できます。入力パネルに次のコードを追加して、リクエストを模擬してみましょう。

{ 
    "method": "GET", 
    "path": ["getSalary","John"], 
    "user": "John" 
}

それでは、Evaluateボタンをクリックして、上記のリクエストにOPAがどのように応答するか確認しましょう。出力パネルには、次のような結果が表示されます。

{
    "allow": true 
}

すべての手順を完了した後のPlaygroundの画面は次のとおりです。

Rego Playgroundのインターフェース。ユーザーが自分の給与を閲覧できるポリシーが表示され、JohnによるGETリクエストの結果は「allow」: trueとなっています。

リクエストのuserをJaneに変更して、ポリシーをもう一度テストしてみましょう。これは、ユーザーが別のユーザーの給与情報を閲覧しようとしていることを意味します。Evaluateをクリックすると、想定どおり次の結果が表示されます。

{
    "allow": false
}

次に、財務部門の従業員がすべてのユーザーの給与情報を閲覧できるよう、ポリシーを更新します。先ほど定義したポリシーに次のコードを追加します。

#finance department workers can view every user's salary info
allow = true { 
    input.method = "GET" 
    input.path = ["getSalary", user_id] 
    finance[input.user] 
} 
finance = {"Ruth","Josh","Vivian"}

新しいポリシーコードでは、financeオブジェクトを定義し、財務部門で働く従業員の名前をすべて追加しました。

userとuser_idに同じ名前(例:Joe)を設定して、ポリシーをテストしてみましょう。ポリシーはtrueを返すはずです。次に、userを財務部門の従業員であるJohnに変更して、ポリシーを実行します。この場合も、trueが返されます。最後に、userをfinanceオブジェクトに登録されていない名前(例:Jane)に変更します。今度は、ポリシーがfalseを返します。

すべてをまとめる

ここまでのコードをすべて組み合わせると、次のようなポリシーになります。

package play 
default allow = false
# Users should have access to view their salary 
allow = true { 
    input.method = "GET" 
    input.path = ["getSalary", user_id] 
    input.user = user_id 
}
allow = true { 
    input.method = "GET" 
    input.path = ["getSalary", user_id] 
    finance[input.user] 
} 
finance = {"John","Mary","Peter","Vivian"}

ポリシーのカスタマイズが拓く未来

OPAとRegoを使ったポリシーの作成方法を理解したら、脆弱性がない状態を維持できるよう、ポリシーを保守してスキャンする必要があります。ポリシーのスキャンにOPAを活用するSnyk Infrastructure as Code (Snyk IaC)なら、簡単なコマンドをいくつか実行するだけで、新しいポリシーや既存のポリシーをスキャンに追加できます。詳しくは、Snyk IaCのドキュメントとSnykでカスタムIaCルールを開発するをご覧ください。

Snykの無料アカウントを作成して、ワークフローに組み込まれたセキュリティチェック、ポリシーガードレール、開発者に使いやすい修正案を活用しながら、IaCの設定ミスを検出・修正しましょう。

ソースコードの段階からインフラを保護

Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。