🏛️

Sentinel Policy CLI入門

2024/10/12に公開

想定読者

  • "Sentinel"と聞いて、「HashiCorpのPolicy as Codeのことか!」とは ならない 方。
  • ちょっとsentinel触ってみたい方。
  • HCP Terraformをご利用中、またはご利用予定の方。

はじめに

みなさん、"sentinel"ってご存知でしょうか?
そもそも知らないか、「あーM社のセキュリティのあれね」となる方がほとんどだと思います(所感)。

ここでは、TerraformなどのHashiCorp製品でPolicy as Codeを実現するためのSentinelというフレームワークをご紹介します。

Sentinelとは?

すでに若干ネタバレしてしまっていますが、Sentinelとは、主にTerraform含むHashiCorpのプロダクトに対してPolicy as Codeを実現するためのフレームワークになります。

PolicyをCodeで管理することには、以下のようなメリットがあります。

  • ポリシーの再利用性
    • ポリシーをコードでUIや手動で制御するのではなく、コードによって実現することで、モジュール等の作成が可能になり、再利用性が高まります。
  • ポリシーのバージョン管理
    • IaC (Infrastructure as Code)と同様に、ポリシーもコードとして管理することで、バージョン管理が可能になります。誰が、いつ、なぜ、そのポリシーを設定したのか、変更したのか、などの情報も含めた管理ができるようになります。
  • ポリシーのテスト
    • マニュアルの制御からコードによる制御になると、新しいポリシーがうまく機能しているかどうかなどのテストも容易になります。
  • ポリシーの自動化
    • ポリシーをCI/CDパイプラインに組み込み、自動的にポリシーをチェックするような運用も可能になります。
  • などなど

TerraformのみならずVaultやNomadなど、HashiCorpのその他プロダクトとも組み合わせて使っていただけるもので、CLIも提供されています。

また、HCP TerraformやTerraform Enterpriseなどをお使いの場合には、Terraformが実行(plan/apply)されるタイミングで自動的にPolicyチェックを行い、その結果によってはapplyの手前で実行を止めるようなことも可能です。

PRが上がるたびに目grepするのはやめて、自動化できるところは自動化していきましょう!

とりあえず動かしてみる

何はともあれ動かしてみます。

ツール準備

  • sentinelコマンド。きっとあなたのPCに入っていることはないと思うので、ダウンロードしてください。
    • macOSやLinuxでHomebrewを導入している場合、brew tap hashicorp/tapからのbrew install sentinelで多分インストールできるはずです。
    • できなかった、もしくはHomebrew入れてない、という場合は こちらからダウンロードしてください。PATH通しもお忘れなく。

一応バージョンを確認しておきます。

➜ sentinel -version
Sentinel v0.28.0

これでツールの準備は完了です。

コードの準備

次に、お試し用のSentinelポリシーと、それに対するテストコードを用意します。
それぞれ、以下のように配置してください。

(作業ディレクトリ)
  ├ 📄 test_sentinel.sentinel
  └ 📁 test/
     └  📁 test_sentinel/
         ├ 📄 pass.hcl
         └ 📄 fail.hcl
test_sentinel.sentinel
param weather
param day

is_sunny = rule {
  weather == "sunny"
}

is_wednesday = rule {
  day == "wednesday"
}

main = rule {
  is_sunny and is_wednesday
}
pass.hcl
param "weather" {
  value = "sunny"
}

param "day" {
  value = "wednesday"
}
fail.hcl
param "weather" {
  value = "sunny"
}

param "day" {
  value = "tuesday"
}

動かしてみる

準備が整ったので、sentinelを動かしてみます。

# 作業ディレクトリにいない場合は作業ディレクトリに移動してからコマンドを実行してください。
➜ sentinel test
FAIL - test_sentinel.sentinel
  FAIL - test/test_sentinel/fail.hcl

    trace:
      test_sentinel.sentinel:12:1 - Rule "main"
        Value:
          false

      test_sentinel.sentinel:4:1 - Rule "is_sunny"
        Value:
          true

      test_sentinel.sentinel:8:1 - Rule "is_wednesday"
        Value:
          false
  PASS - test/test_sentinel/pass.hcl
