In the Advisor, “Policy as Code,” we explored the concept of compliance as code and gave a real-world example for why this is useful. In this article, we move beyond the conceptual “what if” scenario, and delve into the real-world, “Where do I go from here?”
As you are well aware, controls are only as good as their ability to be enforced. If you can’t be certain that a control will apply in every scenario against all of your use cases, then it becomes very difficult to create a well-functioning, defense-in-depth strategy. Below, we review the architecture and enforcement mechanisms used to steer developers, DevOps, and infrastructure engineering staff through the use of the compliance-as-code controls.
An integral part of using Terraform in an enterprise environment is to switch from using the open-source version to using the enterprise version. This brings a lot of additional security and automation benefits, such as role-based access control, CI/CD pipeline integration, and, most importantly, enforcement of Sentinel policy sets.
Sentinel policy sets are collections of various Sentinel rules you can use to enforce consistent checks across some or all of your workspaces in Terraform Enterprise. In our environment, the context of Terraform Enterprise workspaces are equivalent to an individual Amazon Web Services (AWS) account. When you enable a policy set to run across all of your workspaces, it will run your policy-as-code checks every time a workspace executes. Figure 1 shares a sample screenshot of what it looks like to manage and apply policy sets to workspaces in Terraform Enterprise.
To set this up logically, first you have a code repository (GitLab, in this case) that holds all of your Sentinel policy-as-code files and an additional file called sentinel.hcl. You then use Terraform Enterprise to set up a VCS Repo or Branch connection to that code repository, which allows HashiCorp to read the policy files as well as the instructions in the sentinel.hcl file on how it should be applying your rules. The sentinel.hcl file contains specific instructions that indicate:
Each policy that should be checked in the set
The enforcement level of each policy in the set
Any modules the policies are using
The code block in Figure 2 shows you how to specify the source of your Sentinel policy file as well as the enforcement level it will need to run on the system.
Currently, there are three levels of enforcement:
Hard mandatory – Hard enforcement, not even administrators can override these rules
Soft mandatory – Soft enforcement, an administrator can provide an override
Advisory – Warning message (think, “Are you sure you want to do this?”)
In our environment, we use hard rules that generically apply everywhere, such as: “All s3 buckets must be encrypted with a KMS key.” Or “All security groups must not contain port 22 open to 0.0.0.0.” The use case for soft rules is about restricting access to certain resources in shared workspaces. We use soft rules to protect core network route table changes in our shared services workspace. Sometimes, it’s appropriate for users to make changes to networking resources, but only if they are network administrators. Advisory policies are good for reminding users about certain best practices, but they aren’t enforceable controls. We use advisory rules for things like, “Are you sure you need this R5.24Xlarge? You could try starting with a R5.2xlarge first.”
Lastly, the structure of your Sentinel code repository should look something like this (see Figure 3):
So far, we have discussed one of the core enforcement mechanisms, Terraform Enterprise, and how to install policy set enforcement on your workspaces. Next, we discuss the required controls to enforce changes directly into your AWS environments so that you can ensure all changes go through your Terraform Enterprise System.
AWS Control Enforcement
Understanding the authentication and deployment mechanisms for your AWS environment is important for this last step. The idea around this concept is to steer users to embrace the approved mechanisms to deploy (Terraform Enterprise), and not to make changes via the user-based API/AWS console.
The specific mechanisms used to enforce things across all of your AWS accounts are AWS Organizations service control policies. These allow you to place a policy file in the root organizational unit and then join all of your AWS accounts to that organization, applying the policy file universally across all of your accounts. The code block shown in Figure 4 shows what a service control policy looks like.
Service control policy files look a lot like AWS Identity and Access Management policy files because they are very similar in JSON structure. In the example above, the effect will always be to deny access to services you are using in your accounts, unless coming from the Terraform service account role. The policy starts out with an “Effect: Deny” statement, which tells AWS that anything that follows must be denied by default. The Action statement is followed by the list-specific AWS service actions you do not want to allow. The resource statement specifies which resources you want this policy to apply to (in our case, * for all resources). Finally, the condition statement allows your Terraform service role to bypass this deny access policy.
This statement, when applied to your organization, will prevent anyone from making modifications to any of those services unless the request comes from your Terraform service account. In keeping with the previous section on using Sentinel policy sets, this forces all modifications through your policy sets to apply your compliance as code to every change performed in your environment.