Apply security guardrails and best practices on the organizational level using Service Control Policies and HashiCorp Terraform.
Recently, AWS released an update to their AWS Organizations service allowing the use of Conditions, Resources, and NotAction inside Service Control Policies (SCPs). This post explores the new possibilities in relation to security best practices and how to deploy using HashiCorp Terraform.
What is AWS Organizations?
From the official AWS documentation:
“AWS Organizations helps you centrally govern your environment as you grow and scale your workloads on AWS. Whether you are a growing startup or a large enterprise, Organizations helps you to centrally manage billing; control access, compliance, and security; and share resources across your AWS accounts.”
The focus of this post are the “compliance and security” features of AWS Organizations. A key tool of AWS Organizations are the Service Control Policies (SCPs) that allow you to apply security and permission guardrails on the account level. The SCPs can ALLOW or DENY IAM actions in the policy evaluation logicwhich gives you multi-account governance.
You can find additional information about AWS Organizations here.
What is HashiCorp Terraform?
From the official HashiCorp documentation:
“Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.”
Terraform is most commonly recognized as an Infrastructure as Code (IaC) tool and is an open source alternative to Cloud IaC services like AWS CloudFormation.
Terraform can be used to plan, configure, and deploy SCPs for AWS Organizations. For a full introduction to HashiCorp Terraform, visit their website.
Terraform and Service Control Policies
Terraform has a vast network of providers available including IaaS, PaaS, and SaaS services.
For this exercise we will be using the aws_organizations_policy
and aws_organizations_policy_attachment
resources in addition to the aws_iam_policy_document
data inside the AWS provider.
Service Control Policies (SCPs)
Service Control Policies are JSON policies similar to IAM but do not grant permissions. Instead, SCPs specify the maximum permissions or can mandate conditional requirements to perform specific actions. For example, if you want to require Encryption at Rest for all S3 buckets, the JSON policy would look similar to:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::*/*",
"Condition": {
"Null": {
"s3:x-amz-server-side-encryption": true
}
}
}
]
}
This policy will DENY any S3 PutObject API call that does not specify Server Side Encryption. Prior to the March 25th AWS Organizations update, Conditions and the ability to specifying specific Resources were not supported in SCPs. As you can see, this update opens new opportunities for protecting your accounts and enforcing best practices.
Terraform Templates
Terraform templates are written in the HashiCorp Configuration Language (HCL), which is similar to JSON but more human readable (opinions may vary). As mentioned previously, we will be using awsorganizations_policy to configure our SCP. There are two main ways to configure the policy resource inside Terraform. You can embed the JSON into the policy resource, or you can use theaws_iam_policy_document
data document instead. An example SCP enforcing **_Encryption at Rest** written in HCL would look like:
data "aws_iam_policy_document" "deny_unencrypted_uploads" {
statement {
sid = "DenyUnencryptedUploads"
actions = [
"s3:PutObject",
]
resources = [
"arn:aws:s3:::*/*",
]
effect = "Deny"
condition {
test = "**Null**"
variable = "**s3:x-amz-server-side-encryption"
values = [
"true",
]
}
}
}
resource "aws_organizations_policy" "deny_unencrypted_uploads" {
name = "Deny Unencrypted S3 Uploads"
description = "Deny the ability to upload an unencrypted S3 Object."
content = "${data.aws_iam_policy_document.deny_unencrypted_uploads.json}"
}
Notice that we convert the JSON policy into HCL using a data document and under the content section for the policy we reference the data document. By using a data document you are able to further modularize your code. To attach the SCP to an Account, Organizational Unit or root account using var.target_id as a variable and aws_organizations_policy_attachment as the Terraform resource:
resource "aws_organizations_policy_attachment" "deny_unencrypted_uploads_attachment" {
policy_id = "${aws_organizations_policy.deny_unencrypted_uploads.id}"
target_id = "${var.target_id}"
}
All together inside your main.tf
data "aws_iam_policy_document" "deny_unencrypted_uploads" {
statement {
sid = "DenyUnencryptedUploads"
actions = [
"s3:PutObject",
]
resources = [
"arn:aws:s3:::*/*",
]
effect = "Deny"
condition {
test = "Null"
variable = "s3:x-amz-server-side-encryption"
values = [
"true",
]
}
}
}
resource "aws_organizations_policy" "deny_unencrypted_uploads" {
name = "Deny Unencrypted S3 Uploads"
description = "Deny the ability to upload an unencrypted S3 Object."
content = "${data.aws_iam_policy_document.deny_unencrypted_uploads.json}"
}
resource "aws_organizations_policy_attachment" "deny_unencrypted_uploads_attachment" {
policy_id = "${aws_organizations_policy.deny_unencrypted_uploads.id}"
target_id = "${var.target_id}"
}
Server Side Encryption is a type of encryption used to enforce Encryption at Rest. All of the code above and more located in our public GitHub repo. Make a Pull Request to contribute or Fork to start your own!
The above main.tf
is only a small piece involved with deploying a Service Control Policy to an AWS Organization using Terraform. For more detailed steps, review our README.md.
There are a lot of security guardrails available to you using Service Control Policies. Here are more ideas to get you started!
-
Require MFA for S3 Delete
-
Require MFA for EC2 Stop & Termination
-
Denying Non-TLS S3 Calls