始める前に
制約フレームワーク
gcloud beta terraform vet
は制約フレームワーク ポリシーを使用します。これは、制約と制約テンプレートで構成されます。この 2 つの違いは次のとおりです。
- 制約テンプレートは関数宣言に似ています。Rego でルールを定義し、必要に応じて入力パラメータを受け取ります。
- 制約は、制約テンプレートを参照し、それに渡す入力パラメータとポリシーの対象となるリソースを定義するファイルです。
これにより、繰り返しを回避できます。一般的なポリシーを使用して制約テンプレートを作成した後、任意の数の制約を作成し、さまざまな入力パラメータやリソース マッチング ルールを指定できます。
制約テンプレートを作成する
制約テンプレートを作成する手順は次のとおりです。
- サンプルデータを収集します。
- Rego を作成します。
- Rego をテストします。
- 制約テンプレートのスケルトンを設定します。
- Rego をインライン化します。
- 制約を設定します。
サンプルデータを収集する
制約テンプレートを作成するには、サンプルデータが必要です。Terraform ベースの制約は、Terraform プラン JSON の resource_changes
キーから取得したリソース変更データを処理します。
たとえば、JSON は次のようになります。
// tfplan.json
{
"format_version": "0.2",
"terraform_version": "1.0.10",
"resource_changes": [
{
"address": "google_compute_address.internal_with_subnet_and_address",
"mode": "managed",
"type": "google_compute_address",
"name": "internal_with_subnet_and_address",
"provider_name": "registry.terraform.io/hashicorp/google",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"address": "10.0.42.42",
"address_type": "INTERNAL",
"description": null,
"name": "my-internal-address",
"network": null,
"prefix_length": null,
"region": "us-central1",
"timeouts": null
},
"after_unknown": {
"creation_timestamp": true,
"id": true,
"network_tier": true,
"project": true,
"purpose": true,
"self_link": true,
"subnetwork": true,
"users": true
},
"before_sensitive": false,
"after_sensitive": {
"users": []
}
}
}
],
// other data
}
Rego を作成する
サンプルデータを取得したら、制約テンプレートのロジックを Rego で作成できます。Rego には violations
ルールが必要です。レビュー中のリソース変更は、input.review
として利用できます。制約パラメータは input.parameters
として使用できます。たとえば、google_compute_address
リソースに address_type
が許可されるようにするには、次のように記述します。
# validator/tf_compute_address_address_type_allowlist_constraint_v1.rego
package templates.gcp.TFComputeAddressAddressTypeAllowlistConstraintV1
violation[{
"msg": message,
"details": metadata,
}] {
resource := input.review
resource.type == "google_compute_address"
allowed_address_types := input.parameters.allowed_address_types
count({resource.after.address_type} & allowed_address_types) >= 1
message := sprintf(
"Compute address %s has a disallowed address_type: %s",
[resource.address, resource.after.address_type]
)
metadata := {"resource": resource.name}
}
制約テンプレートの名前を指定する
上記の例では、TFComputeAddressAddressTypeAllowlistConstraintV1
という名前を使用しています。これは、各制約テンプレートの固有識別子です。命名のガイドラインに従うことをおすすめします。
- 一般的な形式:
TF{resource}{feature}Constraint{version}
キャメルケースを使用してください(つまり、各単語の先頭を大文字にします)。 - 単一リソース制約の場合は、Terraform プロバイダの命名規則に従ってください。たとえば、
google_tags_tag
の場合、API 名がresourcemanager
であっても、プロダクト名はtags
です。 - テンプレートが複数のタイプのリソースに適用される場合は、リソースの部分を省略し、機能のみを含めます(例: 「TFAddressTypeAllowlistConstraintV1」)。
- バージョン番号が semver 形式に準拠していません。これは 1 個の数字です。これにより、テンプレートのすべてのバージョンを効率的に一意のテンプレートにすることができます。
制約テンプレート名と一致する Rego ファイルの名前を使用することをおすすめしますが、スネークケースを使用します。つまり、名前を _
を使って小文字の個別の単語に変換します。上記の例では、推奨されるファイル名は tf_compute_address_address_type_allowlist_constraint_v1.rego
です。
Rego をテストする
Rego を手動でテストするには、Rego Playground を使用します。機密でないデータを使用してください。
自動テストを作成することをおすすめします。収集したサンプルデータを validator/test/fixtures/<constraint
filename>/resource_changes/data.json
に配置し、次のようにテストファイルで参照します。
# validator/tf_compute_address_address_type_allowlist_constraint_v1_test.rego
package templates.gcp.TFComputeAddressAddressTypeAllowlistConstraintV1
import data.test.fixtures.tf_compute_address_address_type_allowlist_constraint_v1_test.resource_changes as resource_changes
test_violation_with_disallowed_address_type {
parameters := {
"allowed_address_types": "EXTERNAL"
}
violations := violation with input.review as resource_changes[_]
with input.parameters as parameters
count(violations) == 1
}
Rego とテストをポリシー ライブラリの validator
フォルダに配置します。
制約テンプレートのスケルトンを設定する
作業中の Rego ルールのテストが完了したら、これを制約テンプレートとしてパッケージ化する必要があります。制約フレームワークでは、ポリシー Rego のコンテナとして Kubernetes カスタム リソース定義を使用します。
制約テンプレートは、OpenAPI V3 スキーマを使用して、制約からの入力として許可するパラメータも定義します。
Rego で使用したものと同じ名前をスケルトンに使用してください。具体的には次のとおりです。
- Rego と同じファイル名を使用します。例:
tf_compute_address_address_type_allowlist_constraint_v1.yaml
spec.crd.spec.names.kind
はテンプレート名です。metadata.name
にはテンプレート名を含めます。ただし、小文字で指定してください。
制約テンプレートのスケルトンを policies/templates
に配置します。
上記の例では、次のようになります。
# policies/templates/tf_compute_address_address_type_allowlist_constraint_v1.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: tfcomputeaddressaddresstypeallowlistconstraintv1
spec:
crd:
spec:
names:
kind: TFComputeAddressAddressTypeAllowlistConstraintV1
validation:
openAPIV3Schema:
properties:
allowed_address_types:
description: "A list of address_types allowed, for example: ['INTERNAL']"
type: array
items:
type: string
targets:
- target: validation.resourcechange.terraform.cloud.google.com
rego: |
#INLINE("validator/tf_compute_address_address_type_allowlist_constraint_v1.rego")
#ENDINLINE
Rego をインライン化する
この時点で、前の例のディレクトリ レイアウトは次のようになります。
| policy-library/
|- validator/
||- tf_compute_address_address_type_allowlist_constraint_v1.rego
||- tf_compute_address_address_type_allowlist_constraint_v1_test.rego
|- policies
||- templates
|||- tf_compute_address_address_type_allowlist_constraint_v1.yaml
Google 提供のポリシー ライブラリ リポジトリのクローンを作成した場合は、make build
を実行して、validator
で定義された Rego を使用して、policies/templates
の制約テンプレートを自動的に更新します。
制約を設定する
制約には、gcloud beta terraform vet
が適切に適用され、違反を報告するために必要な 3 つの情報が含まれています。
severity
:low
、medium
、またはhigh
match
: 制約が特定のリソースに適用されるかどうかを判断するためのパラメータ。次の一致パラメータがサポートされています。addresses
: glob スタイルの一致を使用して含めるリソース アドレスのリストexcludedAddresses
: (省略可)glob スタイル マッチングを使用して除外するリソース アドレスのリスト。
parameters
: 制約テンプレートの入力パラメータの値。
kind
に制約テンプレート名が含まれていることを確認します。metadata.name
には、説明的なスラッグを設定することをおすすめします。
たとえば、前述の制約テンプレートのサンプルを使用して INTERNAL
アドレスタイプのみを許可するには、次のように入力します。
# policies/constraints/tf_compute_address_internal_only.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: TFComputeAddressAddressTypeAllowlistConstraintV1
metadata:
name: tf_compute_address_internal_only
spec:
severity: high
match:
addresses:
- "**"
parameters:
allowed_address_types:
- "INTERNAL"
マッチングの例:
アドレス マッチャー | 説明 |
---|---|
`module.**` | 任意のモジュール内のすべてのリソース |
`module.my_module.**` | モジュール「my_module」内のすべて |
`**.google_compute_global_forwarding_rule.*` | 任意のモジュール内のすべての google_compute_global_forwarding_rule リソース |
`module.my_module.google_compute_global_forwarding_rule.*` | my_module 内のすべての google_compute_global_forwarding_rule リソース |
リソース アドレスが addresses
と excludedAddresses
の値と一致する場合、リソース アドレスは除外されます。
制限事項
Terraform プランデータは、適用後の実際の状態を可能な限り最も適切に表したものです。ただし、多くの場合、適用後の状態はサーバー側で計算されるため、不明になる場合があります。
CAI の祖先パスの作成は、ポリシーを検証するプロセスの一部です。提供されているデフォルト プロジェクトを使用して、不明なプロジェクト ID の使用を防ぎます。デフォルト プロジェクトが指定されていない場合、祖先パスのデフォルトは organizations/unknown
になります。
不明な祖先を禁止するには、次の制約を追加します。
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: GCPAlwaysViolatesConstraintV1
metadata:
name: disallow_unknown_ancestry
annotations:
description: |
Unknown ancestry is not allowed; use --project=<project> to set a
default ancestry
spec:
severity: high
match:
ancestries:
- "organizations/unknown"
parameters: {}
サポートされているリソース
Terraform プロバイダからの Terraform リソースにリソース変更の制約を作成できます。