Skip to content

Commit

Permalink
start adding better tests for Terraform built in rules
Browse files Browse the repository at this point in the history
  • Loading branch information
lhitchon committed Oct 28, 2018
1 parent 25724ad commit c51d8f2
Show file tree
Hide file tree
Showing 21 changed files with 1,261 additions and 16 deletions.
27 changes: 11 additions & 16 deletions cli/assets/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -633,17 +633,6 @@ rules:
tags:
- ami

- id: EBS_BLOCK_DEVICE_ENCRYPTED
message: EBS block devices should use encryption
resource: aws_instance
severity: FAILURE
assertions:
- every:
key: ebs_block_device
expressions:
- key: encrypted
op: is-true

- id: CLOUDTRAIL_ENCRYPTION
message: CloudTrail should use encryption
resource: aws_cloudtrail
Expand Down Expand Up @@ -700,13 +689,19 @@ rules:
tags:
- rds

- id: ELB_ACCESS_LOGS
message: ELB should have access logs configured
resource: aws_elb
- id: EBS_BLOCK_DEVICE_ENCRYPTED
message: EBS block devices should use encryption
resource: aws_instance
severity: FAILURE
assertions:
- key: access_logs
op: present
- every:
key: ebs_block_device
expressions:
- key: encrypted
op: is-true
tags:
- ec2
- ebs

- id: EBS_VOLUME_ENCRYPTION
message: EBS Volume should be encrypted
Expand Down
121 changes: 121 additions & 0 deletions cli/builtin_terraform_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package main

import (
"fmt"
"github.com/stelligent/config-lint/assertion"
"github.com/stelligent/config-lint/linter"
"github.com/stretchr/testify/assert"
"testing"
)

func loadRules(t *testing.T, filename string) assertion.RuleSet {
r, err := loadBuiltInRuleSet(filename)
assert.Nil(t, err, "Cannot load ruleset file")
return r
}

type BuiltInTestCase struct {
Filename string
RuleID string
WarningCount int
FailureCount int
}

func numberOfWarnings(violations []assertion.Violation) int {
n := 0
for _, v := range violations {
if v.Status == "WARNING" {
n += 1
}
}
return n
}
func numberOfFailures(violations []assertion.Violation) int {
n := 0
for _, v := range violations {
if v.Status == "FAILURE" {
n += 1
}
}
return n
}