1 tests completed in 6.946375ms

同じような結果が出れば成功です!

簡単な解説

概ね見て想像できる結果かなと思いますが、簡単にポイントを説明します。

  • Sentinelのポリシーコードは、.sentinel拡張子で記述します。
    • 今回の例では、test_sentinel.sentinelが本体ファイルです。
  • ポリシーをテストするためのコードは、.hcl拡張子で記述し、(作業ディレクトリ)/test/(ポリシー名)に配置します。
    • 今回の例では、pass.hclfail.hclがテストコードです。
    • これらの中に、ポリシーそのものをテストするためのもろもろを記述します。
  • sentinelポリシーの中には一つのmainが存在し、これがtrueになると成功となります。
  • 可読性向上のために、ruleを使ってロジックを分割することができます。
  • sentinelのtestなどのコマンドは、terraformと同様に、トップディレクトリで実行します。

基本的な文法

個人の主観に基づき一部の文法を抜粋して紹介します。
網羅的に見たい方は公式ドキュメントもご参照ください。

変数

Sentinelでは変数を扱うことができます。mainも変数の一つです。

a = 1      // a == 1
b = 2      // b == 2
c = a + b  // c == 3

型変換もできます。

d = 1.1
a = int(d)     // a == 1
e = string(a)  // e == "1"

その他いくつか種類がありますが、例えばparamを使うことで、外部からの入力を受け取ることができます。

param weather
param day

param宣言した上で、ポリシー設定時に値を渡すことで、ポリシーの再利用が可能です。
(この辺りはHCP Terraformとの組み合わせパートで多分触れます。)

policy "test" {
  source = "foo.sentinel"
  enforcement_level = "hard-mandatory"
  params = {
    "weather" = "sunny"
  }
}

このほか、ListMapなども扱うことが可能です。

Rule

sentinelでは、ruleを使ってポリシー判定を行います。

is_sunny = rule {
  weather == "sunny"
}

is_wednesday = rule {
  day == "wednesday"
}

main = rule {
  is_sunny and is_wednesday
}

こちらの例のように、複数のruleを組み合わせてmainで判定することもできるので、見やすいようにある程度分割しておくと良いでしょう。

条件分岐/ループ

if/elseやforなどの制御構文を扱うこともできます。

if
if condition {
    // ...
} else {
    // ...
}

if condition {
    // ...
} else if other_condition {
    // ...
} else {
    // ...
}

for
list = []
for { "a": 1, "b": 2 } as name {
    append(list, name)
}

print(list) // ["a" "b"]

Collection Operations

filterやmapを使うこともできます。resource全体のうち、特定の条件に合致するものがないかを探す時など、filterはよく使う気がします。

list  = [1, 1, 2, 3, 5, 8]
evens = filter l as v { v % 2 is 0 } // [2, 8]
list = [1, 2, 3]
res  = map list as l { l * 2 }  // [2, 4, 6]

関数

関数を定義することも可能です。

func add_one(x) {
    return x + 1
}
result = add_one(1)   // 2

匿名関数も作れます。

Imports

Sentinelには標準ライブラリ(Standard Imports)が用意されており、importして使うことができます。

import "time"

date_now = time.now() // {"day": 11, "hour": 14, "minute": 59, "month": 10, "month_name": "October", ...

HCP Terraformで使う場合には、このimportを使って、stateやplanの結果などの情報を取得し、ポリシー判定に使うことができます。

さいごに

いかがでしたでしょうか?
少しでもSentinelについての興味を持っていただけたら幸いです。

ここでは触れませんでしたが、SentinelにはSentilel Playgroundというものがあり、ブラウザ上でSentinelのコードを書いて実行することができます。ぜひ、そちらもお試しください!

参考

Discussion