Skip to main content

Rego入門:Regoの基礎

著者
feature cloud security

2023年11月2日

0 分で読めます

このブログシリーズでは、Open Policy Agent(OPA)エンジンの開発者が作ったポリシー言語、Regoをわかりやすく紹介します。Regoを初めて学ぶ方や、ポリシーをコードとして書き始めたい方にぴったりの内容です。

全3回のシリーズで、次の内容を取り上げます。

  • パート1(この記事):Regoの基本概念とOPAの始め方

  • パート2:Rego構文の応用

  • パート3:値とルールの種類

RegoとOPAとは?

Regoは、Open Policy Agent(OPA)フレームワークの開発者が作った宣言型クエリ言語です。Cloud Native Computing Foundation(CNCF)は2019年4月にOPAをインキュベーションレベルのホステッドプロジェクトとして採択し、2021年にインキュベーション段階から卒業しました。

Regoは、ポリシーをコードとして記述するために使われます。バージョン管理やモジュール設計といったプログラミングの手法を、クラウドやInfrastructure as Code(IaC)リソースの評価に適用できます。

OPAは、Regoで記述されたポリシーをコードとして評価するエンジンです。ポリシーの判断と適用を「切り離す」ことで、リソースがポリシーに準拠しているかどうかを判定します。そのため、アプリケーションのコードにチェックをハードコードする必要がありません。

ポリシーをソフトウェアから切り離し、OPAにチェックを任せることで、開発ライフサイクルのスピードと柔軟性が向上します。アプリケーションを再コンパイルしたり、サービスを再デプロイしたりせずに、いつでもポリシーを更新できます。これにより、大規模な開発、コンプライアンス状況の可視性向上、ポリシーのプログラムによる適用が可能になります。また、ポリシーをコードとして導入することで、クラウドやIaCの開発における「シフトレフト」、つまりライフサイクルのより早い段階での導入も実現できます。

Snykでは、Snyk IaC+の標準ルールとカスタムルールをRegoで記述しています。また、内部ではSnykがOPAエンジンを使ってRegoポリシーを評価し、判定を返します。実際、SnykはOPAを使って、セキュリティルールの評価を10億回以上実行してきました。

Regoの仕組み

Regoのような宣言型のクエリ言語では、取得したいデータを記述すると、プログラムがデータソース(inputと呼ばれます)を検索して、一致するものを探します。結果を得るために必要な手順を記述する従来の命令型言語とは異なるアプローチです。宣言型クエリ言語になじみがある方もいるでしょう。おそらく、最も広く使われているのはSQLです。

Regoでは、ポリシーに合格する条件または失敗する条件を記述し、OPAがJSON(またはYAML)の入力ドキュメントを検索して、その条件に一致するデータを探します。

抽象的にはよさそうですが、ルールはどのようなものでしょうか。たとえば、本番環境で仮想ネットワークを作成・削除できるのは、ネットワーク管理者のAliceだけとする社内ポリシーを適用するとします。次のようなルールを記述できます。

allow := true {
  input.user == "alice"
}

このルールが何をし、どのように機能するかは、すぐ後で説明します。まずは、その洗練されたシンプルさに注目してください。

入力

OPAは、あらゆるJSONまたはYAMLドキュメントをinputとして処理できます。先ほどのルール例でinput.userを使っていたことに気づきましたか?inputは特別なJSONドキュメントとして扱われ、グローバルにアクセスできます。つまり、Regoポリシーファイル内のどこからでも参照できます。

前のセクションのルール例に対応する入力ドキュメントを見てみましょう。

{
  "user": "alice"
}

ここでは、現在ログインしているユーザーを表すドキュメントとします。実際の環境では、この入力ドキュメントはKubernetesマニフェストやTerraform planの出力などになります。今後のブログ記事で例をご紹介します。

ルール

入力の形式がわかったところで、ルールの概念を見ていきましょう。Regoでは、ルールは条件付き代入です。各ルールは次の2つの部分で構成されます。

a head {
  and a body
}
  • ヘッドは、変数と、その変数に代入できる値で構成されます。

  • ボディは1つ以上のクエリで構成され、値を変数に代入するために満たす必要がある条件をOPAに伝えます。

ルールは次のように読むことができます。

THIS VARIABLE    :=	HAS THIS VALUE {
    IF THESE CONDITIONS ARE MET
}

つまり、ルールは入力をクエリして条件に一致するものを探し、一致するものが見つかると変数に値を代入します。

先ほどのルール例をもう一度見てみましょう。

allow := true {
  input.user == "alice"
}

上の例では、ヘッド(変数と値)はallow := trueで、ボディ(クエリ)はinput.user == "alice"です。ヘッドとボディを組み合わせるとルールが完成し、次のように読むことができます。

変数allowの値はtrue。IF、userが"alice"と等しい。

Regoでは、:=は代入演算子で、ウォルラス演算子とも呼ばれます。他の言語の等号と同様に、値を変数に代入します。

クエリ

