APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Terraform のポリシー機能、 Sentinel と OPA を比較しながら始めてみる

はじめに

ACSD 安藤です。

Terraform を大規模で使っていくと、一人二人といった少人数のインフラ担当だけで Terraform を書いていくことが大変になり、ある程度の規模のインフラチームもしくは開発チームにも Terraform の書き手を広げていく必要が出ていきます。
そうなると、人によって書きっぷりが違ってしまったり、組織としての標準的な設計から逸脱したリソースやパラメータが設定されてしまうリスクも拡大していき、最近ではある程度 AI が活用でき始めたとはいえ人力でのレビューのコストも増えていきます。

そんな事態に対処するためにはポリシーやガードレールといったものを整備するのが有効で、こんなときに活用できるのが Policy as Code です。
Policy as Code はセキュリティポリシー、コンプライアンスルール、ガバナンスなどの組織のポリシーをコードとして定義し、自動化ツールを使用して機械的に管理・適用する手法です。

Terraform では Policy as Code のツールとして Sentinel と Open Policy Agent (OPA) が対応しています。
いきなり2つ提示されてもどちらを使えばよいか迷ってしまうかと思いますので、それぞれの特徴や書きっぷりをまとめてみました。

HCP Terraform の Policy as Code の対応

前述の通り、 Terraform では Sentinel と Open Policy Agent (OPA) 2 つのツールが対応しています。

developer.hashicorp.com

HCP Terraform では Policy を1つ1つ作成してワークスペースに割り当てることもできますが、複数の Policy をまとめた Policy Sets が利用でき、
Policy Sets は GitHub 等のバージョン管理システムと連携してコードとして管理することができるのでオススメです。

なお HCP Terraform では Free プランから Policy as code が利用できますが、5 Policy / 1 Policy Sets の制限があります。

Sentinel と OPA の比較

HCP Terraform では Policy 機能として、 Sentinel と OSS で Open policy Agent (OPA) がサポートされています。
似た部分が多分にあるツールなので、まずは機能面の比較をしていきます。

概要・言語仕様

Sentinel

Sentinel は HashiCorp が開発している Policy as Code を実装する言語およびフレームワークです。
Terraform の他、同じく HashiCorp が開発している Vault や Consul, Nomad もサポートしています。

www.hashicorp.com

Sentinel は Sentinel 独自の言語を採用しており、 JSON や YAML のような Configuration 言語と、プログラマー/ノンプログラマーどちらにもフレンドリーなプログラミング言語の2つの要素を併せ持つような言語になっています。

developer.hashicorp.com

Sentinel ではライブラリを impot して外部データ等を扱うのですが、 HCP Terraform 用のライブラリとして以下が利用できます。(バージョンの古いものは割愛)

ライブラリ 概要
tfplan/v2 terraform plan の結果を扱えます。変更されるリソースのパラメータを評価したりするのに利用できます
tfconfig/v2 .tf ファイルに記述している内容を評価できます
tfstate/v2 tfstate に格納されているデータを評価できます
tfrun Terraform 実行に関わる情報( Organization/Workspace の情報、コスト予測など)を評価できます

developer.hashicorp.com

OPA

Open policy Agent (OPA) はオープンソースの Policy as Code のツールです。
Terrraform だけではなく、 Kubernetes のマニフェストなど様々な Infrastrucure as Code のツールに対応しているのが特徴です。

www.openpolicyagent.org

OPA は Rego という宣言的なクエリ言語で書かれており、命令型言語の同等のクエリよりもシンプルで簡潔に書けるのが特徴です。

OPA でも Terraform からのインプットとして以下の2つのデータを扱うことができます。

  • input.plan
  • input.run

developer.hashicorp.com

サポートされる機能

Policy や Policy Sets をコードで管理できる点など全体的な機能としては、 Sentinel と OPA は概ね共通しています。

  • Enforcement Level
  • Mock 生成機能

Enforcement Level

