AWS Cloud Operations Blog
Policy-as-Code for Securing AWS and Third-Party Resource Types
This post was written by Scott Alexander and Kevin Formsma from Mphasis Stelligent.
Every day, more developers are having lightbulb moments as they realize they can design and manage their infrastructure. It’s our responsibility, as practitioners of the DevOps mindset, to build systems that allow developers to move quickly and speed up the feedback loop by adopting practices and tools for detecting issues early in the Software Development Life Cycle (SDLC). This includes validating — in developers’ workstations and in delivery pipelines — infrastructure as code configurations for compliance with company standards and best practices. For example, limiting choices for Amazon Elastic Compute Cloud (Amazon EC2) instance sizes or Availability Zones, blocking public Access Control Lists (ACLs) on Amazon Simple Storage Service (Amazon S3) buckets, as well as compliance with regulations such as SOX, HIPAA, and GDPR.
Key Tools
AWS CloudFormation helps developers model, provision, and manage AWS and third-party resources in a safe, repeatable, and predictable manner. Stelligent introduced cfn_nag in 2016 as a static analysis tool for CloudFormation. cfn_nag today includes over 150 rules to check your templates for common errors and security issues. In October 2020, AWS released AWS CloudFormation Guard (Guard), which had a major overhaul with its second release in May 2021. In this blog post, Introducing AWS CloudFormation Guard 2.0, Matteo describes numerous features of Guard 2.0, including providing developers with the ability to write policy rules for any JSON and YAML formatted data file: for example, configurations for Kubernetes and Terraform JSON configurations in addition to CloudFormation templates.
Both cfn_nag and Guard are open source tools. Developers can choose to utilize both tools and write rules for company policy compliance and security best practices. Developers validate infrastructure described with code for policy compliance against rules in order to prevent the provisioning of insecure or non-compliant resource configurations.
Both tools differ primarily in how developers write and manage rules. Developers write cfn_nag rules in Ruby, save rules in files, and package rules as a Ruby Gem or store rules in an S3 bucket. Developers write Guard rules in a domain-specific language (DSL) instead. They save rules in files and choose a location for storing rules, such as an S3 bucket or code repository. The table below summarizes the differences between the two tools:
Feature | cfn_nag | AWS CloudFormation Guard 2.0 |
Rules out of the box | 150+ | 10 example rules available in the source code repository as of September 2021 |
Rule definition | Ruby code | Guard DSL |
Rule testing | RSpec/Ruby | Built-in rule testing feature |
Rule validation targets | CloudFormation templates (JSON, YAML) | Any JSON- and YAML-formatted configuration data, including CloudFormation templates |
Developers looking to quickly leverage policy-as-code validation in their delivery pipelines find immediate value with cfn_nag’s built-in ruleset. Choose to leverage these rules in every pipeline for your CloudFormation-based infrastructure to perform a baseline security review of your templates before deployment. Developers needing to build their own policy-as-code rules find Guard DSL easy to learn and implement. Furthermore, developers with prior Ruby experience can chose to leverage cfn_nag for rule definitions.
Let’s go over examples of how to leverage both tools in your workflow, with a focus on third-party extensions. The AWS CloudFormation Public Registry allows developers to create and manage third-party services as resources in your CloudFormation templates. By writing rules for Guard or cfn_nag, developers can automate the validation of third-party service configurations in their deployment pipelines. Conducting static analysis on third-party integrations at this stage increases security by preventing non-compliant configurations releases.
Example: OpsGenie users have company email IDs
Let’s write a Guard rule to validate that users, as created for Atlassian OpsGenie, only have company email IDs specified in their configuration:
Template snippet
Guard rule
Guard rules are based on clauses that provide assertions, such as Username ==/.*@stelligent.com/
. Rules can then be extended by utilizing various filters and queries to restrict the resources being applied to clauses. In this example rule, the Username
property of any Atlassian::Opsgenie::User
resource is required to match the regular expression showing the company domain.
Example: Preventing hardcoded access keys
Evaluating templates for hardcoded access keys is an important use case for any analysis tool. Developers can implement this rule with Guard. Let’s use the New Relic resource provider shown next as an example:
Template snippet
Guard rule
In this example rule, you created two clauses that work on the NewRelic::Alerts::NrqlAlert
resource type. The first clause is for the ApiKey
property, and the second is for the PolicyId
property. Both clauses leverage regular expressions in order to validate that property values don’t match literal keys or IDs.
Example: Ensure team tags are present
Let’s write another Guard rule to validate that Datadog integration resources always have a team tag specified. The Datadog resource type utilizes a property called HostTags
for this purpose. Guard lets you create simple rules to represent this condition:
Template snippet
Guard rule
In this rule, HostTags
is a list of items with the following format: HostTags == ["tagNameOne:value","tagNameTwo:value"...]
. You use a filter to select every Datadog-related resource type, and then you create a clause to validate that at least some of them match a regular expression for a team tag.
Example: Ensure database communication uses SSL
The Aqua::Enterprise::Server
resource type from Aqua lets users configure the resource by providing values as a YAML string. In this example, we show how to use cfn_nag in order to write a rule to parse the provided YAML string and evaluate the configuration. For example, you want to ensure that communication to the database utilizes an SSL connection:
Template snippet
cfn_nag rule
With this cfn_nag rule, you validate that Aqua::Enterprise::Server
resources have db.ssl
set to true
in the valueYaml
property content. The rule parses the YAML-formatted document, and then filters the results. You could extend this rule to add additional checks for the configuration properties of the Aqua Security Server.
Learn more about writing Guard rules in Introducing AWS CloudFormation Guard 2.0 blog post. For more information on custom rule development for cfn_nag, check Extending cfn_nag with custom rules.
Integrating with your Pipelines
Once you have developed your rules, it’s time to integrate them into your Continuous Integration/Continuous Delivery (CI/CD) pipelines and to validate your AWS and third-party resources against your rules. For an example pipeline integration, check Integrating AWS CloudFormation Guard into CI/CD pipelines.
Conclusion
This post shows you examples of how to leverage both cfn_nag and AWS CloudFormation Guard for policy compliance of AWS and third-party CloudFormation registry resources. Start creating your rules today! Your developers will appreciate the additional ability to manage their own tools, and you will be freed up to focus on higher-value work.
Unless otherwise noted, the code snippets in this blog post are licensed under SPDX-License-Identifier: MIT-0.