クエリの概念を詳しく見ていきましょう。先ほど説明したように、クエリは確認する条件を表します。つまり、IF文の前半部分にあたります。

たとえば、前のセクションのルール例で使ったクエリは次のとおりです。

input.user == "alice"

この行は、inputドキュメントをクエリし、userが"alice"と等しいかどうかをOPAに確認させます。つまり、IF条件を調べます。

ここには少し補足があります。Regoは宣言型なので、クエリは厳密には「これはこうである」と述べるだけです。OPAは、その記述を真にする入力内のすべての値を見つけます。この例では、クエリは「userプロパティの値はaliceである」と述べています。OPAはuserプロパティを調べ、この記述を真にするユーザーを入力から探します。該当する値があればすべて見つけます(ここではaliceを探しています)。

クエリでのinputの参照

Regoクエリを作成するときは、ドット表記を使って目的のプロパティをたどります。入力ドキュメントの階層は、ピリオドで区切ります。まずinputから始め、ピリオドの後にトップレベルのプロパティ名を追加します(この場合はinput.user)。

ネストされたプロパティを参照するには、そのプロパティにたどり着くまでの階層をすべて指定する必要があります。inputから始め、ピリオドとトップレベルのプロパティ(input.your-property-here)を続け、その後もピリオドとプロパティ名を追加して、クエリ対象のネストされたプロパティにたどり着きます。対象のプロパティが配列の場合は、もう少しお待ちください。後ほど説明します。

たとえば、入力ドキュメントが次のような形式だとします。

{
  "admins": {
    "user": "alice"
  }
}

userはinputの下にネストされたadminsの中にあるため、次のように参照します。

input.admins.user

一方、参照する入力プロパティが配列(リスト)の場合は、ワイルドカード演算子(アンダースコア)を使ってプロパティを指定します。たとえば、入力にuser配列が含まれているとします。

{
  "users": [ "alice", "bob", "carlotta" ]
}

この場合、users配列にaliceが含まれているかどうかを調べるには、次の構文を使います。

allow := true {
  input.users[_] == "alice"
}

ここでは、ワイルドカード演算子によってOPAが配列を反復処理し、要素のいずれかがaliceと等しいかどうかを確認します。反復処理については、今後のブログ記事で詳しく説明します。

同様に、以下のusers配列の要素のいずれかで、adminプロパティの値がtrueかどうかを確認したいとします(表示されている要素は1つだけですが)。

{
  "users": [
    {
      "name": "alice",
      "admin": true
    }
  ]
}

次のような構文を使います。

allow := true {
  input.users[_].admin == true
}

もちろん、IF条件だけでは、それに対応する条件付きアクションがなければあまり役に立ちません。そこで、Regoでの代入、つまりIF文の後半部分を理解しておくと役立ちます。

ルールの評価

ルールの条件付きアクションは、変数への値の代入です。この例では、入力を調べて変数allowにtrueを代入するかどうかを確認します。次のように読むことができます。

allow := true {
  IF THIS CONDITION IS MET
}

変数とは?

変数は、特定の値を参照するものです。ここでは、変数xに値1が代入されています。

x := 1

この後は値の代わりに変数を使えます。Regoにとっては同じことです。

x := 1
y := 2
z := x + y

先ほどの例に戻ると、変数はallowで、代入する値はtrueです。

allow := true

先ほど説明した条件(クエリ)と組み合わせると、ルール全体が完成します。

allow := true {
  input.user == "alice"
}

まとめると、このルールは次のように読めます。

ルールallowの値はtrue。IF、input.userが"alice"と等しい。

実際には、このようなルールはよく使われるため、Regoでは次のように短く記述できます。

allow {
  input.user == "alice"
}

OPAでルールを評価する

ルールと入力ドキュメントが用意できました。次はOPAを使って、入力をルールに照らして評価します。入力がポリシーに準拠しているか、またログイン中のユーザーが本番環境で仮想ネットワークを作成・削除することを許可されているか、実際に確認してみましょう。

OPAを使う方法を2つご紹介します。

  • Rego Playgroundを使う

  • OPAのコマンドラインツールを使う

Rego Playgroundを使う

ルールの記述を始める最も簡単な方法は、OPAのRego Playgroundを使うことです。ルールと入力の作成、テスト、共有ができるインタラクティブなツールです。

基本的な使い方は次のとおりです。

  • ルールを編集するには、ページ左側のルールテキスト欄を使います。

  • 入力を編集するには、ページ右上のInput欄を使います。(有効なJSONである必要があります。)

  • ルールを評価するには、入力欄の上にあるEvaluateボタンを選択します。

  • 評価結果は、右下のOutput欄で確認できます。

  • ルールを共有するには、右上のPublishボタンを選択します。OPAがURLを生成するので、共有相手はルールや入力をテストしたり、変更したりできます。

思う存分試してみましょう。失敗を恐れる必要はありません。何をしても壊れることはなく、Regoの構文が正しくない場合はコンパイラーが知らせてくれます。プレイグラウンドを初期状態に戻したいときはページを再読み込みしてください(公開済みのプレイグラウンドを表示している場合は、公開時の状態に戻ります)。