Sentinel や OPA ではポリシー毎にポリシーチェックに失敗した際の挙動を Enforcement Level (強制レベル)として設定することができます。
基本的な挙動としては Sentinel と OPA は共通なのですが、 OPA の mandatory は Sentinel の soft mandatory に相当し、 Sentinel の hard mandatory に相当するものは OPA では現在設定できません。

developer.hashicorp.com

Sentinel OPA 動作
hard mandatory mandatory(※1) ポリシーチェックに失敗した際に Plan が失敗となり、変更しないと Apply できない(例外なく禁止)
soft mandatory mandatory(※1) ポリシーチェックに失敗した際に hard 同様に Plan が失敗となるが、権限のあるユーザー(※2)であればオーバーライドして Apply することも可能(原則禁止だが、例外は作れる)
advisory advisory ポリシーチェックに失敗した際でも Plan は成功となり、 Apply も可能

※1
基本的な挙動としては Sentinel と OPA は共通なのですが、 OPA の mandatory では↓のようなポリシーセット毎の設定で、 Sentinel の soft mandatory のようにオーバーライドを可能にするか、 hard mandatory のようにオーバーライドを不可にするかを選択することになります。
ポリシー毎に soft/hard mandatory を設定できる Sentinel のほうが若干柔軟ではありますね。

※2
オーバーライドを可能にするにはユーザーやグループにManage policy overridesの権限を持たせる必要があります

Mock 生成機能

Mock は開発において単体テスト等で使われる事前定義済のオブジェクトのことを指しますが、 Sentinel でポリシーコードを開発する上でも Mock を使用することができます。

developer.hashicorp.com

そして、 HCP Terraform では Plan の実行後に以下のようなDownload Sentinel mocksというボタンがあり、そこから Mock 用のファイルをダウンロードすることができます。

こちらをダウンロードすると tar.gz ファイルが取得でき、それを展開すると以下のようなファイル群があります。
先ほど Sentinel で扱えるライブラリとして紹介した tfplan/v2などに対応するファイルであることが分かるかと思います。

例えば、 mock-tfplan-v2.sentinel であれば terraform plan を実行したときに出力される変更差分の Mock ファイルになっているので、
特定パラメータの変更を意図通りにチェックできるのか単体テストしながらポリシーを開発できます。
Terraform Plan の実行にはある程度の時間がかかってしまうので、チェックのたびに何度も Plan を繰り返す必要なく、一度の Plan の Mock でテストできるのがメリットですね。

OPA でも Mock を使った単体テストは可能ですが、 HCP Terraform が出力してくれるのは現状 Sentinel の Mock のみとなっております。

利用可能な公開情報

こちらも Sentinel のみになってしまいますが、 Terraform Registry に Policy Libraries として、各種クラウドリソース向けのポリシーなどが公開されています。
Terraform のパブリックモジュールのようにそのまま利用することもできますし、フォークしたり参考にして自分達用にカスタマイズすることもできます。

registry.terraform.io

Azure リソースに対する実装例

それでは Sentinel, OPA それぞれでポリシーを実装するとどんな感じになるか書いてみましょう。

シナリオ設定

Azure で App Service を作成する際、その App Service Plan の SKU を組織のポリシーに合ったものを使わせたい、というポリシーを作ろうと思います。
具体的には、プロダクション環境の場合は( env タグで判定)、 Premium プランの P0v3 ~ P3v3の4つの SKU のみ許可するようなポリシーを作ります。

サンプルコード

Terraform

まずは上記のルールに違反するような App Service Plan を作成します。
env タグに production とあるにもかかわらず、 B1の SKU を利用しようとしてみます。

resource "azurerm_service_plan" "production" {
  name                = "${var.project_name}-production"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  os_type             = "Linux"
  sku_name            = "B1"

  tags = {
    env = "production"
  }
}

次に↑のルール違反のリソースを検出するポリシーを作ります。

Sentinel

まずは Sentinel から。

