Continuousdelivery pipelines, combined with infrastructure as code tools like AWS CloudFormation, allow our customers to manage applications in a safe and predictable way. CloudFormation helps customers model and provision their AWS and third-party application resources, with features such as rollback to provide automation and safety. Together with tools such as AWS CodeBuild, AWS CodePipeline, and AWS CodeDeploy, CloudFormation helps customers automate processes that implement popular practices like GitFlow , allowing for incremental testing and automated guardrails for fast releases. Emerging best practices, such as DevSecOps, shift-left testing and low-code tools, are further enabling security leaders to embed preventive compliance rules earlier in the development lifecycle.
These best practices inspired us to build CloudFormation Guard (cfn-guard) as an open-source tool that helps you write compliance rules using a simple, policy as code language. It will help you validate CloudFormation templates against those rules to keep your AWS resources in compliance with your company policy guidelines. Customers, engineers and AWS AWS Professional Services consultants have used cfn-guard as part of a private beta. Based on the positive feedback we received, we have made it publicly available on GitHub. You can use cfn-guard to both evaluate templates locally as you write them and after you submit them to be deployed in your CI/CD pipelines. In this article, we will show you how to use cfn-guard and offer tips to integrate it into your application management processes.
Although similar tools exist to create custom compliance rules, such as cfn-nag, cfripper, and checkov, cfn-guard uses a domain-specific language (DSL) to write rules. Learning the rule language is easier than learning a programming language like Python or Ruby, which is required to make custom rules in similar tools. Because cfn-guard is written in Rust, it can be compiled to a native binary to evaluate thousands of rules across templates.
To prepare your environment, we will download Rust, then get and build cfn-guard, and finally verify our installation. You can install Rust in macOS, Linux, and Windows machines with rustup; we will mostly cover the instructions specific to macOS here. Refer to the GitHub repository for additional installation options.
We will use Homebrew as our macOS package manager, then rustup-init to install the compiler and the cargo package manager, along with the rest of the Rust default toolchain to create binaries. Use the following three commands in your terminal:
Consider the following scenario: you want to allow your developers to create ephemeral environments so they can test their code using the latest Amazon Linux distribution. To control costs, you want to allow them to create Amazon EC2 instances using only small instance types. You want to restrict the size of the attached EBS volumes while ensuring consistency with other managed volumes. To comply with security requirements, you want developers to create only encrypted volumes and use a non-standard port for secure shell access to the instances.
You will provide developers with instructions on how to deploy these environments on-demand, including how to use cfn-guard rule files to author compliant templates. You can setup a controlled pipeline to enforce rules; if developers then attempt to submit a non-compliant template, the pipeline will throw an error and not deploy the resources.
Note that cfn-guard focuses on the Resources section of your template; however, you can still use other CloudFormation capabilities in your code, including Parameters and Outputs. For this example, you are allowing the developers to use their own (previously assigned) key pairs. Further, you are leveraging a publicly provided value via AWS Systems Manager Parameter Store that picks up the latest Amazon Linux image, regardless of the region where the template is executed.
As it stands, this template is syntactically correct: it complies with all built-in rules and warnings as evaluated by the current version of the CloudFormation linter (cfn-lint). However, similar to other tools mentioned before, cfn-lint requires you to write custom rules in Python. If you have invested in custom rules for cfn-lint or other tools, you can continue to use those rules together with cfn-guard.
There are other options and additional functionality that can be used when writing rules. For example, the second rule (repeated below) explicitly checks for this literal value (33322) and syntax of the security group:
You can leverage wildcards to make your rule more explicit and flexible. For example, the following version of the second rule will check explicitly for having the valid port (in our context, port 33322) and explicitly for not having the default port (port 22 for SSH). This revised rule will also work if the security group has other rules, making the rule itself more reusable in other templates. To do so, it uses * as a wildcard in the Property Name:
There are other conventions in cfn-guard to write rules. For example, you can write the volume size rule in separate lines, or you can use the let option to add all valid values in one place in your code. Arguably, this convention improves the readability and conciseness of the rules file. It also minimizes future rule changes if other size options need to be added:
How about AND expressions? As it turns out, cfn-guard considers all lines together in a rule file to be an implicit AND operation. This is common across security tools, similar to how IAM conditions with multiple keys and values are evaluated in policies: the overall evaluation succeeds when all the rules evaluate successfully.
3a8082e126