サンプルのallowルールと入力ドキュメントを使ったプレイグラウンドを用意しました。次のURLからアクセスできます。https://play.openpolicyagent.org/p/SH5ApmfodX 

または、新しいプレイグラウンドを開き、左側のテキストボックスにルールを貼り付けてください。

allow := true {
  input.user == "alice"
}

次に、右上のテキストボックスに入力を貼り付けます。

{
  "user": "alice"
}

社内ポリシーでは、本番環境で仮想ネットワークを作成・削除できるのは、ネットワーク管理者のAliceだけと定められていました。入力ドキュメントは、現在ログインしているユーザーを表しています。ルールは、入力ドキュメントのユーザーがaliceと等しいかを確認し、等しければallowはtrueと評価されます。

Evaluateボタンをクリックしてください。次のような出力が表示されます。

{
  "allow": true
}

つまり、allowはtrueと評価されています。OPAは、入力ドキュメントが社内ポリシーに準拠していると判定しました。ユーザーはAliceなので、本番環境で仮想ネットワークを作成・削除することが許可されています。

OPAのコマンドラインツールを使う

OPAでルールを評価するもう1つの方法は、opaコマンドラインツールを使うことです。opaのインストール手順はOPAのドキュメントで確認できます。インストール後、次の2つを用意してください。

  • ルールを含む.regoポリシーファイルと、ファイルの先頭に記述するpackage rules.check_userのようなパッケージ宣言。ここでは、ポリシーファイルをcheck_user.regoという名前にしています。

  • 入力を含む.jsonファイル。ここでは、入力ファイルをinput.jsonという名前にしています。

この2つが揃ったら、opa evalコマンドを使ってポリシーをコードとして評価できます。

opa eval -i input.json -d check_user.rego "data.rules.check_user" --format pretty

ファイル名は自由に付けられますが、コマンドが次の形式になるようにしてください。

opa eval -i <input file> -d <rule file> "data.<package name>" --format pretty

この例のようにallowの評価結果がtrueになると、Rego Playgroundで表示されたものと同じ出力が表示されます。

{
  "allow": true
}

ここでもOPAは、入力が会社のポリシーに適合している、つまり準拠していることを示しています。これは、現在ログインしているユーザーが本番アカウントで仮想ネットワークを作成および削除することを許可されているという意味です。

ポリシーに準拠しない入力をテストする

入力ドキュメントがポリシーに準拠しない場合は、どのようになるでしょうか。Playgroundまたはローカルの.regoファイルで、入力を次のように変更してください。

{
  "user": "bob"
}

次に、ルールを評価すると(Rego Playgroundで「Evaluate」をクリックするか、前述のopa evalコマンドを実行します)、次の出力が表示されます。

{}

これはどういう意味でしょうか。この例でOPAが「undefined」(つまり空の集合)を返すのは、入力の中に条件input.user == "alice"に一致する値が見つからないためです。結果がないため、集合は空になります。その結果、allowの評価結果はtrueにならず、入力ドキュメントは会社のポリシーに準拠していません。残念、Bob!

次のステップ

Rego for Beginnersシリーズの続きもぜひブログでご覧ください。ANDおよびOR構造、カスタムメッセージ、特殊なキーワードなど、中級レベルのRegoルール構文を解説します。

それまでの間、こちらのリソースもご活用ください。

Regoを使ってSnyk IaC用のカスタムルールを作成する場合は、こちらのドキュメントをご覧ください。Snykに組み込まれたセキュリティルールとコンプライアンス対応ルールセットに加え、IaC+のカスタムルールを使うと、SDLC全体にわたってセキュリティ制御をカスタマイズできます。

IaC+では、IDE、SCM、CLI、CI/CD、Terraform Cloud、およびAWS、Azure、Google Cloudなどのデプロイ済みクラウド環境にまたがる課題UI、ルールセット、ポリシーエンジンにより、コードからクラウドまで、構成上の問題を一元的に確認し、制御できます。 

開発者のために設計されたIaCセキュリティ

Snykは、統合されたポリシー・アズ・コードエンジンにより、SDLCからクラウドでの実行時までInfrastructure as Codeを保護します。すべてのチームが安全に開発、デプロイ、運用できるよう支援します。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

blog feature pypi spoof
Article

オープンソースのメンテナーに、Snyk AI Security Platformをすべて無料で提供

オープンソースのメンテナーは、実際の脆弱性レポートに追われ、優先順位付けや修正、修正版のリリースを迅速に進めるための支援を必要としています。SnykのSecure Developer Programでは、条件を満たすプロジェクトにSnyk AI Security Platformを無料で提供します。

blog feature snyk code green
Blog

型レベルセキュリティ:安全なAIコード生成の未来とは?

セキュア・バイ・デザインの型を使えば、よくあるバグをコンパイル時エラーにできます。この記事では、型レベルセキュリティによってAIが生成する脆弱性を丸ごと防げる可能性を探ります。