Sentinel に必要なコードは、個別のポリシーを定義する sentinel ファイルとポリシーを割り当てる宣言をする sentinel.hcl の2種類のコードがいるようです。
sentinel.hcl のファイル名は固定となっています。 sentinel ファイルのパスやファイル名は比較的自由ですが、個人的にはディレクトリを分けたかったので policies/配下に置いています。

/
├── sentinel.hcl
└── policies/
                   └── appservice_plan_prod.sentinel

先に個別のポリシーを定義する sentinel ファイルから作ってみます。

# /policies/appservice_plan_prod.sentinel

# 先ほど出てきた tfplan/v2のライブラリをインポート
import "tfplan/v2" as tfplan

# 変数として allow_skus をリストとして作成。動的型付けなので特に型を指定する必要なし
allow_skus = [
    "P0v3",
    "P1v3",
    "P2v3",
    "P3v3",
]

# ポリシーに違反するリソースをフィルタリングする文。詳細は後述
notAllowdAppServicePlans = filter tfplan.resource_changes as _, resource_changes {
    resource_changes.type is "azurerm_service_plan" and
        resource_changes.mode is "managed" and
        (resource_changes.change.actions contains "create" or
            resource_changes.change.actions  contains "update") and
        resource_changes.change.after.tags.env is "production" and
        resource_changes.change.after.sku_name not in allow_skus
}

# ポリシー違反したリソースに関する情報を出力
for notAllowdAppServicePlans as address, v {
    print("App Service Plan is allowed PremiumV3 SKU in Production.")
    print("Adress:", v.address, "Env:", v.change.after.tags.env, "SKU:", v.change.after.sku_name)
}

# Sentinel に必須なメインルール。ブール値を評価して False だった場合はポリシー違反として検知する
main = rule {
    length(notAllowdAppServicePlans) is 0
}

notAllowdAppServicePlans の部分では、
tfplan.resource_changesに terraform plan で出力されるリソース一覧、つまり azurerm_service_plan.production を含むオブジェクトがあるので、
それを filter 文でループを回してリソースの情報を評価・抽出しています。

resource_changes.typeazurerm_ のような Terraform 上のリソースタイプなので azurerm_service_plan を抽出します。

resource_changes.change.actions はリソースに対してどんなアクションが取られるかがリスト形式で入っています。
変更がないリソースもno-opというアクションで入っているので、そういったリソースを除外して、
create 新規作成か update 変更のリソースを抽出します。 developer.hashicorp.com

ここからはリソースの持つパラメータです。
resource_changes.change.after.tags.env にはタグの env の値を参照しています。

resource_changes.change.after.sku_name には SKU 名が入っているので、これが allow_skus に含まれている SKU でなければポリシー違反となります。

# /sentinel.hcl

policy "appservice_plan_prod" {
  source            = "./policies/appservice_plan_prod.sentinel"
  enforcement_level = "advisory"
}

最後に sentinel.hcl でポリシーを割り当てる宣言をします。
enforcement_level もここで指定します。

こちらのポリシーを Policy Sets として登録して Terraform Plan を実行すると、 このように、 advisory なので Passed として Sentinel のチェック結果が表示されます。
Description として表示されるのは appservice_plan_prod.sentinel の main ルールのすぐ上に書いたコメントがそのまま記載されるようです。
他にも print 文で書かれたログにもリソースの情報がパースされて表示されているので、どのリソースでポリシー違反があったか特定もしやすいかと思います。

OPA

次に OPA のほうもやっていきます。

まずはファイル構成から。
大枠は Sentinel と似てますが、トップレベルに置く HCL ファイル名は sentinel.hcl ではなく policies.hcl になります。
また、個別のポリシーを定義するファイルは.rego というファイル形式になります。

/
├── policies.hcl
└── policies/
                   └── appservice_plan_prod.rego
# /policies/appservice_plan_prod.rego

# パッケージとして宣言
package terraform.policies.appservice_plan_prod

# ライブラリをインポート
import input.plan as plan       # Sentinel の tfplan/v2のように、 OPA では input.plan をインポート
import future.keywords.in      # in の演算子をインポート