func TestTerraformBuiltInRules(t *testing.T) {
ruleSet := loadRules(t, "assets/terraform.yml")
testCases := []BuiltInTestCase{
{"security-groups.tf", "SG_WORLD_INGRESS", 1, 0},
{"security-groups.tf", "SG_WORLD_EGRESS", 2, 0},
{"security-groups.tf", "SG_SSH_WORLD_INGRESS", 0, 1},
{"security-groups.tf", "SG_RD_WORLD_INGRESS", 0, 0},
{"security-groups.tf", "SG_NON_32_INGRESS", 2, 0},
{"security-groups.tf", "SG_INGRESS_PORT_RANGE", 0, 0},
{"security-groups.tf", "SG_EGRESS_PORT_RANGE", 0, 0},
{"security-groups.tf", "SG_MISSING_EGRESS", 0, 0},
{"cloudfront.tf", "CLOUDFRONT_DISTRIBUTION_LOGGING", 0, 1},
{"cloudfront.tf", "CLOUDFRONT_DISTRIBUTION_ORIGIN_POLICY", 0, 0},
{"cloudfront.tf", "CLOUDFRONT_DISTRIBUTION_DISTRIBUTION_PROTOCOl", 0, 0},
{"iam.tf", "IAM_ROLE_POLICY_NOT_ACTION", 0, 0},
{"iam.tf", "IAM_ROLE_POLICY_NOT_RESOURCE", 0, 0},
{"iam.tf", "IAM_ROLE_POLICY_WILDCARD_ACTION", 0, 0},
{"iam.tf", "IAM_ROLE_POLICY_WILDCARD_RESOURCE", 0, 0},
{"iam.tf", "IAM_POLICY_NOT_ACTION", 0, 0},
{"iam.tf", "IAM_POLICY_NOT_RESOURCE", 0, 0},
{"iam.tf", "IAM_POLICY_WILDCARD_ACTION", 0, 1},
{"iam.tf", "IAM_POLICY_WILDCARD_RESOURCE", 1, 0},
{"iam.tf", "IAM_USER", 0, 0},
{"iam.tf", "IAM_USER_POLICY_ATTACHMENT", 0, 1},
{"iam.tf", "IAM_USER_GROUP", 0, 0},
{"iam.tf", "POLICY_VERSION", 0, 1},
{"iam.tf", "ASSUME_ROLEPOLICY_VERSION", 0, 1},
{"elb.tf", "ELB_ACCESS_LOGGING", 1, 0},
{"s3.tf", "S3_BUCKET_ACL", 0, 0},
{"s3.tf", "S3_NOT_ACTION", 0, 0},
{"s3.tf", "S3_NOT_PRINCIPAL", 0, 0},
{"s3.tf", "S3_BUCKET_POLICY_WILDCARD_PRINCIPAL", 1, 0},
{"s3.tf", "S3_BUCKET_POLICY_WILDCARD_ACTION", 1, 0},
{"s3.tf", "S3_BUCKET_OBJECT_ENCRYPTION", 0, 1},
{"sns.tf", "SNS_TOPIC_POLICY_WILDCARD_PRINCIPAL", 1, 0},
{"sns.tf", "SNS_TOPIC_POLICY_NOT_ACTION", 0, 0},
{"sns.tf", "SNS_TOPIC_POLICY_NOT_PRINCIPAL", 0, 0},
{"sqs.tf", "SQS_QUEUE_POLICY_WILDCARD_PRINCIPAL", 1, 0},
{"sqs.tf", "SQS_QUEUE_POLICY_WILDCARD_ACTION", 0, 0},
{"sqs.tf", "SQS_QUEUE_POLICY_NOT_ACTION", 0, 0},
{"sqs.tf", "SQS_QUEUE_POLICY_NOT_PRINCIPAL", 0, 0},
{"sqs.tf", "SQS_QUEUE_ENCRYPTION", 0, 1},
{"lambda.tf", "LAMBDA_PERMISSION_INVOKE_ACTION", 0, 0},
{"lambda.tf", "LAMBDA_PERMISSION_WILDCARD_PRINCIPAL", 0, 0},
{"lambda.tf", "LAMBDA_FUNCTION_ENCRYPTION", 1, 0},
{"lambda.tf", "LAMBDA_ENVIRONMENT_SECRETS", 0, 1},
{"waf.tf", "WAF_WEB_ACL", 0, 0},
{"alb.tf", "ALB_LISTENER_HTTPS", 0, 3},
{"alb.tf", "ALB_ACCESS_LOGS", 0, 0},
{"ami.tf", "AMI_VOLUMES_ENCRYPTED", 0, 1},
{"ami.tf", "AMI_COPY_SNAPSHOTS_ENCRYPTED", 0, 1},
{"ec2.tf", "EBS_BLOCK_DEVICE_ENCRYPTED", 0, 0},
{"ec2.tf", "EBS_VOLUME_ENCRYPTION", 0, 2},
{"cloudtrail.tf", "CLOUDTRAIL_ENCRYPTION", 0, 1},
{"codebuild.tf", "CODEBUILD_PROJECT_ENCRYPTION", 0, 1},
{"codepipeline.tf", "CODEPIPELINE_ENCRYPTION", 0, 0},
{"db.tf", "DB_INSTANCE_ENCRYPTION", 0, 1},
{"db.tf", "RDS_CLUSTER_ENCYPTION", 0, 2},
{"efs.tf", "EFS_ENCRYPTED", 0, 1},
{"kinesis.tf", "KINESIS_FIREHOSE_DELIVERY_STREAM_ENCRYPTION", 0, 1},
{"redshift.tf", "REDSHIFT_CLUSTER_ENCRYPTION", 0, 1},
{"ecs.tf", "ECS_ENVIRONMENT_SECRETS", 0, 1},
}
for _, tc := range testCases {

filenames := []string{"testdata/builtin/terraform/" + tc.Filename}
options := linter.Options{
RuleIDs: []string{tc.RuleID},
}
vs := assertion.StandardValueSource{}
l, err := linter.NewLinter(ruleSet, vs, filenames)
report, err := l.Validate(ruleSet, options)
assert.Nil(t, err, "Validate failed for file")
warningMessage := fmt.Sprintf("Expecting %d warnings for RuleID %s in File %s", tc.WarningCount, tc.RuleID, tc.Filename)
assert.Equal(t, tc.WarningCount, numberOfWarnings(report.Violations), warningMessage)
failureMessage := fmt.Sprintf("Expecting %d failures for RuleID %s in File %s", tc.FailureCount, tc.RuleID, tc.Filename)
assert.Equal(t, tc.FailureCount, numberOfFailures(report.Violations), failureMessage)
}
}
134 changes: 134 additions & 0 deletions cli/testdata/builtin/terraform/alb.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
resource "aws_security_group" "alb_sg" {
vpc_id = "${aws_vpc.website.id}"
name = "load balancer"
ingress {
protocol = "tcp"
from_port = 80
to_port = 80
cidr_blocks = [ "0.0.0.0/0" ]
}
egress {
protocol = "tcp"
from_port = 0
to_port = 65535
cidr_blocks = [ "0.0.0.0/0" ]
}
}

