cfn-nag 0.0.44 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cfn_nag +27 -11
- data/bin/cfn_nag_rules +5 -4
- data/bin/cfn_nag_scan +29 -0
- data/lib/cfn-nag.rb +3 -0
- data/lib/cfn-nag/cfn_nag.rb +115 -0
- data/lib/cfn-nag/custom_rule_loader.rb +72 -0
- data/lib/cfn-nag/custom_rules/CloudFormationAuthenticationRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/CloudFrontDistributionAccessLoggingRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/EbsVolumeHasSseRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/ElasticLoadBalancerAccessLoggingRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyNotResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyNotResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyWildcardActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyWildcardResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotActionOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotActionOnTrustPolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotPrincipalOnTrustPolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotResourceOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnTrustPolicyRule.rb +27 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardResourceOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/LambdaPermissionInvokeFunctionActionRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/LambdaPermissionWildcardPrincipalRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/ManagedPolicyOnUserRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/PolicyOnUserRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyNotPrincipalRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardActionRule.rb +30 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/S3BucketPublicReadAclRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/S3BucketPublicReadWriteAclRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupEgressOpenToWorldRule.rb +39 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupEgressPortRangeRule.rb +38 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressCidrNon32Rule.rb +40 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressOpenToWorldRule.rb +39 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressPortRangeRule.rb +38 -0
- data/lib/{custom_rules/security_group_missing_egress.rb → cfn-nag/custom_rules/SecurityGroupMissingEgressRule.rb} +6 -12
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotPrincipalRule.rb +26 -0
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotPrincipalRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardActionRule.rb +30 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/UserHasInlinePolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/UserMissingGroupRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/WafWebAclDefaultActionRule.rb +34 -0
- data/lib/cfn-nag/custom_rules/base.rb +28 -0
- data/lib/cfn-nag/custom_rules/unencrypted_s3_put_allowed.rb +58 -0
- data/lib/{profile.rb → cfn-nag/profile.rb} +0 -1
- data/lib/{profile_loader.rb → cfn-nag/profile_loader.rb} +2 -2
- data/lib/{result_view → cfn-nag/result_view}/json_results.rb +0 -0
- data/lib/{result_view → cfn-nag/result_view}/rules_view.rb +0 -0
- data/lib/{result_view → cfn-nag/result_view}/simple_stdout_results.rb +5 -12
- data/lib/cfn-nag/rule_definition.rb +36 -0
- data/lib/cfn-nag/rule_dumper.rb +23 -0
- data/lib/cfn-nag/rule_registry.rb +43 -0
- data/lib/cfn-nag/template_discovery.rb +24 -0
- data/lib/cfn-nag/violation.rb +58 -0
- metadata +79 -36
- data/lib/cfn_nag.rb +0 -219
- data/lib/custom_rule_loader.rb +0 -64
- data/lib/custom_rules/unencrypted_s3_put_allowed.rb +0 -58
- data/lib/custom_rules/user_missing_group.rb +0 -34
- data/lib/json_rules/basic_rules.rb +0 -49
- data/lib/json_rules/cfn_rules.rb +0 -4
- data/lib/json_rules/cidr_rules.rb +0 -77
- data/lib/json_rules/cloudfront_rules.rb +0 -4
- data/lib/json_rules/ebs_rules.rb +0 -4
- data/lib/json_rules/iam_policy_rules.rb +0 -153
- data/lib/json_rules/iam_user_rules.rb +0 -15
- data/lib/json_rules/lambda_rules.rb +0 -9
- data/lib/json_rules/loadbalancer_rules.rb +0 -9
- data/lib/json_rules/port_rules.rb +0 -33
- data/lib/json_rules/s3_bucket_rules.rb +0 -51
- data/lib/json_rules/sns_rules.rb +0 -29
- data/lib/json_rules/sqs_rules.rb +0 -25
- data/lib/model/action_parser.rb +0 -27
- data/lib/model/cfn_model.rb +0 -182
- data/lib/model/iam_user_parser.rb +0 -34
- data/lib/model/parser_registry.rb +0 -31
- data/lib/model/s3_bucket_policy.rb +0 -25
- data/lib/model/s3_bucket_policy_parser.rb +0 -28
- data/lib/model/security_group_parser.rb +0 -59
- data/lib/rule.rb +0 -208
- data/lib/rule_registry.rb +0 -45
- data/lib/violation.rb +0 -41
@@ -1,15 +0,0 @@
|
|
1
|
-
violation id: 'F10',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::IAM::User")|'\
|
3
|
-
'select(.Properties.Policies|length > 0)]|map(.LogicalResourceId) ',
|
4
|
-
message: 'IAM user should not have any directly attached policies. Should be on group'
|
5
|
-
|
6
|
-
violation id: 'F11',
|
7
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::IAM::Policy")|'\
|
8
|
-
'select(.Properties.Users|length > 0)]|map(.LogicalResourceId) ',
|
9
|
-
message: 'IAM policy should not apply directly to users. Should be on group'
|
10
|
-
|
11
|
-
violation id: 'F12',
|
12
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::IAM::ManagedPolicy")|'\
|
13
|
-
'select(.Properties.Users|length > 0)]|map(.LogicalResourceId) ',
|
14
|
-
message: 'IAM managed policy should not apply directly to users. Should be on group'
|
15
|
-
|
@@ -1,9 +0,0 @@
|
|
1
|
-
warning id: 'W24',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::Lambda::Permission")|'\
|
3
|
-
'select(.Properties.Action != "lambda:InvokeFunction")]|map(.LogicalResourceId) ',
|
4
|
-
message: 'Lambda permission beside InvokeFunction might not be what you want? Not sure!?'
|
5
|
-
|
6
|
-
violation id: 'F13',
|
7
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::Lambda::Permission")|'\
|
8
|
-
'select(.Properties.Principal == "*")]|map(.LogicalResourceId) ',
|
9
|
-
message: 'Lambda permission principal should not be wildcard'
|
@@ -1,9 +0,0 @@
|
|
1
|
-
warning id: 'W25',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::ElasticLoadBalancing::LoadBalancer")|'\
|
3
|
-
'select(.Properties.AccessLoggingPolicy == null)]|map(.LogicalResourceId) ',
|
4
|
-
message: 'Elastic Load Balancer should have access logging configured'
|
5
|
-
|
6
|
-
warning id: 'W26',
|
7
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::ElasticLoadBalancing::LoadBalancer")|'\
|
8
|
-
'select(.Properties.AccessLoggingPolicy?.Enabled == false)]|map(.LogicalResourceId) ',
|
9
|
-
message: 'Elastic Load Balancer should have access logging enabled'
|
@@ -1,33 +0,0 @@
|
|
1
|
-
warning id: 'W27',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | '\
|
3
|
-
'select(.Type == "AWS::EC2::SecurityGroup")| '\
|
4
|
-
'if (.Properties.SecurityGroupIngress|type == "object") '\
|
5
|
-
'then select(.Properties.SecurityGroupIngress.ToPort != .Properties.SecurityGroupIngress.FromPort) '\
|
6
|
-
'else if (.Properties.SecurityGroupIngress|type == "array") '\
|
7
|
-
' then select([.Properties.SecurityGroupIngress[].ToPort] != [.Properties.SecurityGroupIngress[].FromPort]) '\
|
8
|
-
' else empty '\
|
9
|
-
' end '\
|
10
|
-
'end '\
|
11
|
-
']|map(.LogicalResourceId)',
|
12
|
-
message: 'Security Groups found ingress with port range instead of just a single port'
|
13
|
-
|
14
|
-
warning id: 'W28',
|
15
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroupIngress")|select(.Properties.ToPort != .Properties.FromPort)]|map(.LogicalResourceId)',
|
16
|
-
message: 'Security Group ingress with port range instead of just a single port'
|
17
|
-
|
18
|
-
warning id: 'W29',
|
19
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | '\
|
20
|
-
'select(.Type == "AWS::EC2::SecurityGroup")| '\
|
21
|
-
'if (.Properties.SecurityGroupEgress|type == "object") '\
|
22
|
-
'then select(.Properties.SecurityGroupEgress.ToPort != .Properties.SecurityGroupEgress.FromPort) '\
|
23
|
-
'else if (.Properties.SecurityGroupEgress|type == "array") '\
|
24
|
-
' then select([.Properties.SecurityGroupEgress[].ToPort] != [.Properties.SecurityGroupEgress[].FromPort]) '\
|
25
|
-
' else empty '\
|
26
|
-
' end '\
|
27
|
-
'end'\
|
28
|
-
']|map(.LogicalResourceId)',
|
29
|
-
message: 'Security Groups found egress with port range instead of just a single port'
|
30
|
-
|
31
|
-
warning id: 'W30',
|
32
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroupEgress")|select(.Properties.ToPort != .Properties.FromPort)]|map(.LogicalResourceId)',
|
33
|
-
message: 'Security Group egress with port range instead of just a single port'
|
@@ -1,51 +0,0 @@
|
|
1
|
-
warning id: 'W31',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::S3::Bucket")|'\
|
3
|
-
'select(.Properties.AccessControl? == "PublicRead")]|map(.LogicalResourceId) ',
|
4
|
-
message: 'S3 Bucket likely should not have a public read acl'
|
5
|
-
|
6
|
-
violation id: 'F14',
|
7
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::S3::Bucket")|'\
|
8
|
-
'select(.Properties.AccessControl? == "PublicReadWrite")]|map(.LogicalResourceId) ',
|
9
|
-
message: 'S3 Bucket should not have a public read-write acl'
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
s3_wildcard_action_filter = <<END
|
14
|
-
def s3_wildcard_action:
|
15
|
-
if .Statement|type == "object"
|
16
|
-
then select(.Statement.Effect == "Allow" and (if .Statement.Action|type=="string" then (.Statement.Action == "s3:*" or .Statement.Action == "*") else ((.Statement.Action|indices("s3:*")|length > 0) or (.Statement.Action|indices("*")|length > 0)) end))
|
17
|
-
else select(.Statement[]|.Effect == "Allow" and (if .Action|type=="string" then (.Action == "s3:*" or .Action == "*") else ((.Action|indices("s3:*")|length > 0) or (.Action|indices("*")|length > 0)) end))
|
18
|
-
end;
|
19
|
-
END
|
20
|
-
|
21
|
-
s3_wildcard_principal_filter = <<END
|
22
|
-
def s3_wildcard_principal:
|
23
|
-
if .Statement|type == "object"
|
24
|
-
then select(.Statement.Effect == "Allow" and (.Statement.Principal?|type=="string") and (.Statement.Principal == "*") )
|
25
|
-
else select(.Statement[]|.Effect == "Allow" and ((.Principal?|type=="string") and (.Principal == "*")) )
|
26
|
-
end;
|
27
|
-
END
|
28
|
-
|
29
|
-
s3_wildcard_aws_principal_filter = <<END
|
30
|
-
def s3_wildcard_aws_principal:
|
31
|
-
if .Statement|type == "object"
|
32
|
-
then select(.Statement.Effect == "Allow" and (.Statement.Principal?|type=="object") and (.Statement.Principal.AWS == "*"))
|
33
|
-
else select(.Statement[]|(.Effect == "Allow" and (.Principal?|type=="object") and (.Principal.AWS == "*")))
|
34
|
-
end;
|
35
|
-
END
|
36
|
-
|
37
|
-
|
38
|
-
violation id: 'F15',
|
39
|
-
jq: s3_wildcard_action_filter +
|
40
|
-
"[#{resources_by_type('AWS::S3::BucketPolicy')}|select(.Properties.PolicyDocument|s3_wildcard_action)]|map(.LogicalResourceId) ",
|
41
|
-
message: 'S3 Bucket policy should not allow * action'
|
42
|
-
|
43
|
-
violation id: 'F16',
|
44
|
-
jq: s3_wildcard_principal_filter +
|
45
|
-
"[#{resources_by_type('AWS::S3::BucketPolicy')}|select(.Properties.PolicyDocument|s3_wildcard_principal)]|map(.LogicalResourceId) ",
|
46
|
-
message: 'S3 Bucket policy should not allow * principal'
|
47
|
-
|
48
|
-
violation id: 'F17',
|
49
|
-
jq: s3_wildcard_aws_principal_filter +
|
50
|
-
"[#{resources_by_type('AWS::S3::BucketPolicy')}|select(.Properties.PolicyDocument|s3_wildcard_aws_principal)]|map(.LogicalResourceId) ",
|
51
|
-
message: 'S3 Bucket policy should not allow * AWS principal'
|
data/lib/json_rules/sns_rules.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
sns_wildcard_principal_filter = <<END
|
2
|
-
def sns_wildcard_principal:
|
3
|
-
if .Statement|type == "object"
|
4
|
-
then select(.Statement.Effect == "Allow" and (.Statement.Principal?|type=="string") and (.Statement.Principal == "*"))
|
5
|
-
else select(.Statement[]|(.Effect == "Allow" and (.Principal?|type=="string") and (.Principal == "*")))
|
6
|
-
end;
|
7
|
-
END
|
8
|
-
|
9
|
-
sns_wildcard_aws_principal_filter = <<END
|
10
|
-
def sns_wildcard_aws_principal:
|
11
|
-
if .Statement|type == "object"
|
12
|
-
then select(.Statement.Effect == "Allow" and (.Statement.Principal?|type=="object") and (.Statement.Principal.AWS == "*"))
|
13
|
-
else select(.Statement[]|(.Effect == "Allow" and (.Principal?|type=="object") and (.Principal.AWS == "*")))
|
14
|
-
end;
|
15
|
-
END
|
16
|
-
|
17
|
-
# i guess we could have principal "AWS": ["1111111111", "*", "222222222222"]... or ["*","arn:..."]
|
18
|
-
|
19
|
-
#sns action wildcard doesnt seem to be accepted by sns so dont sweat it
|
20
|
-
|
21
|
-
violation id: 'F18',
|
22
|
-
jq: sns_wildcard_principal_filter +
|
23
|
-
"[#{resources_by_type('AWS::SNS::TopicPolicy')}|select(.Properties.PolicyDocument|sns_wildcard_principal)]|map(.LogicalResourceId) ",
|
24
|
-
message: 'SNS topic policy should not allow * principal'
|
25
|
-
|
26
|
-
violation id: 'F19',
|
27
|
-
jq: sns_wildcard_aws_principal_filter +
|
28
|
-
"[#{resources_by_type('AWS::SNS::TopicPolicy')}|select(.Properties.PolicyDocument|sns_wildcard_aws_principal)]|map(.LogicalResourceId) ",
|
29
|
-
message: 'SNS topic policy should not allow * AWS principal'
|
data/lib/json_rules/sqs_rules.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
sqs_wildcard_action_filter = <<END
|
2
|
-
def sqs_wildcard_action:
|
3
|
-
if .Statement|type == "object"
|
4
|
-
then select(.Statement.Effect == "Allow" and .Statement.Action and (if .Statement.Action|type=="string" then (.Statement.Action | contains("*") ) else (.Statement.Action|contains(["*"])) end))
|
5
|
-
else select(.Statement[]|.Effect == "Allow" and .Action and (if .Action|type=="string" then (.Action | contains("*")) else (.Action|contains(["*"])) end))
|
6
|
-
end;
|
7
|
-
END
|
8
|
-
|
9
|
-
sqs_wildcard_principal_filter = <<END
|
10
|
-
def sqs_wildcard_principal:
|
11
|
-
if .Statement|type == "object"
|
12
|
-
then select(.Statement.Effect == "Allow" and (((.Statement.Principal?|type=="string") and (.Statement.Principal|contains("*"))) or ((.Statement.Principal?|type=="object") and (.Statement.Principal|contains({"AWS": "*"}))) ))
|
13
|
-
else select(.Statement[]|.Effect == "Allow" and ( ((.Principal?|type=="string") and (.Principal|contains("*"))) or ((.Principal?|type=="object") and (.Principal|contains({"AWS": "*"}))) ) )
|
14
|
-
end;
|
15
|
-
END
|
16
|
-
|
17
|
-
violation id: 'F20',
|
18
|
-
jq: sqs_wildcard_action_filter +
|
19
|
-
"[#{resources_by_type('AWS::SQS::QueuePolicy')}|select(.Properties.PolicyDocument|sqs_wildcard_action)]|map(.LogicalResourceId) ",
|
20
|
-
message: 'SQS Queue policy should not allow * action'
|
21
|
-
|
22
|
-
violation id: 'F21',
|
23
|
-
jq: sqs_wildcard_principal_filter +
|
24
|
-
"[#{resources_by_type('AWS::SQS::QueuePolicy')}|select(.Properties.PolicyDocument|sqs_wildcard_principal)]|map(.LogicalResourceId) ",
|
25
|
-
message: 'SQS Queue policy should not allow * principal'
|
data/lib/model/action_parser.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
class ActionParser
|
4
|
-
|
5
|
-
def include?(actual_action:, action_to_look_for:)
|
6
|
-
if actual_action.is_a? Array
|
7
|
-
matches = actual_action.find do |single_action|
|
8
|
-
match_potentially_wildcard_action(actual_action: single_action,
|
9
|
-
action_to_look_for: action_to_look_for)
|
10
|
-
end
|
11
|
-
not matches.nil?
|
12
|
-
elsif actual_action.is_a? String
|
13
|
-
match_potentially_wildcard_action(actual_action: actual_action,
|
14
|
-
action_to_look_for: action_to_look_for)
|
15
|
-
else
|
16
|
-
fail "actual_action needs to be a String or Array (of String),not : #{actual_action.class}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def match_potentially_wildcard_action(actual_action:, action_to_look_for:)
|
23
|
-
actual_action_regex = actual_action.gsub /\*/, '.*'
|
24
|
-
match_position = Regexp.new(actual_action_regex) =~ action_to_look_for
|
25
|
-
not match_position.nil?
|
26
|
-
end
|
27
|
-
end
|
data/lib/model/cfn_model.rb
DELETED
@@ -1,182 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require_relative 'parser_registry'
|
3
|
-
|
4
|
-
# consider a canonical form for template too...
|
5
|
-
# always transform optional things into more general forms....
|
6
|
-
# although referencing violations becomes tricky then (a la c preprocessor)
|
7
|
-
|
8
|
-
class CfnModel
|
9
|
-
def initialize
|
10
|
-
@dangling_ingress_or_egress_rules = []
|
11
|
-
@dangler = Object.new
|
12
|
-
end
|
13
|
-
|
14
|
-
def parse(cfn_json_string)
|
15
|
-
@json_hash = JSON.load cfn_json_string
|
16
|
-
self
|
17
|
-
end
|
18
|
-
|
19
|
-
def parameters
|
20
|
-
@json_hash['Parameters']
|
21
|
-
end
|
22
|
-
|
23
|
-
def security_groups
|
24
|
-
fail 'must call parse first' unless @json_hash
|
25
|
-
security_groups_hash = resources_by_type('AWS::EC2::SecurityGroup')
|
26
|
-
wire_ingress_rules_to_security_groups(security_groups_hash)
|
27
|
-
wire_egress_rules_to_security_groups(security_groups_hash)
|
28
|
-
security_groups_hash.values
|
29
|
-
end
|
30
|
-
|
31
|
-
def dangling_ingress_or_egress_rules
|
32
|
-
fail 'must call parse first' unless @json_hash
|
33
|
-
@dangling_ingress_or_egress_rules
|
34
|
-
end
|
35
|
-
|
36
|
-
def iam_users
|
37
|
-
fail 'must call parse first' unless @json_hash
|
38
|
-
iam_users_hash = resources_by_type('AWS::IAM::User')
|
39
|
-
wire_user_to_group_additions_to_users(iam_users_hash)
|
40
|
-
iam_users_hash.values
|
41
|
-
end
|
42
|
-
|
43
|
-
def bucket_policies
|
44
|
-
bucket_policy_hash = resources_by_type('AWS::S3::BucketPolicy')
|
45
|
-
bucket_policy_hash.values
|
46
|
-
end
|
47
|
-
|
48
|
-
def resources_by_type(resource_type)
|
49
|
-
resources_map = {}
|
50
|
-
resources.each do |resource_name, resource|
|
51
|
-
if resource['Type'] == resource_type
|
52
|
-
resource_parser = ParserRegistry.instance.registry[resource_type].new
|
53
|
-
resources_map[resource_name] = resource_parser.parse(resource_name, resource)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
resources_map
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def resolve_user_logical_resource_id(user)
|
62
|
-
if not user['Ref'].nil?
|
63
|
-
user['Ref']
|
64
|
-
elsif not user['Fn::GetAtt'].nil?
|
65
|
-
fail 'Arn not legal for user to group addition'
|
66
|
-
else
|
67
|
-
@dangler
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def resolve_group_id(group_id)
|
72
|
-
if not group_id['Ref'].nil?
|
73
|
-
group_id['Ref']
|
74
|
-
elsif not group_id['Fn::GetAtt'].nil?
|
75
|
-
fail 'GroupId only legal att on security group resource' unless group_id['Fn::GetAtt'][1] == 'GroupId'
|
76
|
-
group_id['Fn::GetAtt'][0]
|
77
|
-
else
|
78
|
-
@dangling_ingress_or_egress_rules << group_id
|
79
|
-
@dangler
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def wire_user_to_group_additions_to_users(iam_users_hash)
|
84
|
-
resources_by_type('AWS::IAM::UserToGroupAddition').each do |resource_name, user_to_group_addition|
|
85
|
-
user_to_group_addition['Users'].each do |user|
|
86
|
-
unless resolve_user_logical_resource_id(user) == @dangler
|
87
|
-
iam_users_hash[resolve_user_logical_resource_id(user)].add_group user_to_group_addition['GroupName']
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
iam_users_hash
|
92
|
-
end
|
93
|
-
|
94
|
-
def wire_ingress_rules_to_security_groups(security_groups_hash)
|
95
|
-
resources_by_type('AWS::EC2::SecurityGroupIngress').each do |resource_name, ingress_rule|
|
96
|
-
if not ingress_rule['GroupId'].nil?
|
97
|
-
group_id = resolve_group_id(ingress_rule['GroupId'])
|
98
|
-
|
99
|
-
unless group_id == @dangler
|
100
|
-
security_groups_hash[group_id].add_ingress_rule ingress_rule
|
101
|
-
end
|
102
|
-
else
|
103
|
-
fail "GroupId must be set: #{ingress_rule}"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
security_groups_hash
|
107
|
-
end
|
108
|
-
|
109
|
-
def wire_egress_rules_to_security_groups(security_groups_hash)
|
110
|
-
resources_by_type('AWS::EC2::SecurityGroupEgress').each do |resource_name, egress_rule|
|
111
|
-
if not egress_rule['GroupId'].nil?
|
112
|
-
group_id = resolve_group_id(egress_rule['GroupId'])
|
113
|
-
|
114
|
-
unless group_id == @dangler
|
115
|
-
security_groups_hash[group_id].add_egress_rule egress_rule
|
116
|
-
end
|
117
|
-
else
|
118
|
-
fail "GroupId must be set: #{egress_rule}"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
security_groups_hash
|
122
|
-
end
|
123
|
-
|
124
|
-
def resources
|
125
|
-
@json_hash['Resources']
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
class SecurityGroup
|
132
|
-
attr_accessor :group_description, :vpc_id, :logical_resource_id
|
133
|
-
attr_reader :ingress_rules, :egress_rules
|
134
|
-
|
135
|
-
def initialize
|
136
|
-
@ingress_rules = []
|
137
|
-
@egress_rules = []
|
138
|
-
end
|
139
|
-
|
140
|
-
def add_ingress_rule(ingress_rule)
|
141
|
-
@ingress_rules << ingress_rule
|
142
|
-
end
|
143
|
-
|
144
|
-
def add_egress_rule(egress_rule)
|
145
|
-
@egress_rules << egress_rule
|
146
|
-
end
|
147
|
-
|
148
|
-
def to_s
|
149
|
-
<<-END
|
150
|
-
{
|
151
|
-
logical_resource_id: #{@logical_resource_id}
|
152
|
-
group_description: #{@group_description}
|
153
|
-
vpc_id: #{@vpc_id}
|
154
|
-
ingress_rules: #{@ingress_rules}
|
155
|
-
egress_rules: #{@egress_rules}
|
156
|
-
}
|
157
|
-
END
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
class IamUser
|
162
|
-
attr_accessor :logical_resource_id
|
163
|
-
attr_reader :groups
|
164
|
-
|
165
|
-
def initialize
|
166
|
-
@groups = []
|
167
|
-
end
|
168
|
-
|
169
|
-
def add_group(group)
|
170
|
-
@groups << group
|
171
|
-
end
|
172
|
-
|
173
|
-
def to_s
|
174
|
-
<<-END
|
175
|
-
{
|
176
|
-
logical_resource_id: #{@logical_resource_id}
|
177
|
-
groups: #{@groups}
|
178
|
-
}
|
179
|
-
END
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require_relative 'cfn_model'
|
2
|
-
|
3
|
-
|
4
|
-
class IamUserParser
|
5
|
-
|
6
|
-
def parse(resource_name, resource_json)
|
7
|
-
properties = resource_json['Properties']
|
8
|
-
iam_user = IamUser.new
|
9
|
-
|
10
|
-
iam_user.logical_resource_id = resource_name
|
11
|
-
|
12
|
-
unless properties.nil?
|
13
|
-
unless properties['Groups'].nil?
|
14
|
-
properties['Groups'].each do |group|
|
15
|
-
iam_user.add_group group
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
iam_user
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class IamUserToGroupAdditionParser
|
25
|
-
|
26
|
-
def parse(resource_name, resource_json)
|
27
|
-
user_to_group_addition = {}
|
28
|
-
user_to_group_addition['logical_resource_id'] = resource_name
|
29
|
-
resource_json['Properties'].each_pair do |key, value|
|
30
|
-
user_to_group_addition[key] = value
|
31
|
-
end
|
32
|
-
user_to_group_addition
|
33
|
-
end
|
34
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require_relative 'security_group_parser'
|
2
|
-
require_relative 'iam_user_parser'
|
3
|
-
require_relative 's3_bucket_policy_parser'
|
4
|
-
require_relative 'parser_registry'
|
5
|
-
|
6
|
-
class ParserRegistry
|
7
|
-
attr_reader :registry
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@registry = {
|
11
|
-
'AWS::EC2::SecurityGroup' => SecurityGroupParser,
|
12
|
-
'AWS::EC2::SecurityGroupIngress' => SecurityGroupXgressParser,
|
13
|
-
'AWS::EC2::SecurityGroupEgress' => SecurityGroupXgressParser,
|
14
|
-
'AWS::IAM::User' => IamUserParser,
|
15
|
-
'AWS::IAM::UserToGroupAddition' => IamUserToGroupAdditionParser,
|
16
|
-
'AWS::S3::BucketPolicy' => S3BucketPolicyParser
|
17
|
-
}
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.instance
|
21
|
-
if @instance.nil?
|
22
|
-
@instance = ParserRegistry.new
|
23
|
-
end
|
24
|
-
@instance
|
25
|
-
end
|
26
|
-
|
27
|
-
def add_parser(resource_name, parser)
|
28
|
-
@registry[resource_name] = parser
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|