# 変数として allow_skus をリストとして作成。=ではなく:=なので注意( Go 言語らしい)
allow_skus := [
  "P0v3",
  "P1v3",
  "P2v3",
  "P3v3",
]

# Sentinel の Main ルールに当たるもの
deny[msg] {
  r := plan.resource_changes[_]
  r.type == "azurerm_service_plan"
  r.change.after.tags.env == "production"
  not r.change.after.sku_name in allow_skus
  msg := sprintf("App Service Plan is allowed PremiumV3 SKU in Production : %v, evn_tags: %v, sku: %v, all: %v.", [r.address, r.change.after.tags.env,  r.change.after.sku_name, r])
}

ざっくりと見た感じでは OPA と似た感じになっていますね。
個人的には deny[msg]の中でループを回している感じが直感的には掴みづらかったのですが、アンダースコア_がイテレーターとして機能しているようです。
その下3つの条件文が and 条件となっていて、最終的には msg に文字列を代入して返す、というのが OPA の書き方のようです。

# /policies.hcl

policy "appservice_plan_prod" {
  query = "data.terraform.policies.appservice_plan_prod.deny"
  description = "Check appservice_plan SKU in Production"
  enforcement_level = "advisory"
}

最後に policies.hcl でポリシーを割り当てる宣言をします。
Sentinel ではポリシーファイルの場所をファイルパスで渡していましたが、 OPA では query としてパッケージ名を指定します。
パッケージ名としては、 Rego ファイルで宣言したパッケージ名 terraform.policies.appservice_plan_prod の先頭にdata. , 最後に .deny (ルール名)を付けた、
data.terraform.policies.appservice_plan_prod.deny が query に渡すパッケージ名になります。(ここがなかなか理解しづらかった。。)
また OPA のほうだけ policy ブロック内で description を指定できます。

ポリシーチェックの画面はこちらです。

リストが入れ子になったような出力でメッセージが表示されています。
description は policies.hcl の policy ブロックに記載されたものが表示されています。

所感

このように、 Sentinel と OPA で大枠では似たような機能を持っていますが、 HCP Terraform での対応状況など細部で違いがありました。

どのような状況でどちらのツールが適しているか

コード的にできることは Sentinel と OPA では大きな差はなさそうですが、
Terraform Regisry のポリシーライブラリに実際使用したり参考にできるポリシーがあることや HCP Terraform の Mock 機能があるなど、
利用しやすい環境が整っているのは Sentinel だと感じられました。

また、どちらのツールを採用するかをそれぞれの対応しているツールの範囲を考慮して選択してもらうのも良いかと思います。

ツール 対応範囲 適している環境
Sentinel Vault, Consul, Nomad などの Hashicorp スイーツでサポート Hashicorp スイーツを複数導入している環境
OPA Kubernetes のマニフェストなど OSS を中心とした IaC をサポート Kubernetes など複数の IaC を導入している環境

Hashicorp スイーツも Kubernetes 等の IaC もどちらもある、という場合は、重点的にポリシーを整備したいほうに寄せるのが良いかと思います。

最後に

今回は Sentinel と OPA という2つのツールの比較という観点で記事を書かせていただきましたが、 Terraform が良くも悪くもコスト面やセキュリティ面でインパクトのある変更を加えられてしまうツールなので、
どちらのツール(あるいはそれ以外)でも良いのである程度ガバナンスを利かせることで、インシデントを防げる状態に持っていく必要があると感じています。

今後も Sentinel/OPA の使っていくことになるので、また新しいトピックや面白い活用方法などあればご紹介していきたいと思います。

ACS事業部のご紹介

最後にご紹介です。
私の所属するACS事業部では、開発者ポータルBackstage、Azure AI Serviceなどを活用し、Platform Engineering+AIの推進・内製化のご支援をしております。

www.ap-com.co.jp www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です!
我々の事業部のCultureDeckはこちらです。

www.ap-com.co.jp

今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。 www.ap-com.co.jp

本記事の投稿者
安藤知樹:書きっぷりはSentinelのほうが好みかもしれない…