resource "aws_alb" "alb" {
name = "web-alb"
internal = false
load_balancer_type = "application"
security_groups = [ "${aws_security_group.alb_sg.id}" ]
subnets = [
"${aws_subnet.public1.id}",
"${aws_subnet.public2.id}"
]
access_logs {
bucket = "${aws_s3_bucket.alb_logs.bucket}"
enabled = true
}
}

resource "aws_s3_bucket" "alb_logs" {}

resource "aws_s3_bucket_policy" "bucket_policy" {
bucket = "${aws_s3_bucket.alb_logs.bucket}"
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "MYBUCKETPOLICY",
"Statement": [
{
"Sid": "AllowAccessLogs",
"Effect": "Allow",
"Principal": {
"AWS": "929392832123"
},
"Action": "s3:PutObject",
"Resource": [
"${aws_s3_bucket.alb_logs.arn}",
"${aws_s3_bucket.alb_logs.arn}/*"
]
}
]
}
POLICY
}

resource "aws_security_group" "ec2_sg" {
vpc_id = "${aws_vpc.website.id}"
name = "instance"
ingress {
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = [ "${var.ssh_cidr_block}" ]
}
ingress {
protocol = "tcp"
from_port = 80
to_port = 80
security_groups = [ "${aws_security_group.alb_sg.id}" ]
}
egress {
protocol = "tcp"
from_port = 0
to_port = 65535
cidr_blocks = [ "0.0.0.0/0" ]
}
}

data "template_file" "user_data" {
template = "${file("user_data.tpl")}"
vars {
title = "${var.title}"
}
}

resource "aws_launch_configuration" "webserver" {
image_id = "${var.ami}"
instance_type = "t2.micro"
lifecycle {
create_before_destroy = true
}
key_name = "${var.key_name}"
associate_public_ip_address = true
security_groups = [ "${aws_security_group.ec2_sg.id}" ]
user_data = "${data.template_file.user_data.rendered}"
}

resource "aws_autoscaling_group" "asg" {
name = "terraform-test"
min_size = 1
max_size = 2
health_check_grace_period = 300
health_check_type = "ELB"
desired_capacity = 2
launch_configuration = "${aws_launch_configuration.webserver.name}"
vpc_zone_identifier = [ "${aws_subnet.public1.id}", "${aws_subnet.public2.id}" ]
target_group_arns = [ "${aws_alb_target_group.tg.arn}" ]
tag {
key = "Name"
value = "${var.name_tag}"
propagate_at_launch = true
}
}

resource "aws_alb_target_group" "tg" {
name = "webserver-target"
port = 80
protocol = "HTTP"
vpc_id = "${aws_vpc.website.id}"
}

resource "aws_alb_listener" "l" {
load_balancer_arn = "${aws_alb.alb.id}"
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = "${aws_alb_target_group.tg.arn}"
}
}

17 changes: 17 additions & 0 deletions cli/testdata/builtin/terraform/ami.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
resource "aws_ami" "my-ami" {
name = "nginx-example"
virtualization_type = "hvm"
root_device_name = "/dev/xvda"
ebs_block_device {
device_name = "/dev/xvda"
snapshot_id = "${var.snapshot_id}"
volume_size = 8
}
}

resource "aws_ami_copy" "my-ami-copy" {
name = "nginx-example-copy"
description = "A copy of nginx-example"
source_ami_id = "${aws_ami.my-ami.id}"
source_ami_region = "us-east-1"
}
75 changes: 75 additions & 0 deletions cli/testdata/builtin/terraform/cloudfront.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
resource "aws_s3_bucket" "content" {}

resource "aws_cloudfront_origin_access_identity" "example" {
comment = "Testing"
}

resource "aws_s3_bucket_policy" "policy" {
bucket = "${aws_s3_bucket.content.bucket}"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": " CloudFront Origin Identity",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${aws_cloudfront_origin_access_identity.example.id}"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${aws_s3_bucket.content.bucket}/*"
}
]
}
POLICY
}

resource "aws_s3_bucket_object" "index" {
bucket = "${aws_s3_bucket.content.bucket}"
key = "index.html"
content = "Hello, world!"
content_type = "text/html"
}

variable "origin_id" {
default = "website_origin"
}

resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = "${aws_s3_bucket.content.bucket_regional_domain_name}"
origin_id = "${var.origin_id}"
s3_origin_config {
origin_access_identity = "origin-access-identity/cloudfront/${aws_cloudfront_origin_access_identity.example.id}"
}
}
enabled = true
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "${var.origin_id}"

forwarded_values {
query_string = false

cookies {
forward = "none"
}
}

viewer_protocol_policy = "allow-all"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
restrictions {
geo_restriction {
restriction_type = "whitelist"
locations = ["US"]
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
Loading

0 comments on commit c51d8f2

Please sign in to comment.