Using Terraform & Sentinel for Infrastructure Governance

Hashicorp Terraform is a tool for provisioning infrastructure efficiently. With Terraform, you define how your infrastructure should look, and run Terraform to bring that infrastructure to life. Terraform can interact with virtually any cloud provider (AWS, Azure, GCP, etc.).

For example, this piece of Terraform code will create an AWS EC2 instance based on the RHEL 7.5 Base Image AMI (ami-6871a115):

provider "aws" {
region = "us-west-1”
}

resource "aws_instance" "web" {
ami = "ami-6871a115"
instance_type = "t2.micro"
tags {
Name = "My AWS Instance!"
}
}

As infrastructure engineers begin to write more Terraform code, it’s important to think about the limitations that we should place on provisioning resources. For example, do we want to allow anyone to create extra-large instances on AWS? This could potentially cost the business a lot of money. What about an engineer creating an AWS instance with an insecure security group? This creates a potential security vulnerability.

While Terraform is great solution for creating resources, we need a way to monitor these resources and ensure that they are created according to the standards of our organization. This is where policy enforcement with Sentinel comes into play.

Sentinel

Sentinel is an embedded policy-as-code framework integrated with the HashiCorp Enterprise products. It enables fine-grained, logic-based policy decisions, and can be extended to use information from external sources. We can use Sentinel with Terraform Enterprise to provide some governance over our infrastructure.

Note: Sentinel policy enforcement is only available in the Premium version of Terraform Enterprise.

Example: Use Sentinel to limit AWS resource size
We want to limit the size of AWS resources. In this example, we will allow the following resource sizes: ‘t2.micro’, ‘t2.small’, ‘t2.medium’.

Sentinel will prevent our engineers from creating any instances outside of the above list.

import "tfplan"

# Get all AWS instances from all modules
get_aws_instances = func() {
instances = []
for tfplan.module_paths as path {
instances += values(tfplan.module(path).resources.aws_instance) else []
}
return instances
}

# Allowed Types
allowed_types = [
"t2.micro",
“t2.small”,
“t2.medium”
]

aws_instances = get_aws_instances()

# Rule to restrict instance types
instance_type_allowed = rule {
all aws_instances as _, instances {
all instances as index, r {
r.applied.instance_type in allowed_types
}
}
}

# Main rule that requires other rules to be true
main = rule {
(instance_type_allowed) else true
}

Above is our Sentinel code that enforces the size on AWS Instances. We first define a function ‘get_aws_instances’ that will return a list of AWS instances scheduled for creation. Next we define a rule that loops over each instance and verifies that the instance_type is contained in the ‘allowed_types’ list we have defined.

We can apply this policy in the Terraform Enterprise GUI under ‘Settings’ -> ‘Policies’ -> ‘Create a new policy’.

When creating a policy, we need to specify a name, a description, and an enforcement mode.

There are three enforcement modes with Sentinel:

  • Hard mandatory: cannot override the policy. Engineers will need to change the Terraform code to get around this policy.
  • Soft mandatory: Sentinel policy will initially stop Terraform from running, however, a manual override is possible.
  • Advisory: Logging only. Terraform will allow a breach of policy.

After writing the policy, we have to add it to a ‘Policy set’. This is a group of policies that will get enforced for your current workspace (or all workspaces). In my example, I applied my policy to the ‘global’ policy set. This means that all policies within ‘global’ will get applied to any workspace in my organization.

Example: Use Sentinel to check security groups on AWS instances

We can also use Sentinel policies to verify the security groups for our AWS instances. In this example, we will allow only the “sg-ab23fds4″ security group.

import "tfplan"

# Get all AWS instances from all modules
get_aws_instances = func() {
instances = []
for tfplan.module_paths as path {
instances += values(tfplan.module(path).resources.aws_instance) else []
}
return instances
}

allowed_sgs = [
"sg-ab23fds4",
]

aws_instances = get_aws_instances()

# Rule to restrict instance types
sg_allowed = rule {
all aws_instances as _, instances {
all instances as index, r {
all r.applied.vpc_security_group_ids as sg {
sg in allowed_sgs
}
}
}
}

# Main rule that requires other rules to be true
main = rule {
(sg_allowed) else true
}

The code above is similar to the first example, except in this case we are checking the security group ID’s for our AWS instances. We are looping over each of the security groups and verifying that they are contained in the ‘allowed_sgs’ list.


Interested in Terraform? Learn how Shadow-Soft can help you evaluate, adopt and integrate Terraform


Final thoughts

With Sentinel, we can enforce policy across all infrastructure that is managed by Terraform. This can help keep your business secure and efficient. As we have seen in the examples above, we can use Sentinel to enforce instance sizes or security groups. We have used AWS in this example, but remember that Terraform supports multiple cloud providers. We could easily modify our Sentinel policies to check instances sizes for Azure or Google Cloud Provider instances.

Related Posts