• Stars
    star
    1,253
  • Rank 37,509 (Top 0.8 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 9 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Linting tool for CloudFormation templates


cfn_nag

Background

The cfn-nag tool looks for patterns in CloudFormation templates that may indicate insecure infrastructure. Roughly speaking, it will look for:

  • IAM rules that are too permissive (wildcards)
  • Security group rules that are too permissive (wildcards)
  • Access logs that aren't enabled
  • Encryption that isn't enabled
  • Password literals

For more background on the tool, please see this post at Stelligent's blog:

Finding Security Problems Early in the Development Process of a CloudFormation Template with "cfn-nag"

Installation

Gem Install

Presuming Ruby >= 2.5.x is installed, installation is just a matter of:

gem install cfn-nag

Brew Install

On MacOS or Linux you can alternatively install with brew:

brew install ruby brew-gem
brew gem install cfn-nag

CodePipeline

To run cfn_nag as an action in CodePipeline, you can deploy via the AWS Serverless Application Repository.

Usage

To execute:

cfn_nag_scan --input-path <path to cloudformation json>

The path can be a directory or a particular template. If it is a directory, all .json, .template, .yml and .yaml files will be processed, including recursing into subdirectories.

The default output format is free-form text, but json output can be selected with the --output-format json flag.

Optionally, a --debug flag will dump information about the internals of rule loading.

Run with --help for a full listing of supported switches.

To see a list of all the rules cfn-nag currently supports, there is a command-line utility that will dump them to stdout:

cfn_nag_rules

Results

  • The results are dumped to stdout
  • A failing violation will return a non-zero exit code.
  • A warning will return a zero/success exit code.
  • A fatal violation stops analysis (per file) because the template is malformed in some severe way

Running in Docker

A Dockerfile is provided for convenience. It is published on DockerHub as stelligent/cfn_nag.

https://hub.docker.com/r/stelligent/cfn_nag

You can also build it locally.

docker build -t stelligent/cfn_nag .

You can mount a local directory containing templates into the Docker container and then call cfn_nag in the container. This example uses the test templates used in unit testing cfn_nag:

$ docker run -v `pwd`/spec/test_templates:/templates -t stelligent/cfn_nag /templates/json/efs/filesystem_with_encryption.json
{
  "failure_count": 0,
  "violations": [

  ]
}
$ docker run -v `pwd`/spec/test_templates:/templates -t stelligent/cfn_nag /templates/json/efs/filesystem_with_no_encryption.json
{
  "failure_count": 1,
  "violations": [
    {
      "id": "F27",
      "type": "FAIL",
      "message": "EFS FileSystem should have encryption enabled",
      "logical_resource_ids": [
        "filesystem"
      ]
    }
  ]
}

Running as a GitHub Action

cfn_nag_scan can be run as part of a GitHub Workflow to evaluate code during continuous integration pipelines.

In your GitHub Workflow file, create a step which uses the cfn_nag Action:

- name: Simple test
  uses: stelligent/cfn_nag@master
  with:
    input_path: tests

More information about the GitHub Action can be found here.

Results Filtering

Profiles

cfn-nag supports the notion of a "profile" which is effectively an allow list of rules to apply. The profile is a text file that must contain a rule identifier per line. When specified via the --profile-path command line argument, cfn-nag will ONLY return violations from those particular rules.

The motivation behind creating a "profile" is that different developers might care about different rules. For example, an "infrastructure_developer" might care about IAM rules, while an "app_developer" might not even be able to create IAM resources and therefore not care about those rules.

Here is an example profile:

F1
F2
F27
W3
W5

Global Deny List

The deny list is basically the opposite of the profile: it's a list of rules to NEVER apply. When specified via the --deny-list-path command line argument, cfn-nag will NEVER return violations from those particular rules specified in the file.

In case a rule is specified in both, the deny list will take priority over the profile, and the rule will not be applied.

The format is as follows. The only two salient fields are RulesToSuppress and the id per item. The reason won't be interpreted by cfn-nag, but it is recommended to justify and document why the rule should never be applied.

RulesToSuppress:
- id: W3
  reason: W3 is something we never care about at enterprise X

Per-Resource Rule Suppression

In the event that there is a rule that you want to suppress, a cfn_nag Metadata key can be added to the affected resource to tell cfn_nag to not raise a failure or warning for that rule.

For example, if you are setting up a public-facing ELB that's open to inbound connections from the internet with resources like the following:

public_alb.yaml

# Partial template
PublicAlbSecurityGroup:
  Properties:
    GroupDescription: 'Security group for a public Application Load Balancer'
    VpcId:
      Ref: vpc
  Type: AWS::EC2::SecurityGroup
PublicAlbSecurityGroupHttpIngress:
  Properties:
    CidrIp: 0.0.0.0/0
    FromPort: 80
    GroupId:
      Ref: PublicAlbSecurityGroup
    IpProtocol: tcp
    ToPort: 80
  Type: AWS::EC2::SecurityGroupIngress

cfn_nag will raise warnings like the following:

$ cfn_nag_scan -i public_alb.yaml
------------------------------------------------------------
public_alb.yaml
------------------------------------------------------------------------------------------------------------------------
| WARN W9
|
| Resources: ["PublicAlbSecurityGroup"]
|
| Security Groups found with ingress cidr that is not /32
------------------------------------------------------------
| WARN W2
|
| Resources: ["PublicAlbSecurityGroup"]
|
| Security Groups found with cidr open to world on ingress.  This should never be true on instance.  Permissible on ELB

Failures count: 0
Warnings count: 2

By adding the metadata, these warnings can be suppressed:

public_alb_with_suppression.yaml

# Partial template
PublicAlbSecurityGroup:
  Properties:
    GroupDescription: 'Security group for a public Application Load Balancer'
    VpcId:
      Ref: vpc
  Type: AWS::EC2::SecurityGroup
  Metadata:
    cfn_nag:
      rules_to_suppress:
        - id: W9
          reason: "This is a public facing ELB and ingress from the internet should be permitted."
        - id: W2
          reason: "This is a public facing ELB and ingress from the internet should be permitted."
PublicAlbSecurityGroupHttpIngress:
  Properties:
    CidrIp: 0.0.0.0/0
    FromPort: 80
    GroupId:
      Ref: PublicAlbSecurityGroup
    IpProtocol: tcp
    ToPort: 80
  Type: AWS::EC2::SecurityGroupIngress
$ cfn_nag_scan -i public_alb_with_suppression.yaml
------------------------------------------------------------
public_alb_with_supression.yaml
------------------------------------------------------------
Failures count: 0
Warnings count: 0

Setting Template Parameter Values

CloudFormation Template Parameters can present a problem for static analysis as the values are specified at the point of deployment. In other words, the values aren't available when the static analysis is done - static analysis can only look at the "code" that is in front of it. Therefore a security group ingress rule of 0.0.0.0/0 won't be flagged if the cidr is parameterized and the 0.0.0.0/0 is passed in at deploy time.

To allow for checking parameter values, a user can specify the parameter values in a JSON file passed on the command line to both cfn_nag and cfn_nag_scan with the --parameter-values-path=<filename/uri> flag.

The format of the JSON is a single key, "Parameters", whose value is a dictionary with each key/value pair mapping to the Parameters:

{
  "Parameters": {
    "Cidr": "0.0.0.0/0"
  }
}

This will provide "0.0.0.0/0" to the following Parameter:

Parameters:
  Cidr:
    Type: String

BEWARE that if there are extra parameters in the JSON, they are quietly ignored (to allow cfn_nag_scan to apply the same JSON across all the templates).

If the JSON is malformed or doesn't meet the above specification, then parsing will fail with a FATAL violation.

Mappings

Prior to 0.5.55, calls to Fn::FindInMap were effectively ignored. The underlying model would leave them be, and so they would appear as Hash values to rules. For example: { "Fn::FindInMap" => [map1, key1, key2]}

Starting in 0.5.55, the model will attempt to compute the value for a call to FindInMap and present that value to the rules. This evaluation supports keys that are:

  • static text
  • references to parameters (with parameter substitution)
  • references to AWS pseudofunctions (see next section)
  • nested maps

If the evaluation logic can't figure out the value for a key, it will default to the old behavior of returning the Hash for the whole expression.

AWS Pseudofunctions

Also prior to 0.5.55, calls to AWS pseudofunctions were effectively ignored. The underlying model would leave them be, and so they would appear as Hash values to rules. For example: {"Ref"=>"AWS::Region"}. A common use case is to organize mappings by region, so pseudofunction evaluation is important to better supporting map evaluation.

Starting in 0.5.55, the model will present the following AWS pseudofunctions to rules with the default values:

'AWS::URLSuffix' => 'amazonaws.com',
'AWS::Partition' => 'aws',
'AWS::NotificationARNs' => '',
'AWS::AccountId' => '111111111111',
'AWS::Region' => 'us-east-1',
'AWS::StackId' => 'arn:aws:cloudformation:us-east-1:111111111111:stack/stackname/51af3dc0-da77-11e4-872e-1234567db123',
'AWS::StackName' => 'stackname'

Additionally, the end user can override the value supplied via the traditional parameter substitution mechanism. For example:

{
  "Parameters": {
    "AWS::Region": "eu-west-1"
  }
}

Controlling the Behavior of Conditions

Up until version 0.4.66 of cfn_nag, the underlying model did not do any processing of Fn::If within a template. This meant that if a property had a conditional value, it was up to the rule to parse the Fn::If. Given that an Fn::If could appear just about anywhere, it created a whack-a-mole situation for rule developers. At best, the rule logic could ignore values that were Hash presuming the value wasn't a Hash in the first place.

In order to address this issue, the default behavior for cfn_nag is now to substitute Fn::If with the true outcome. This means by default that rules will not inspect the false outcomes for security violations.

In addition to substituting Fn::If at the property value level, the same behavior is applied to Fn::If at the top-level of Properties. For example:

Resource1:
  Type: Foo
  Properties: !If
    - IsNone
    - Description: Up
    - Description: DOwn

Will look the same as:

Resource1:
  Type: Foo
  Properties:
    Description: Up

To provide some control over this behavior, a user can specify the condition values in a JSON file passed on the command line to both cfn_nag and cfn_nag_scan with the --condition-values-path=<filename/uri> flag.

The format of the JSON is a a dictionary with each key/value pair mapping to the Conditions:

{
  "Condition1": true,
  "Condition2": false
}

Stelligent Policy Complexity Metrics (spcm)

The basis for SPCM is described in the blog post Thought Experiment Proposed Complexity Metric for IAM Policy Documents.

Starting in version 0.6.0 of cfn_nag:

  • spcm_scan can scan a directory of CloudFormation templates (like cfn_nag_scan) and generate a report with the SPCM metrics in either JSON or HTML format
  • A rule is added (to cfn_nag) to warn on an IAM::Policy or IAM::Role with a SPCM score of >= 50 (default)
  • The rule threshold can be controlled via the command line: cfn_nag_scan --rule-arguments spcm_threshold:100
  • Custom rule developers can now develop rules to accept end user values for settings via the same --rule-arguments mechanism. The Rule object only needs to declare an attr_accessor, e.g. attr_accessor :spcm_threshold and cfn_nag will take care of the details to inject values from the --rule-arguments

Distribution of Custom Rules

The release of 0.5.x includes some major changes in how custom rules (can) be distributed and loaded. Before this release, there were two places where rules were loaded from: the lib/cfn-nag/custom_rules directory within the core cfn_nag gem, and the custom-rule-directory specified on the command line.

There are two use cases that forced a redesign of how/where custom rules are loaded. The rule loading mechanism has been generalized such that custom rule repositories can be used to discover rules.

  1. A bunch of "rule files" sitting around on a filesystem isn't great from a traditional software development perspective. There is no version or traceability on these files, so 0.5.x introduces the notion of a "cfn_nag rule gem". A developer can develop custom rules as part of a separate gem, version it and install it... and those rules are referenced from cfn_nag as long as the gem metadata includes cfn_nag_rules => true. For a gem named like "cfn-nag-hipaa-rules", any *.rb under lib/cfn-nag-hipaa-rules will be loaded. Any custom rules should derive from CfnNag::BaseRule in cfn-nag/base_rule (not cfn-nag/custom-rules/base). If the rule must derive from something else, defining a method cfn_nag_rule? that returns true will also cause it to be loaded as a rule.

  2. When cfn_nag is running in an AWS Lambda - there isn't really a filesystem (besides /tmp) in the traditional sense. Therefore, only core rules are usable from the Lambda. To support custom rules, cfn_nag supports discovering rules from an S3 bucket instead of the filesystem.

Everything you've likely seen about how to develop custom rules in Ruby still holds true.

To discover rules from an S3 bucket, create a file s3.yml with this content:

---
repo_class_name: S3BucketBasedRuleRepo
repo_arguments:
  s3_bucket_name: cfn-nag-rules-my-enterprise
  prefix: /rules

To apply *Rule.rb files in the bucket cfn-nag-rules-my-enterprise with the prefix /rules (e.g. /rules/MyNewRule.rb), specify this file on the command line to cfn_nag as such:

cat my_cfn_template.yml | cfn_nag --rule-repository s3.yml

If rules are in more than one bucket, then create multiple s3*.yml files and specify them in the --rule-repository argument.

If the ambient AWS credentials have permission to access the bucket cfn-nag-rules-enterprise then it will find all rules like /rules/*Rule.rb. If a particular aws_profile should be used, add it as a key under repo_arguments, e.g aws_profile: my_aws_profile

Beyond the filesystem, gem installs and S3 - the new architecture theoretically supports developing other "rule repositories" to load rules from DynamoDb, relational databases, or other web services.

Development

New Rules

To author new rules for your own use and/or community contribution, see Custom Rule Development for details.

A screencast demonstrating soup-to-nuts TDD custom rule development is available here:

https://www.youtube.com/watch?v=JRZct0naFd4&t=1601s

Specs

To run the specs, you need to ensure you have Docker installed and cfn_nag dependencies installed via

gem install bundle
bundle install

Then, to run all of the specs, just run rake test:all.

To run the end-to-end tests, run rake test:e2e. The script will bundle all gems in the Gemfile, build and install the cfn_nag gem locally, install spec dependencies, and then executes tests tagged with 'end_to_end'. It will also pull down sample templates provided by Amazon and run cfn_nag_scan against them, to see if any known-good templates cause exceptions within cfn-nag.

Local Install

To install the current git branch locally:

bundle install
scripts/deploy_local.sh

VS Code Remote Development

There is a complete remote development environment created and setup with all the tools and settings pre-configured for ease in rule development and creation. You can enable this by using the VS Code Remote development functionality.

  • Install the VS Code Remote Development extension pack
  • Open the repo in VS Code
  • When prompted Folder contains a dev container configuration file. Reopen folder to develop in a container click the Reopen in Container button
  • When opening in the future use the [Dev Container] cfn_nag Development option

More information about the VS Code Remote Development setup can be found here, VS Code Remote Development.

Support

To report a bug or request a feature, submit an issue through the GitHub repository via: https://github.com/stelligent/cfn_nag/issues/new

More Repositories

1

mu

A full-stack DevOps on AWS framework
Go
973
star
2

cloudformation_templates

AWS - CloudFormation Templates
Shell
571
star
3

config-lint

Command line tool to validate configuration files
HCL
193
star
4

devops-essentials

Source code samples for DevOps Essentials on AWS Complete Video Course
HTML
161
star
5

pipeline-dashboard

Simple dashboard for pipelines on AWS
JavaScript
155
star
6

cloudformation-custom-resources

Java
122
star
7

dromedary

Sample app to demonstrate a working pipeline using Infrastructure as Code and AWS Code Services
JavaScript
106
star
8

stelligent-u

Templates and code for Stelligent U lessons
JavaScript
98
star
9

sagemaker-pipeline

Sagemaker pipeline for AWS Summit New York
Python
58
star
10

devopsinthecloud

HTML
50
star
11

stelligent_commons

Scripts and other utilities we commonly use
Ruby
48
star
12

aws-devsecops-workshop

A continuous security pipeline demo for the AWS DevSecOps Workshop.
Ruby
45
star
13

cfn-leaprog

cfn-LEAst-Privilege-ROle-Generator: Experimental tool for generating least privileged IAM roles for CloudFormation and Service Catalog Launch Constraints.
Ruby
40
star
14

dromedary-serverless

Dromedary...without servers.
JavaScript
38
star
15

serverspec-aws-resources

Some serverspec resources to allow testing AWS resources. This repository is deprecated - you should instead use: https://github.com/k1LoW/awspec
Ruby
27
star
16

microservice-exemplar

Sample microservice built with Spring Boot to manage bananas. 🍌
Java
23
star
17

mutato

Repo formerly known as mu-cdk. A.K.A Mu2. Pronounced: mew-tah-toe
TypeScript
23
star
18

config-rule-status

A project to create AWS Config Rules and use them to test AWS Resource compliance.
JavaScript
21
star
19

skaffold_on_aws

example of running skaffold on an aws eks cluster
HTML
21
star
20

continuous_integration_example

Demo project for Continuous Integration - from the book Continuous Integration (Duvall, et. al)
XSLT
20
star
21

opendelivery_platform

Open platform for CD
Ruby
17
star
22

tophat

If I have to stand up a Jenkins server ONE MORE TIME, I'm going to turn this car around!
Ruby
12
star
23

asgard-in-the-cloud

Setup Asgard in ten minutes using a CloudFormation and Chef.
Ruby
11
star
24

potemkin-decorator

Potemkin is a decorator to setup initial conditions for a boto "integration test" with real AWS services via CloudFormation, and to tear them down as well.
Python
11
star
25

keystore

Secure storage of secrets using Amazon Web Services
Ruby
10
star
26

crossing

Utility for storing objects in S3 while taking advantage of client side envelope encryption with KMS
Ruby
10
star
27

packer-ami-pipeline

Demo CodePipeline for building and publishing AMIs with Packer
Ruby
10
star
28

cfn-nag-pipeline

Lambda function to run cfn_nag in CodePipeline
Ruby
10
star
29

stelligent_pipelines

CI/CD pipelines
Shell
9
star
30

crossing-go

Utility for storing objects in S3 while taking advantage of client side envelope encryption with KMS
Go
9
star
31

aws-anchore-engine-scanner

This guide details steps and procedures you can follow to create, launch and implement your own standalone container scanning solution within AWS ecosystem. This approach uses an opensource container scanning tool called Anchore Engine as a proof-of-concept and provides examples of how Anchore integrates with your favorite CI/CD systems orchestration platforms.
Python
9
star
32

minimal-pipeline-gem

Minimal helpers for automating CloudFormation pipelines with ruby
Ruby
8
star
33

python-testing

Examples of testing with python related to AWS boto3 use
Python
7
star
34

zap

Build tools for OWASP Zed Attack Proxy
Ruby
7
star
35

cloudpatrol

Rails App for AWS Policy Management and Cleanup
Ruby
7
star
36

cfn-model

An object model for CloudFormation templates
Ruby
6
star
37

cfnctl

Control Cloudformation lifecycle
Python
6
star
38

inspector-status

A pipeline plugin to get the AWS Inspector findings from AWS Resources
Ruby
6
star
39

stelligent_demo

Python
6
star
40

aws_group_policy

templates for creating a full privilege group and a read only group, as well as a script for moving non-mfa'd users from the privileged group to the read only group
Ruby
6
star
41

aws-inspector-quickstart

aws-inspector-quickstart
Python
6
star
42

test-platform

Python
5
star
43

cfn_nag_examples

5
star
44

lockdown

AWS Emergency Compromise Response
Python
5
star
45

openvpn-ami

Automation for generating an OpenVPN AMI
Ruby
5
star
46

aws-int-test-rspec-helper

RSpec helper for doing AWS SDK integration testing
Ruby
5
star
47

ciexample_jenkins

5
star
48

honolulu_jenkins_cookbooks

Cookbooks for spinning up a Jenkins Server for the Honolulu Answers application
Ruby
5
star
49

jenkins_chef_cookbooks

A collection of cookbooks used to set up a Jenkins server
Ruby
4
star
50

empty-stack

Creating an empty stack in cloudformation to reduce the errors on creation.
Python
4
star
51

yq

YAML query, tool for querying YAML from the command line
Go
4
star
52

mu-cloud9

mu extension for AWS Cloud9
4
star
53

vpc-with-client-vpn

Shell
4
star
54

developer-sandboxes

Using Access Based Access Controls (Tags) in AWS to create Developer Sandboxes for EC2.
Python
4
star
55

stellitime-api

Example mini-app to use during interview process.
Python
3
star
56

cfn-nag-service

Exposes cfn-nag as a service through a Lambda/APIGW or Docker image
Ruby
3
star
57

drifter

Demo of CloudFormation Drift Detection
Shell
3
star
58

stelligent_jenkins_cookbooks

Cloudformation template, startup script, OpsWorks repo for setting up a production account VPC + Jenkins instance
Ruby
3
star
59

cloud-custodian-example

Example project for Cloud Custodian deployment using CloudFormation
3
star
60

cumulogenesis

Python
3
star
61

nando_automation_demo

nando_automation_demo
Python
3
star
62

sample-pipeline-with-cfn-nag

Sample repository to demonstrate using cfn_nag in CodePipeline
Makefile
3
star
63

deploy-button

functions and templates to have a deploy button for your code pipeline
Python
3
star
64

hab-demo-pipeline

POC for automating Habitat with AWS CodePipeline
JavaScript
2
star
65

utility-vagrants

A collection of utility VMs defined in a Vagrantfile
Shell
2
star
66

hamburgerstore

Data store for pipeline instance metadata. Nothing to do with hamburgers. Sorry.
Ruby
2
star
67

jenkins-factory

Ruby
2
star
68

banana-service

Sample microservice for mu Edit Add topics
Java
2
star
69

amz-linux-hardening

Ruby
2
star
70

aws-stubs-intro

Demonstrates the use of AWS Stubs for the Ruby v2 SDK
Ruby
2
star
71

devopsinthecloudpuppet

Puppet
2
star
72

jenkins_cookbooks

This is Stelligent's open source Jenkins server setup. It only have a few Jenkins-server-maintenance jobs, but is designed to be easily extended to create a pipeline for your application.
Ruby
2
star
73

mu-cfn_nag

mu extension for adding cfn_nag to your pipeline
2
star
74

serverless-synchronous-resource-plugin

Serverless plugin for deploying custom CFN stacks
JavaScript
2
star
75

mu-extension-example

JavaScript
2
star
76

docker-api-ecr-sample

Ruby
2
star
77

parameter-store-example

Shows an example how to use the AWS Parameter Store Service
Ruby
2
star
78

devopsinthecloudjenkins

1
star
79

mu-pipeline-dashboard

mu extension for Stelligent's Pipeline Dashboard
1
star
80

mu-elasticsearch

1
star
81

mu-minimal-ec2

Continuous Delivery for Microservices on EC2 with mu
Shell
1
star
82

iam_complexity_metrics

Experimenting with the notion of complexity metrics for IAM policy documents
Ruby
1
star
83

aws-trend-micro-dssc

An example of how to integrate Trend Micro Deep Security Smart Check with AWS CodePipeline
Makefile
1
star
84

dns-test

Testing multi-account public hosted zones
HTML
1
star
85

cfn-man

command line documentation for cloud formation resources
Python
1
star
86

MySQL-Vault

Shell
1
star
87

BenchOps-Resources

Useful resources from the bench
1
star
88

homebrew-tap

This is Stelligent's homebrew tap. Formulas should go in here.
Ruby
1
star
89

mu-workshop-lab1

Lab for learning mu
Java
1
star
90

lz-lambdas

Landing Zone Lambda Code
Python
1
star
91

iot-pipeline

JavaScript
1
star
92

elastic-volumes-lambda

Lambda and example terraform to demo automation of elastic volume live modifications
HCL
1
star
93

mu-workshop-lab2

Lab for learning mu
1
star
94

jenkins-worker-base

Provide a base docker image for jenkins workers
Shell
1
star
95

munatra

Mu implementation with Sinatra 'Hello World' app and CasperJS acceptance testing.
Ruby
1
star
96

opendelivery_jenkins

1
star
97

mu-workshop-lab3

Lab for learning mu
Java
1
star
98

cfn-nag-rule-repository-complete

Ruby
1
star
99

chat-app

A simple chat application for demo purposes
CSS
1
star
100

s3-archive-delete

Archive and delete contents of an S3 bucket so it can then be deleted itself
JavaScript
1
star