Skip to content

Commit

Permalink
add SecurityGroupLinter
Browse files Browse the repository at this point in the history
  • Loading branch information
lhitchon committed Mar 15, 2018
1 parent f641611 commit 9cfc227
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 0 deletions.
13 changes: 13 additions & 0 deletions assertion/util.go
Expand Up @@ -46,3 +46,16 @@ func ShouldIncludeFile(patterns []string, filename string) bool {
}
return false
}

func FilterResourcesByType(resources []Resource, resourceType string) []Resource {
if resourceType == "*" {
return resources
}
filtered := make([]Resource, 0)
for _, resource := range resources {
if resource.Type == resourceType {
filtered = append(filtered, resource)
}
}
return filtered
}
2 changes: 2 additions & 0 deletions cli/app.go
Expand Up @@ -59,6 +59,8 @@ func makeLinter(linterType string, log assertion.LoggingFunction) Linter {
return KubernetesLinter{Log: log}
case "Terraform":
return TerraformLinter{Log: log}
case "SecurityGroup":
return SecurityGroupLinter{Log: log}
default:
fmt.Printf("Type not supported: %s\n", linterType)
return nil
Expand Down
93 changes: 93 additions & 0 deletions cli/security_group.go
@@ -0,0 +1,93 @@
package main

import (
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/lhitchon/config-lint/assertion"
)

type SecurityGroupLinter struct {
Log assertion.LoggingFunction
}

func loadSecurityGroupResources(log assertion.LoggingFunction) []assertion.Resource {
region := &aws.Config{Region: aws.String("us-east-1")}
awsSession := session.New()
ec2Client := ec2.New(awsSession, region)
response, err := ec2Client.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{})
if err != nil {
panic(err)
}
resources := make([]assertion.Resource, 0)
for _, securityGroup := range response.SecurityGroups {

// convert to JSON string
jsonData, err := json.Marshal(securityGroup)
if err != nil {
panic(err)
}

// then convert to an interface{}
// seem to need this for JMESPath to work properly
var data interface{}
err = json.Unmarshal(jsonData, &data)
if err != nil {
panic(err)
}

r := assertion.Resource{
Id: *securityGroup.GroupId,
Type: "AWS::EC2::SecurityGroup",
Properties: data,
}
resources = append(resources, r)
}
return resources
}

func (l SecurityGroupLinter) ValidateSecurityGroupResources(report *assertion.ValidationReport, resources []assertion.Resource, rules []assertion.Rule, tags []string) {

valueSource := assertion.StandardValueSource{Log: l.Log}
filteredRules := assertion.FilterRulesByTag(rules, tags)
resolvedRules := assertion.ResolveRules(filteredRules, valueSource, l.Log)

for _, rule := range resolvedRules {
l.Log(fmt.Sprintf("Rule %s: %s", rule.Id, rule.Message))
for _, resource := range assertion.FilterResourcesByType(resources, rule.Resource) {
if assertion.ExcludeResource(rule, resource) {
l.Log(fmt.Sprintf("Ignoring resource %s", resource.Id))
} else {
_, violations := assertion.CheckRule(rule, resource, l.Log)
for _, violation := range violations {
report.Violations[violation.Status] = append(report.Violations[violation.Status], violation)
}
}
}
}
}

func (l SecurityGroupLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIds []string) assertion.ValidationReport {
report := assertion.ValidationReport{
Violations: make(map[string]([]assertion.Violation), 0),
FilesScanned: make([]string, 0),
}
rules := assertion.FilterRulesById(ruleSet.Rules, ruleIds)
resources := loadSecurityGroupResources(l.Log)
l.ValidateSecurityGroupResources(&report, resources, rules, tags)
return report
}

func (l SecurityGroupLinter) Search(filenames []string, searchExpression string) {
resources := loadSecurityGroupResources(l.Log)
for _, resource := range resources {
v, err := assertion.SearchData(searchExpression, resource.Properties)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s: %s\n", resource.Id, v)
}
}
}
14 changes: 14 additions & 0 deletions example-files/rules/security_groups.yml
@@ -0,0 +1,14 @@
Version: 1
Description: Rules for Security Groups
Type: SecurityGroup
Rules:
- id: SG1
message: Should not allow access to SSH from anywhere
resource: "AWS::EC2::SecurityGroup"
assertions:
- not:
- type: value
key: IpPermissions[?(FromPort==`22`)].IpRanges[].CidrIp | [0]
op: eq
value: "0.0.0.0/0"
severity: NON_COMPLIANT

0 comments on commit 9cfc227

Please sign in to comment.