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.
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:
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:
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:
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
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:
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.
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.
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.