Policy Validation — Preventive Control with Terraform Vet
Co-author Ethan Han.
gcloud beta terraform vet is a command-line tool for ensuring that a terraform plan will conform with a set of arbitrary policies (defined using Constraint Framework). gcloud beta terraform vet focus on GCP customers using Terraform to provision Infrastructure. gcloud beta terraform vet could help GCP customers to improve security & governance through Policy as Code. For Kubernetes resources provisioned in GKE or other resources provisioned with Config Connector, the Policy Controller solution can be another alternative to implement policy controls.
Infrastructure Policy Controls
Preventive controls are realized through policy and architecture. Policy is defined as a series of programmatic constraints that protect the organization from threats. Architecture is defined as how infrastructure constructs can be used to protect the organization.
Preventive control uses gcloud beta terraform vet to scan a terraform plan and it is primarily focused on detecting policy violation during pre deployment stage and block non-compliant infrastructure before it is deployed.
The preventive controls process is illustrated in the following figure and focuses on the new resources created via CICD pipeline:
- In step 1, configure terraform scripts in a source code repository
- In step 2, apply terraform plan and generate terraform plan file in json format. The Cloud Build can be used for the CI/CD pipeline process.
- In step 3, fetch Policy Library and execute gcloud beta terraform vet to check policy violations on the terraform plan output file.
- In step 4, based on the policy validation result, there will be two different scenarios:
- If the scripts passed policy validation, the pipeline will proceed with other steps
- Alternatively, the pipeline will failed to proceed.
Step by Step Guide for Preventive Control with Terraform Vet
All commands are executed in Cloud Shell.
Prerequisites
- A Google Cloud project is ready
- Your account should have the following Identity and Access Management (IAM) permissions
- resourcemanager.projects.getIamPolicy
- resourcemanager.projects.get
Create a policy library
- Configure the following environment variable, update the values accordingly.
export POLICY_LIBRARY_REPO="Your Repository"
2. Go to the Cloud Shell and clone the policy library.
git clone https://github.com/GoogleCloudPlatform/policy-library.git
3. Add the library your existing repository to your POLICY_LIBRARY_REPO.
cd policy-library
git remote set-url origin SOURCE_CODE_REPOSITORY
git add --all .
git commit -m "Initial commit of policy library constraints"
git push -u origin main
4. A policy library repository contains the following directories:
- policies/ — This directory contains two subdirectories:
- constraints/ — This directory is initially empty. Place your constraint files here.
- templates/ — This directory contains pre-defined constraint templates. - samples/ — This directory contains sample constraints.
- validator/ — This directory contains the .rego files and their associated unit tests. You don’t need to touch this directory unless you intend to modify existing constraint templates or create new ones. Running “make build” inlines the Rego content in the corresponding constraint template files.
Create terraform script
- Create the following Terraform main.tf file in the current directory (policy-library).
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 3.84"
}
}
}
resource "google_project_iam_binding" "sample_iam_binding" {
project = "PROJECT_ID"
role = "roles/viewer"
members = [
"user:EMAIL_ADDRESS"
]
}
Replace the following:
- PROJECT_ID: your project ID.
- EMAIL_ADDRESS: a sample email address. This can be any valid email address. For example, user@example.com.
2. Initialize Terraform and generate a Terraform plan using the following:
terraform init
3. Export the Terraform plan, if asked, click Authorize when prompted:
terraform plan -out=test.tfplan
4. Convert the Terraform plan to JSON:
terraform show -json ./test.tfplan > ./tfplan.json
5. Copy the sample IAM domain restriction constraint into the policies/constraints directory.
cp samples/iam_service_accounts_only.yaml policies/constraints
6. Enter the following command to verify the policy constraints and validate that your Terraform plan complies with your policies. Use the tfplan.json created in the previous step as the input.
gcloud beta terraform vet tfplan.json --policy-library=. --format=json
Since the provided email address in the IAM policy binding does not belong to a service account, the plan violates the set up constraint. You will find the following error message.
[
{
"constraint": "GCPIAMAllowedPolicyMemberDomainsConstraintV2.service_accounts_only",
....
},
"message": "IAM policy for //cloudresourcemanager.googleapis.com/projects/PROJECT_ID contains member from unexpected domain: user:user@example.com",
"metadata": {
"ancestry_path": "organizations/ORG_ID/projects/PROJECT_ID",
"constraint": {
"annotations": {
"description": "Checks that members that have been granted IAM roles belong to allowlisted domains.",
"validation.gcp.forsetisecurity.org/originalName": "service_accounts_only",
"validation.gcp.forsetisecurity.org/yamlpath": "policies/constraints/iam_service_accounts_only.yaml"
},
...
}