cumulus-aws 0.11.1
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 +15 -0
- data/.gitignore +3 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +202 -0
- data/README.md +41 -0
- data/autocomplete +137 -0
- data/bin/cumulus +658 -0
- data/cumulus +2 -0
- data/cumulus-aws.gemspec +20 -0
- data/lib/autoscaling/AutoScaling.rb +40 -0
- data/lib/autoscaling/loader/Loader.rb +56 -0
- data/lib/autoscaling/manager/Manager.rb +360 -0
- data/lib/autoscaling/models/AlarmConfig.rb +165 -0
- data/lib/autoscaling/models/AlarmDiff.rb +172 -0
- data/lib/autoscaling/models/AutoScalingDiff.rb +178 -0
- data/lib/autoscaling/models/GroupConfig.rb +330 -0
- data/lib/autoscaling/models/PolicyConfig.rb +135 -0
- data/lib/autoscaling/models/PolicyDiff.rb +73 -0
- data/lib/autoscaling/models/ScheduledActionDiff.rb +53 -0
- data/lib/autoscaling/models/ScheduledConfig.rb +96 -0
- data/lib/aws_extensions/ec2/DhcpOptions.rb +41 -0
- data/lib/aws_extensions/ec2/Instance.rb +29 -0
- data/lib/aws_extensions/ec2/NetworkAcl.rb +25 -0
- data/lib/aws_extensions/ec2/NetworkInterface.rb +14 -0
- data/lib/aws_extensions/ec2/RouteTable.rb +26 -0
- data/lib/aws_extensions/ec2/SecurityGroup.rb +16 -0
- data/lib/aws_extensions/ec2/Subnet.rb +28 -0
- data/lib/aws_extensions/ec2/Volume.rb +24 -0
- data/lib/aws_extensions/ec2/Vpc.rb +14 -0
- data/lib/aws_extensions/ec2/VpcEndpoint.rb +11 -0
- data/lib/aws_extensions/elb/BackendServerDescription.rb +12 -0
- data/lib/aws_extensions/elb/PolicyDescription.rb +14 -0
- data/lib/aws_extensions/kinesis/StreamDescription.rb +12 -0
- data/lib/aws_extensions/route53/AliasTarget.rb +21 -0
- data/lib/aws_extensions/s3/Bucket.rb +33 -0
- data/lib/aws_extensions/s3/BucketAcl.rb +28 -0
- data/lib/aws_extensions/s3/BucketCors.rb +17 -0
- data/lib/aws_extensions/s3/BucketLifecycle.rb +21 -0
- data/lib/aws_extensions/s3/BucketLogging.rb +18 -0
- data/lib/aws_extensions/s3/BucketNotification.rb +23 -0
- data/lib/aws_extensions/s3/BucketPolicy.rb +18 -0
- data/lib/aws_extensions/s3/BucketTagging.rb +15 -0
- data/lib/aws_extensions/s3/BucketVersioning.rb +14 -0
- data/lib/aws_extensions/s3/BucketWebsite.rb +49 -0
- data/lib/aws_extensions/s3/CORSRule.rb +27 -0
- data/lib/aws_extensions/s3/ReplicationConfiguration.rb +22 -0
- data/lib/cloudfront/CloudFront.rb +83 -0
- data/lib/cloudfront/loader/Loader.rb +31 -0
- data/lib/cloudfront/manager/Manager.rb +183 -0
- data/lib/cloudfront/models/CacheBehaviorConfig.rb +237 -0
- data/lib/cloudfront/models/CacheBehaviorDiff.rb +211 -0
- data/lib/cloudfront/models/CustomOriginConfig.rb +51 -0
- data/lib/cloudfront/models/CustomOriginDiff.rb +74 -0
- data/lib/cloudfront/models/DistributionConfig.rb +183 -0
- data/lib/cloudfront/models/DistributionDiff.rb +131 -0
- data/lib/cloudfront/models/InvalidationConfig.rb +37 -0
- data/lib/cloudfront/models/OriginConfig.rb +144 -0
- data/lib/cloudfront/models/OriginDiff.rb +86 -0
- data/lib/cloudfront/models/OriginSslProtocols.rb +28 -0
- data/lib/cloudfront/models/OriginSslProtocolsDiff.rb +39 -0
- data/lib/common/BaseLoader.rb +80 -0
- data/lib/common/manager/Manager.rb +148 -0
- data/lib/common/models/Diff.rb +114 -0
- data/lib/common/models/ListChange.rb +21 -0
- data/lib/common/models/TagsDiff.rb +55 -0
- data/lib/common/models/UTCTimeSource.rb +17 -0
- data/lib/conf/Configuration.rb +365 -0
- data/lib/ec2/EC2.rb +503 -0
- data/lib/ec2/IPProtocolMapping.rb +165 -0
- data/lib/ec2/loaders/EbsLoader.rb +19 -0
- data/lib/ec2/loaders/InstanceLoader.rb +32 -0
- data/lib/ec2/managers/EbsManager.rb +176 -0
- data/lib/ec2/managers/InstanceManager.rb +509 -0
- data/lib/ec2/models/EbsGroupConfig.rb +133 -0
- data/lib/ec2/models/EbsGroupDiff.rb +48 -0
- data/lib/ec2/models/InstanceConfig.rb +202 -0
- data/lib/ec2/models/InstanceDiff.rb +95 -0
- data/lib/elb/ELB.rb +148 -0
- data/lib/elb/loader/Loader.rb +65 -0
- data/lib/elb/manager/Manager.rb +581 -0
- data/lib/elb/models/AccessLogConfig.rb +82 -0
- data/lib/elb/models/AccessLogDiff.rb +47 -0
- data/lib/elb/models/HealthCheckConfig.rb +91 -0
- data/lib/elb/models/HealthCheckDiff.rb +50 -0
- data/lib/elb/models/ListenerConfig.rb +99 -0
- data/lib/elb/models/ListenerDiff.rb +91 -0
- data/lib/elb/models/LoadBalancerConfig.rb +239 -0
- data/lib/elb/models/LoadBalancerDiff.rb +265 -0
- data/lib/iam/IAM.rb +36 -0
- data/lib/iam/loader/Loader.rb +117 -0
- data/lib/iam/manager/IamGroups.rb +98 -0
- data/lib/iam/manager/IamResource.rb +288 -0
- data/lib/iam/manager/IamRoles.rb +112 -0
- data/lib/iam/manager/IamUsers.rb +54 -0
- data/lib/iam/manager/Manager.rb +29 -0
- data/lib/iam/migration/AssumeRoleUnifier.rb +34 -0
- data/lib/iam/migration/PolicyUnifier.rb +90 -0
- data/lib/iam/models/GroupConfig.rb +40 -0
- data/lib/iam/models/IamDiff.rb +132 -0
- data/lib/iam/models/PolicyConfig.rb +67 -0
- data/lib/iam/models/ResourceWithPolicy.rb +208 -0
- data/lib/iam/models/RoleConfig.rb +53 -0
- data/lib/iam/models/StatementConfig.rb +35 -0
- data/lib/iam/models/UserConfig.rb +21 -0
- data/lib/kinesis/Kinesis.rb +94 -0
- data/lib/kinesis/loader/Loader.rb +19 -0
- data/lib/kinesis/manager/Manager.rb +206 -0
- data/lib/kinesis/models/StreamConfig.rb +75 -0
- data/lib/kinesis/models/StreamDiff.rb +58 -0
- data/lib/lambda/Lambda.rb +41 -0
- data/lib/route53/loader/Loader.rb +32 -0
- data/lib/route53/manager/Manager.rb +241 -0
- data/lib/route53/models/AliasTarget.rb +86 -0
- data/lib/route53/models/RecordConfig.rb +178 -0
- data/lib/route53/models/RecordDiff.rb +140 -0
- data/lib/route53/models/Vpc.rb +24 -0
- data/lib/route53/models/ZoneConfig.rb +156 -0
- data/lib/route53/models/ZoneDiff.rb +118 -0
- data/lib/s3/S3.rb +89 -0
- data/lib/s3/loader/Loader.rb +66 -0
- data/lib/s3/manager/Manager.rb +296 -0
- data/lib/s3/models/BucketConfig.rb +321 -0
- data/lib/s3/models/BucketDiff.rb +167 -0
- data/lib/s3/models/GrantConfig.rb +189 -0
- data/lib/s3/models/GrantDiff.rb +50 -0
- data/lib/s3/models/LifecycleConfig.rb +142 -0
- data/lib/s3/models/LifecycleDiff.rb +46 -0
- data/lib/s3/models/LoggingConfig.rb +81 -0
- data/lib/s3/models/NotificationConfig.rb +157 -0
- data/lib/s3/models/NotificationDiff.rb +62 -0
- data/lib/s3/models/ReplicationConfig.rb +133 -0
- data/lib/s3/models/ReplicationDiff.rb +60 -0
- data/lib/s3/models/WebsiteConfig.rb +107 -0
- data/lib/security/SecurityGroups.rb +39 -0
- data/lib/security/loader/Loader.rb +94 -0
- data/lib/security/manager/Manager.rb +246 -0
- data/lib/security/models/RuleConfig.rb +161 -0
- data/lib/security/models/RuleDiff.rb +72 -0
- data/lib/security/models/RuleMigration.rb +127 -0
- data/lib/security/models/SecurityGroupConfig.rb +172 -0
- data/lib/security/models/SecurityGroupDiff.rb +112 -0
- data/lib/sns/SNS.rb +40 -0
- data/lib/sqs/SQS.rb +62 -0
- data/lib/sqs/loader/Loader.rb +34 -0
- data/lib/sqs/manager/Manager.rb +128 -0
- data/lib/sqs/models/DeadLetterConfig.rb +70 -0
- data/lib/sqs/models/DeadLetterDiff.rb +35 -0
- data/lib/sqs/models/QueueConfig.rb +115 -0
- data/lib/sqs/models/QueueDiff.rb +89 -0
- data/lib/util/Colors.rb +111 -0
- data/lib/util/StatusCodes.rb +51 -0
- data/lib/vpc/loader/Loader.rb +73 -0
- data/lib/vpc/manager/Manager.rb +954 -0
- data/lib/vpc/models/AclEntryConfig.rb +150 -0
- data/lib/vpc/models/AclEntryDiff.rb +54 -0
- data/lib/vpc/models/DhcpConfig.rb +100 -0
- data/lib/vpc/models/DhcpDiff.rb +90 -0
- data/lib/vpc/models/EndpointConfig.rb +76 -0
- data/lib/vpc/models/EndpointDiff.rb +69 -0
- data/lib/vpc/models/NetworkAclConfig.rb +87 -0
- data/lib/vpc/models/NetworkAclDiff.rb +116 -0
- data/lib/vpc/models/RouteConfig.rb +82 -0
- data/lib/vpc/models/RouteDiff.rb +50 -0
- data/lib/vpc/models/RouteTableConfig.rb +92 -0
- data/lib/vpc/models/RouteTableDiff.rb +101 -0
- data/lib/vpc/models/SubnetConfig.rb +113 -0
- data/lib/vpc/models/SubnetDiff.rb +78 -0
- data/lib/vpc/models/VpcConfig.rb +173 -0
- data/lib/vpc/models/VpcDiff.rb +315 -0
- data/rakefile.rb +8 -0
- metadata +245 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "conf/Configuration"
|
|
2
|
+
require "ec2/EC2"
|
|
3
|
+
|
|
4
|
+
require "aws-sdk"
|
|
5
|
+
|
|
6
|
+
module Cumulus
|
|
7
|
+
module SecurityGroups
|
|
8
|
+
class << self
|
|
9
|
+
@@client = Aws::EC2::Client.new(Configuration.instance.client)
|
|
10
|
+
|
|
11
|
+
require "aws_extensions/ec2/SecurityGroup"
|
|
12
|
+
Aws::EC2::Types::SecurityGroup.send(:include, AwsExtensions::EC2::SecurityGroup)
|
|
13
|
+
|
|
14
|
+
def id_security_groups
|
|
15
|
+
@id_security_groups ||= Hash[security_groups.map { |a| [a.group_id, a] }]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Public: Returns a Hash of vpc id to Hash of security group name to group
|
|
19
|
+
def vpc_security_groups
|
|
20
|
+
@vpc_security_groups ||= Hash[security_groups.map(&:vpc_id).uniq.map do |vpc_id|
|
|
21
|
+
[vpc_id, Hash[security_groups.select { |g| g.vpc_id == vpc_id }.map { |g| [g.group_name, g] }]]
|
|
22
|
+
end]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Describe all security groups
|
|
26
|
+
def security_groups
|
|
27
|
+
@security_groups ||= @@client.describe_security_groups.security_groups
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Public: Returns a Hash of vpc id to Hash of security group id to group name
|
|
31
|
+
def vpc_security_group_id_names
|
|
32
|
+
@vpc_security_group_id_names ||= Hash[vpc_security_groups.map do |vpc_id, group_hash|
|
|
33
|
+
[vpc_id, Hash[group_hash.map {|_, sg| [sg.group_id, sg.group_name]}]]
|
|
34
|
+
end]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require "common/BaseLoader"
|
|
2
|
+
require "conf/Configuration"
|
|
3
|
+
require "security/models/SecurityGroupConfig"
|
|
4
|
+
require "util/Colors"
|
|
5
|
+
require "util/StatusCodes"
|
|
6
|
+
|
|
7
|
+
module Cumulus
|
|
8
|
+
module SecurityGroups
|
|
9
|
+
# Public: Load Security Group assets
|
|
10
|
+
module Loader
|
|
11
|
+
include Common::BaseLoader
|
|
12
|
+
|
|
13
|
+
@@groups_dir = Configuration.instance.security.groups_directory
|
|
14
|
+
@@rules_dir = Configuration.instance.security.rules_directory
|
|
15
|
+
@@subnet_files = Configuration.instance.security.subnet_files
|
|
16
|
+
|
|
17
|
+
# Public: Load all the security group configurations as SecurityGroupConfig objects
|
|
18
|
+
#
|
|
19
|
+
# Returns an array of SecurityGroupConfig
|
|
20
|
+
def Loader.groups
|
|
21
|
+
# List all the directories to load groups from each vpc
|
|
22
|
+
vpc_dirs = Dir.entries(@@groups_dir).reject { |f| f == "." or f == ".."}.select { |f| File.directory?(File.join(@@groups_dir, f)) }
|
|
23
|
+
|
|
24
|
+
vpc_groups = vpc_dirs.map do |d|
|
|
25
|
+
aws_vpc = EC2::named_vpcs[d]
|
|
26
|
+
|
|
27
|
+
if aws_vpc.nil?
|
|
28
|
+
puts Colors.red("No VPC named #{d} exists")
|
|
29
|
+
exit StatusCodes::EXCEPTION
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Common::BaseLoader.resources(File.join(@@groups_dir, d)) do |file_name, json|
|
|
33
|
+
name = "#{aws_vpc.name}/#{file_name}"
|
|
34
|
+
SecurityGroupConfig.new(name, aws_vpc.vpc_id, json)
|
|
35
|
+
end
|
|
36
|
+
end.flatten
|
|
37
|
+
|
|
38
|
+
non_vpc_groups = Common::BaseLoader.resources(@@groups_dir) do |file_name, json|
|
|
39
|
+
SecurityGroupConfig.new(file_name, nil, json)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if !EC2::supports_ec2_classic and !non_vpc_groups.empty?
|
|
43
|
+
puts "Ignoring Non-VPC Security Groups because your account does not support them"
|
|
44
|
+
non_vpc_groups = []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
vpc_groups + non_vpc_groups
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Public: Load a single static rule
|
|
51
|
+
#
|
|
52
|
+
# Returns the static rule as json
|
|
53
|
+
def Loader.rule(rule_name)
|
|
54
|
+
Common::BaseLoader.resource(rule_name, @@rules_dir) { |_, json| json }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Public: Get the local definition of a subnet group.
|
|
58
|
+
#
|
|
59
|
+
# name - the name of the subnet group to get
|
|
60
|
+
#
|
|
61
|
+
# Returns an array of ip addresses that is empty if there is no subnet group with that name
|
|
62
|
+
def Loader.subnet_group(name)
|
|
63
|
+
if self.subnet_groups[name].nil?
|
|
64
|
+
raise "Could not find subnet #{name}"
|
|
65
|
+
else
|
|
66
|
+
self.subnet_groups[name]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Internal: Get the subnet group definitions
|
|
73
|
+
#
|
|
74
|
+
# Returns a hash that maps group name to an array of ips
|
|
75
|
+
def Loader.subnet_groups
|
|
76
|
+
@subnet_groups ||= self.load_subnet_groups
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Internal: Load the subnet group definitions
|
|
80
|
+
#
|
|
81
|
+
# Returns a hash that maps group name to an array of ips
|
|
82
|
+
def Loader.load_subnet_groups
|
|
83
|
+
@@subnet_files.reduce({}) do |sofar, f|
|
|
84
|
+
subnet_group = Common::BaseLoader.resource(f, "") { |_, json| json }
|
|
85
|
+
if subnet_group
|
|
86
|
+
subnet_group.merge(sofar)
|
|
87
|
+
else
|
|
88
|
+
sofar
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
require "common/manager/Manager"
|
|
2
|
+
require "conf/Configuration"
|
|
3
|
+
require "security/loader/Loader"
|
|
4
|
+
require "security/models/SecurityGroupConfig"
|
|
5
|
+
require "security/models/SecurityGroupDiff"
|
|
6
|
+
require "security/SecurityGroups"
|
|
7
|
+
require "util/Colors"
|
|
8
|
+
|
|
9
|
+
require "aws-sdk"
|
|
10
|
+
require "json"
|
|
11
|
+
|
|
12
|
+
module Cumulus
|
|
13
|
+
module SecurityGroups
|
|
14
|
+
class Manager < Common::Manager
|
|
15
|
+
def initialize
|
|
16
|
+
super()
|
|
17
|
+
@ec2 = Aws::EC2::Client.new(Configuration.instance.client)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Public: Migrate AWS Security Groups to Cumulus configuration.
|
|
21
|
+
def migrate
|
|
22
|
+
groups_dir = "#{@migration_root}/security-groups"
|
|
23
|
+
|
|
24
|
+
if !Dir.exists?(@migration_root)
|
|
25
|
+
Dir.mkdir(@migration_root)
|
|
26
|
+
end
|
|
27
|
+
if !Dir.exists?(groups_dir)
|
|
28
|
+
Dir.mkdir(groups_dir)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Make the directories needed for resources that require it
|
|
32
|
+
aws_resources.map do |name, _|
|
|
33
|
+
parts = name.partition("/")
|
|
34
|
+
if parts.length > 1
|
|
35
|
+
"#{groups_dir}/#{parts.first}"
|
|
36
|
+
end
|
|
37
|
+
end.uniq.compact.each do |dir|
|
|
38
|
+
if !Dir.exists?(dir)
|
|
39
|
+
Dir.mkdir(dir)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
aws_resources.each_value do |resource|
|
|
44
|
+
puts "Processing #{resource.vpc_group_name}..."
|
|
45
|
+
config = SecurityGroupConfig.new(resource.vpc_group_name, resource.vpc_id)
|
|
46
|
+
config.populate!(resource)
|
|
47
|
+
|
|
48
|
+
puts "Writing #{resource.vpc_group_name} configuration to file..."
|
|
49
|
+
File.open("#{groups_dir}/#{config.name}.json", "w") { |f| f.write(config.pretty_json) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
File.open("#{@migration_root}/subnets.json", "w") do |f|
|
|
53
|
+
f.write(JSON.pretty_generate({
|
|
54
|
+
"all" => ["0.0.0.0/0"]
|
|
55
|
+
}))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
puts Colors.blue("IP addresses for inbound and outbound rules have been left as is in each individual security group, except in the case of 0.0.0.0/0.")
|
|
59
|
+
puts Colors.blue("0.0.0.0/0 has been renamed to 'all' and is referenced as such in security group definitions.")
|
|
60
|
+
puts Colors.blue("See subnets.json to see the definition of the 'all' subnet group.")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def resource_name
|
|
64
|
+
"Security Group"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def local_resources
|
|
68
|
+
@local_resources ||= Hash[Loader.groups.map { |local| [local.name, local] }]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Hash the aws security groups using the vpc and security group name
|
|
72
|
+
def aws_resources
|
|
73
|
+
@aws_resources ||= Hash[SecurityGroups::security_groups.map { |sg| [sg.vpc_group_name, sg] }]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def unmanaged_diff(aws)
|
|
77
|
+
SecurityGroupDiff.unmanaged(aws)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def added_diff(local)
|
|
81
|
+
SecurityGroupDiff.added(local)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def diff_resource(local, aws)
|
|
85
|
+
local.diff(aws)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def create(local)
|
|
89
|
+
result = @ec2.create_security_group({
|
|
90
|
+
group_name: local.name.split("/").last,
|
|
91
|
+
description: local.description,
|
|
92
|
+
vpc_id: local.vpc_id,
|
|
93
|
+
})
|
|
94
|
+
security_group_id = result.group_id
|
|
95
|
+
|
|
96
|
+
update_tags(security_group_id, local.tags, {})
|
|
97
|
+
update_inbound(local.vpc_id, security_group_id, local.inbound, [])
|
|
98
|
+
|
|
99
|
+
allow_all_rule = RuleConfig.allow_all
|
|
100
|
+
allow_all_outbound = Configuration.instance.security.outbound_default_all_allowed or local.outbound.find { |g| g.hash == allow_all_rule.hash }
|
|
101
|
+
|
|
102
|
+
outbound_remove = if allow_all_outbound
|
|
103
|
+
[]
|
|
104
|
+
else
|
|
105
|
+
[allow_all_rule]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
outbound_add = local.outbound.reject { |g| g.hash == allow_all_rule.hash }
|
|
109
|
+
|
|
110
|
+
update_outbound(local.vpc_id, security_group_id, outbound_add, outbound_remove)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def update(local, diffs)
|
|
114
|
+
diffs_by_type = diffs.group_by(&:type)
|
|
115
|
+
|
|
116
|
+
if diffs_by_type.include?(SecurityGroupChange::DESCRIPTION)
|
|
117
|
+
puts "\tUnfortunately, AWS's SDK does not allow updating the description."
|
|
118
|
+
else
|
|
119
|
+
diffs.each do |diff|
|
|
120
|
+
case diff.type
|
|
121
|
+
when SecurityGroupChange::TAGS
|
|
122
|
+
update_tags(diff.aws.group_id, diff.tags_to_add, diff.tags_to_remove)
|
|
123
|
+
when SecurityGroupChange::INBOUND
|
|
124
|
+
update_inbound(local.vpc_id, diff.aws.group_id, diff.added_inbounds, diff.removed_inbounds)
|
|
125
|
+
when SecurityGroupChange::OUTBOUND
|
|
126
|
+
update_outbound(local.vpc_id, diff.aws.group_id, diff.added_outbounds, diff.removed_outbounds)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# Internal: Update the tags associated with a security group.
|
|
135
|
+
#
|
|
136
|
+
# security_group_id - the id of the security group to update
|
|
137
|
+
# add - the tags to add (expects a hash of key value pairs)
|
|
138
|
+
# remove - the tags to remove (expects a hash of key value pairs)
|
|
139
|
+
def update_tags(security_group_id, add, remove)
|
|
140
|
+
if !add.empty?
|
|
141
|
+
puts Colors.blue("\tadding tags...")
|
|
142
|
+
@ec2.create_tags({
|
|
143
|
+
resources: [security_group_id],
|
|
144
|
+
tags: add.map { |k, v| { key: k, value: v } }
|
|
145
|
+
})
|
|
146
|
+
end
|
|
147
|
+
if !remove.empty?
|
|
148
|
+
puts Colors.blue("\tremoving tags...")
|
|
149
|
+
@ec2.delete_tags({
|
|
150
|
+
resources: [security_group_id],
|
|
151
|
+
tags: remove.map { |k, v| { key: k, value: v } }
|
|
152
|
+
})
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Internal: Update the inbound rules associated with a security group.
|
|
157
|
+
#
|
|
158
|
+
# vpc_id - - the id of the vpc the security groups are in
|
|
159
|
+
# security_group_id - the id of the security group
|
|
160
|
+
# add - the inbound rules to associate with the security group
|
|
161
|
+
# remove - the inbound rules to dissociate from the security group
|
|
162
|
+
def update_inbound(vpc_id, security_group_id, add, remove)
|
|
163
|
+
update_rules(
|
|
164
|
+
vpc_id,
|
|
165
|
+
security_group_id,
|
|
166
|
+
add,
|
|
167
|
+
remove,
|
|
168
|
+
{
|
|
169
|
+
:type => "inbound",
|
|
170
|
+
:add_action => @ec2.method(:authorize_security_group_ingress),
|
|
171
|
+
:remove_action => @ec2.method(:revoke_security_group_ingress)
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Internal: Update the outbound rules associated with a security group.
|
|
177
|
+
#
|
|
178
|
+
# vpc_id - - the id of the vpc the security groups are in
|
|
179
|
+
# security_group_id - the id of the security group
|
|
180
|
+
# add - the outbound rules to associate with the security group
|
|
181
|
+
# remove - the outbound rules to associate with the security group
|
|
182
|
+
#
|
|
183
|
+
def update_outbound(vpc_id, security_group_id, add, remove)
|
|
184
|
+
update_rules(
|
|
185
|
+
vpc_id,
|
|
186
|
+
security_group_id,
|
|
187
|
+
add,
|
|
188
|
+
remove,
|
|
189
|
+
{
|
|
190
|
+
:type => "outbound",
|
|
191
|
+
:add_action => @ec2.method(:authorize_security_group_egress),
|
|
192
|
+
:remove_action => @ec2.method(:revoke_security_group_egress)
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Internal: Update rules associated with a security group. Called by inbound
|
|
198
|
+
# and outbound.
|
|
199
|
+
#
|
|
200
|
+
# vpc_id - - the id of the vpc the security groups are in
|
|
201
|
+
# security_group_id - the id of the security group
|
|
202
|
+
# add - the rules to associate with the security group
|
|
203
|
+
# remove - the rules to remove from the security group
|
|
204
|
+
# options - a hash containing options about the operation to run. Should contain:
|
|
205
|
+
# - type - a string representing the type of rules to process
|
|
206
|
+
# - add_action - the client method to call to add rules
|
|
207
|
+
# - remove_action - the client method to call to remove rules
|
|
208
|
+
def update_rules(vpc_id, security_group_id, add, remove, options)
|
|
209
|
+
if !add.empty?
|
|
210
|
+
puts Colors.blue("\tadding #{options[:type]} rules...")
|
|
211
|
+
options[:add_action].call({
|
|
212
|
+
group_id: security_group_id,
|
|
213
|
+
ip_permissions: add.map do |added|
|
|
214
|
+
missing_group = added.security_groups.find do |s|
|
|
215
|
+
!SecurityGroups::vpc_security_groups[vpc_id].include?(s)
|
|
216
|
+
end
|
|
217
|
+
if missing_group
|
|
218
|
+
puts Colors.red("\t\tNo such security group: #{missing_group}. Security group not added.")
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
added.to_aws(vpc_id)
|
|
222
|
+
end
|
|
223
|
+
})
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if !remove.empty?
|
|
227
|
+
puts Colors.blue("\tremoving #{options[:type]} rules...")
|
|
228
|
+
options[:remove_action].call({
|
|
229
|
+
group_id: security_group_id,
|
|
230
|
+
ip_permissions: remove.map do |removed|
|
|
231
|
+
missing_group = removed.security_groups.find do |s|
|
|
232
|
+
!SecurityGroups::vpc_security_groups[vpc_id].include?(s)
|
|
233
|
+
end
|
|
234
|
+
if missing_group
|
|
235
|
+
puts Colors.red("\t\tNo such security group: #{missing_group}. Security group not removed.")
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
removed.to_aws(vpc_id)
|
|
239
|
+
end
|
|
240
|
+
})
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
require "security/loader/Loader"
|
|
2
|
+
require "security/SecurityGroups"
|
|
3
|
+
|
|
4
|
+
module Cumulus
|
|
5
|
+
module SecurityGroups
|
|
6
|
+
# Public: An object representing configuration for a security group rule
|
|
7
|
+
class RuleConfig
|
|
8
|
+
|
|
9
|
+
attr_reader :from
|
|
10
|
+
attr_reader :protocol
|
|
11
|
+
attr_reader :security_groups
|
|
12
|
+
attr_reader :subnets
|
|
13
|
+
attr_reader :to
|
|
14
|
+
|
|
15
|
+
# Public: Static method that will produce a RuleConfig from an AWS rule resource.
|
|
16
|
+
#
|
|
17
|
+
# aws - the aws resource to use
|
|
18
|
+
#
|
|
19
|
+
# Returns a RuleConfig containing the data in the AWS rule
|
|
20
|
+
def RuleConfig.from_aws(aws)
|
|
21
|
+
RuleConfig.new({
|
|
22
|
+
"security-groups" => aws.user_id_group_pairs.map { |security| SecurityGroups::id_security_groups[security.group_id].group_name },
|
|
23
|
+
"protocol" => if aws.ip_protocol == "-1" then "all" else aws.ip_protocol end,
|
|
24
|
+
"from-port" => if aws.ip_protocol != "icmp" and aws.from_port != -1 then aws.from_port end,
|
|
25
|
+
"to-port" => if aws.ip_protocol != "icmp" and aws.to_port != -1 then aws.to_port end,
|
|
26
|
+
"icmp-type" => if aws.ip_protocol == "icmp"
|
|
27
|
+
if aws.from_port != -1 then aws.from_port else "all" end
|
|
28
|
+
end,
|
|
29
|
+
"icmp-code" => if aws.ip_protocol == "icmp"
|
|
30
|
+
if aws.to_port != -1 then aws.to_port else "all" end
|
|
31
|
+
end,
|
|
32
|
+
"subnets" => aws.ip_ranges.map { |ip| ip.cidr_ip },
|
|
33
|
+
}.reject { |k, v| v.nil? })
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Public: Static method that will produce a RuleConfig that allows all access
|
|
37
|
+
#
|
|
38
|
+
# Returns the RuleConfig
|
|
39
|
+
def RuleConfig.allow_all
|
|
40
|
+
RuleConfig.new({
|
|
41
|
+
"protocol" => "all",
|
|
42
|
+
"subnets" => ["0.0.0.0/0"]
|
|
43
|
+
})
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Public: Static method that will produce multiple RuleConfigs, one for each port
|
|
47
|
+
# range.
|
|
48
|
+
#
|
|
49
|
+
# json - a hash containing the JSON configuration for the rule
|
|
50
|
+
#
|
|
51
|
+
# Returns an array of RuleConfigs
|
|
52
|
+
def RuleConfig.expand_ports(json)
|
|
53
|
+
ports = json["ports"]
|
|
54
|
+
|
|
55
|
+
if !ports.nil?
|
|
56
|
+
ports.map do |port|
|
|
57
|
+
rule_hash = json.clone
|
|
58
|
+
|
|
59
|
+
if port.is_a? String
|
|
60
|
+
parts = port.split("-").map(&:strip)
|
|
61
|
+
rule_hash["from-port"] = parts[0].to_i
|
|
62
|
+
rule_hash["to-port"] = parts[1].to_i
|
|
63
|
+
else
|
|
64
|
+
rule_hash["from-port"] = port
|
|
65
|
+
rule_hash["to-port"] = port
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
RuleConfig.new(rule_hash)
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
RuleConfig.new(json)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Public: Constructor
|
|
76
|
+
#
|
|
77
|
+
# json - a hash containing the JSON configuration for the rule
|
|
78
|
+
def initialize(json)
|
|
79
|
+
@protocol = json["protocol"]
|
|
80
|
+
|
|
81
|
+
if @protocol.downcase == "icmp"
|
|
82
|
+
@from = json["icmp-type"]
|
|
83
|
+
@to = json["icmp-code"]
|
|
84
|
+
else
|
|
85
|
+
@from = json["from-port"]
|
|
86
|
+
@to = json["to-port"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@security_groups = if !json["security-groups"].nil? then json["security-groups"] else [] end
|
|
90
|
+
@subnets = if !json["subnets"].nil?
|
|
91
|
+
json["subnets"].flat_map do |subnet|
|
|
92
|
+
if subnet.match(/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\/\d+/).nil?
|
|
93
|
+
Loader.subnet_group(subnet)
|
|
94
|
+
else
|
|
95
|
+
subnet
|
|
96
|
+
end
|
|
97
|
+
end.sort
|
|
98
|
+
else
|
|
99
|
+
[]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Public: Get the configuration as a hash
|
|
104
|
+
#
|
|
105
|
+
# Returns the hash
|
|
106
|
+
def hash
|
|
107
|
+
security_hashes = @security_groups.map do |security_group|
|
|
108
|
+
{
|
|
109
|
+
"security-groups" => [security_group],
|
|
110
|
+
"protocol" => @protocol,
|
|
111
|
+
"from-port" => if @protocol != "icmp" then @from end,
|
|
112
|
+
"to-port" => if @protocol != "icmp" then @to end,
|
|
113
|
+
"subnets" => [],
|
|
114
|
+
"icmp-type" => if @protocol == "icmp" then @from end,
|
|
115
|
+
"icmp-code" => if @protocol == "icmp" then @to end,
|
|
116
|
+
}.reject { |k, v| v.nil? }
|
|
117
|
+
end
|
|
118
|
+
subnet_hashes = @subnets.map do |subnet|
|
|
119
|
+
{
|
|
120
|
+
"security-groups" => [],
|
|
121
|
+
"protocol" => @protocol,
|
|
122
|
+
"from-port" => if @protocol != "icmp" then @from end,
|
|
123
|
+
"to-port" => if @protocol != "icmp" then @to end,
|
|
124
|
+
"subnets" => [subnet],
|
|
125
|
+
"icmp-type" => if @protocol == "icmp" then @from end,
|
|
126
|
+
"icmp-code" => if @protocol == "icmp" then @to end,
|
|
127
|
+
}.reject { |k, v| v.nil? }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
security_hashes + subnet_hashes
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Public: Converts the RuleConfig into the format needed by AWS
|
|
134
|
+
# to authorize/deauthorize rules
|
|
135
|
+
#
|
|
136
|
+
# vpc_id - the id of the vpc that security group ids should be derived from
|
|
137
|
+
def to_aws(vpc_id)
|
|
138
|
+
{
|
|
139
|
+
ip_protocol: if @protocol == "all" then "-1" else @protocol end,
|
|
140
|
+
from_port: if @from == "all" then "-1" else @from end,
|
|
141
|
+
to_port: if @to == "all" then "-1" else @to end,
|
|
142
|
+
user_id_group_pairs: if !@security_groups.empty?
|
|
143
|
+
@security_groups.map do |sg|
|
|
144
|
+
{
|
|
145
|
+
group_id: SecurityGroups::vpc_security_group_id_names[vpc_id].key(sg)
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
end,
|
|
149
|
+
ip_ranges: if !@subnets.empty?
|
|
150
|
+
@subnets.map do |subnet|
|
|
151
|
+
{
|
|
152
|
+
cidr_ip: subnet
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|