piculet 0.0.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.
- data/README.md +124 -0
- data/bin/piculet +124 -0
- data/lib/piculet.rb +3 -0
- data/lib/piculet/client.rb +148 -0
- data/lib/piculet/dsl.rb +44 -0
- data/lib/piculet/dsl/converter.rb +120 -0
- data/lib/piculet/dsl/ec2.rb +30 -0
- data/lib/piculet/dsl/permission.rb +64 -0
- data/lib/piculet/dsl/permissions.rb +47 -0
- data/lib/piculet/dsl/security-group.rb +48 -0
- data/lib/piculet/exporter.rb +58 -0
- data/lib/piculet/ext/ec2-owner-id-ext.rb +88 -0
- data/lib/piculet/ext/ip-permission-collection-ext.rb +45 -0
- data/lib/piculet/ext/string-ext.rb +27 -0
- data/lib/piculet/logger.rb +33 -0
- data/lib/piculet/version.rb +5 -0
- data/lib/piculet/wrapper/ec2-wrapper.rb +18 -0
- data/lib/piculet/wrapper/permission-collection.rb +134 -0
- data/lib/piculet/wrapper/permission.rb +92 -0
- data/lib/piculet/wrapper/security-group-collection.rb +36 -0
- data/lib/piculet/wrapper/security-group.rb +56 -0
- metadata +149 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'piculet/wrapper/security-group-collection'
|
2
|
+
|
3
|
+
module Piculet
|
4
|
+
class EC2Wrapper
|
5
|
+
def initialize(ec2, options)
|
6
|
+
@ec2 = ec2
|
7
|
+
@options = options.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def security_groups
|
11
|
+
SecurityGroupCollection.new(@ec2.security_groups, @options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def updated?
|
15
|
+
!!@options.updated
|
16
|
+
end
|
17
|
+
end # EC2Wrapper
|
18
|
+
end # Piculet
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'piculet/logger'
|
2
|
+
require 'piculet/ext/ip-permission-collection-ext'
|
3
|
+
require 'piculet/wrapper/permission'
|
4
|
+
|
5
|
+
module Piculet
|
6
|
+
class EC2Wrapper
|
7
|
+
class SecurityGroupCollection
|
8
|
+
class SecurityGroup
|
9
|
+
class PermissionCollection
|
10
|
+
include Logger::ClientHelper
|
11
|
+
|
12
|
+
def initialize(security_group, direction, options)
|
13
|
+
@security_group = security_group
|
14
|
+
@permissions = security_group.send("#{direction}_ip_permissions")
|
15
|
+
@direction = direction
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def each
|
20
|
+
perm_list = @permissions ? @permissions.aggregate : []
|
21
|
+
|
22
|
+
perm_list.each do |perm|
|
23
|
+
yield(Permission.new(perm, self, @options))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def authorize(protocol, ports, *sources)
|
28
|
+
log(:info, " authorize #{format_sources(sources)}", :green)
|
29
|
+
|
30
|
+
unless @options.dry_run
|
31
|
+
sources = normalize_sources(sources)
|
32
|
+
|
33
|
+
case @direction
|
34
|
+
when :ingress
|
35
|
+
@security_group.authorize_ingress(protocol, ports, *sources)
|
36
|
+
@options.updated = true
|
37
|
+
when :egress
|
38
|
+
sources.push(:protocol => protocol, :ports => ports)
|
39
|
+
@security_group.authorize_egress(*sources)
|
40
|
+
@options.updated = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def revoke(protocol, ports, *sources)
|
46
|
+
log(:info, " revoke #{format_sources(sources)}", :green)
|
47
|
+
|
48
|
+
unless @options.dry_run
|
49
|
+
sources = normalize_sources(sources)
|
50
|
+
|
51
|
+
case @direction
|
52
|
+
when :ingress
|
53
|
+
@security_group.revoke_ingress(protocol, ports, *sources)
|
54
|
+
@options.updated = true
|
55
|
+
when :egress
|
56
|
+
sources.push(:protocol => protocol, :ports => ports)
|
57
|
+
@security_group.revoke_egress(*sources)
|
58
|
+
@options.updated = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def create(protocol, port_range, dsl)
|
64
|
+
dsl_ip_ranges = dsl.ip_ranges || []
|
65
|
+
dsl_groups = (dsl.groups || []).map do |i|
|
66
|
+
i.kind_of?(Array) ? i : [@options.ec2.owner_id, i]
|
67
|
+
end
|
68
|
+
|
69
|
+
sources = dsl_ip_ranges + dsl_groups
|
70
|
+
|
71
|
+
unless sources.empty?
|
72
|
+
log(:info, 'Create Permission', :cyan, "#{log_id} > #{protocol} #{port_range}")
|
73
|
+
authorize(protocol, port_range, *sources)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def log_id
|
78
|
+
vpc = @security_group.vpc_id || :classic
|
79
|
+
name = @security_group.name
|
80
|
+
|
81
|
+
unless @options.ec2.own?(@security_group.owner_id)
|
82
|
+
name = "#{@security_group.owner_id}/#{name}"
|
83
|
+
end
|
84
|
+
|
85
|
+
"#{vpc} > #{name}(#{@direction})"
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def normalize_sources(sources)
|
90
|
+
normalized = []
|
91
|
+
|
92
|
+
sources.each do |src|
|
93
|
+
case src
|
94
|
+
when String
|
95
|
+
normalized << src
|
96
|
+
when Array
|
97
|
+
owner_id, group = src
|
98
|
+
|
99
|
+
unless group =~ /\Asg-[0-9a-f]\Z/
|
100
|
+
sg_coll = @options.ec2.security_groups.filter('group-name', group)
|
101
|
+
sg_coll = sg_coll.filter('vpc-id', @security_group.vpc_id) if @security_group.vpc?
|
102
|
+
sg_coll = sg_coll.filter('owner-id', owner_id) unless @options.ec2.own?(owner_id)
|
103
|
+
|
104
|
+
unless (sg = sg_coll.first)
|
105
|
+
raise "Can't find SecurityGroup: #{owner_id}/#{group} in #{@security_group.vpc_id || :classic}"
|
106
|
+
end
|
107
|
+
|
108
|
+
group = sg.id
|
109
|
+
end
|
110
|
+
|
111
|
+
normalized << {:user_id => owner_id, :group_id => group}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
return normalized
|
116
|
+
end
|
117
|
+
|
118
|
+
def format_sources(sources)
|
119
|
+
sources.map {|src|
|
120
|
+
if src.kind_of?(Array)
|
121
|
+
owner_id, group = src
|
122
|
+
dst = [group]
|
123
|
+
dst.unshift(owner_id) unless @options.ec2.own?(owner_id)
|
124
|
+
dst.join('/')
|
125
|
+
else
|
126
|
+
src
|
127
|
+
end
|
128
|
+
}.join(', ')
|
129
|
+
end
|
130
|
+
end # PermissionCollection
|
131
|
+
end # SecurityGroup
|
132
|
+
end # SecurityGroupCollection
|
133
|
+
end # EC2Wrapper
|
134
|
+
end # Piculet
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'piculet/logger'
|
3
|
+
|
4
|
+
module Piculet
|
5
|
+
class EC2Wrapper
|
6
|
+
class SecurityGroupCollection
|
7
|
+
class SecurityGroup
|
8
|
+
class PermissionCollection
|
9
|
+
class Permission
|
10
|
+
extend Forwardable
|
11
|
+
include Logger::ClientHelper
|
12
|
+
|
13
|
+
def_delegators(
|
14
|
+
:@permission,
|
15
|
+
:protocol, :port_range, :ip_ranges, :groups)
|
16
|
+
|
17
|
+
def initialize(permission, collection, options)
|
18
|
+
@permission = permission
|
19
|
+
@collection = collection
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def eql?(dsl)
|
24
|
+
dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups = normalize_attrs(dsl)
|
25
|
+
(self_ip_ranges == dsl_ip_ranges) and (self_groups == dsl_groups)
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(dsl)
|
29
|
+
log(:info, 'Update Permission', :green, log_id)
|
30
|
+
|
31
|
+
plus_ip_ranges, minus_ip_ranges, plus_groups, minus_groups = diff(dsl)
|
32
|
+
|
33
|
+
unless (plus_ip_ranges + plus_groups).empty?
|
34
|
+
@collection.authorize(protocol, port_range, *(plus_ip_ranges + plus_groups))
|
35
|
+
end
|
36
|
+
|
37
|
+
unless (minus_ip_ranges + minus_groups).empty?
|
38
|
+
@collection.revoke(protocol, port_range, *(minus_ip_ranges + minus_groups))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete
|
43
|
+
log(:info, 'Delete Permission', :red, log_id)
|
44
|
+
|
45
|
+
self_ip_ranges, self_groups = normalize_self_attrs
|
46
|
+
|
47
|
+
unless (self_ip_ranges + self_groups).empty?
|
48
|
+
@collection.revoke(protocol, port_range, *(self_ip_ranges + self_groups))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def log_id
|
54
|
+
"#{@collection.log_id} > #{protocol} #{port_range}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def diff(dsl)
|
58
|
+
dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups = normalize_attrs(dsl)
|
59
|
+
|
60
|
+
[
|
61
|
+
dsl_ip_ranges - self_ip_ranges,
|
62
|
+
self_ip_ranges - dsl_ip_ranges,
|
63
|
+
dsl_groups - self_groups,
|
64
|
+
self_groups - dsl_groups,
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
def normalize_attrs(dsl)
|
69
|
+
dsl_ip_ranges = (dsl.ip_ranges || []).sort
|
70
|
+
dsl_groups = (dsl.groups || []).map {|i|
|
71
|
+
i.kind_of?(Array) ? i : [@options.ec2.owner_id, i]
|
72
|
+
}.sort
|
73
|
+
|
74
|
+
self_ip_ranges, self_groups = normalize_self_attrs
|
75
|
+
|
76
|
+
[dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups]
|
77
|
+
end
|
78
|
+
|
79
|
+
def normalize_self_attrs
|
80
|
+
self_ip_ranges = (@permission.ip_ranges || []).sort
|
81
|
+
self_groups = (@permission.groups || []).map {|i|
|
82
|
+
[i.owner_id, i.name]
|
83
|
+
}.sort
|
84
|
+
|
85
|
+
[self_ip_ranges, self_groups]
|
86
|
+
end
|
87
|
+
end # Permission
|
88
|
+
end # PermissionCollection
|
89
|
+
end # SecurityGroup
|
90
|
+
end # SecurityGroupCollection
|
91
|
+
end # EC2Wrapper
|
92
|
+
end # Piculet
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'piculet/logger'
|
3
|
+
require 'piculet/wrapper/security-group'
|
4
|
+
|
5
|
+
module Piculet
|
6
|
+
class EC2Wrapper
|
7
|
+
class SecurityGroupCollection
|
8
|
+
include Logger::ClientHelper
|
9
|
+
|
10
|
+
def initialize(security_groups, options)
|
11
|
+
@security_groups = security_groups
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
@security_groups.each do |sg|
|
17
|
+
yield(SecurityGroup.new(sg, @options))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(name, opts = {})
|
22
|
+
log(:info, 'Create SecurityGroup', :cyan, "#{opts[:vpc] || :classic} > #{name}")
|
23
|
+
log(:warn, '`egress any 0.0.0.0/0` is implicitly defined', :yellow) if @options.dry_run && opts[:vpc]
|
24
|
+
|
25
|
+
if @options.dry_run
|
26
|
+
sg = OpenStruct.new({:id => '<new security group>', :name => name}.merge(opts))
|
27
|
+
else
|
28
|
+
sg = @security_groups.create(name, opts)
|
29
|
+
@options.updated = true
|
30
|
+
end
|
31
|
+
|
32
|
+
SecurityGroup.new(sg, @options)
|
33
|
+
end
|
34
|
+
end # SecurityGroupCollection
|
35
|
+
end # EC2Wrapper
|
36
|
+
end # Piculet
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'piculet/logger'
|
3
|
+
require 'piculet/wrapper/permission-collection'
|
4
|
+
|
5
|
+
module Piculet
|
6
|
+
class EC2Wrapper
|
7
|
+
class SecurityGroupCollection
|
8
|
+
class SecurityGroup
|
9
|
+
extend Forwardable
|
10
|
+
include Logger::ClientHelper
|
11
|
+
|
12
|
+
def_delegators(
|
13
|
+
:@security_group,
|
14
|
+
:vpc_id, :name, :vpc?)
|
15
|
+
|
16
|
+
def initialize(security_group, options)
|
17
|
+
@security_group = security_group
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def eql?(dsl)
|
22
|
+
@security_group.description == dsl.description
|
23
|
+
end
|
24
|
+
|
25
|
+
def update(dsl)
|
26
|
+
if @security_group.description != dsl.description
|
27
|
+
log(:warn, '`description` cannot be updated', :yellow, "#{vpc_id || :classic} > #{name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
# XXX:
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete
|
34
|
+
log(:info, 'Delete SecurityGroup', :red, "#{vpc_id || :classic} > #{name}")
|
35
|
+
|
36
|
+
if name == 'default'
|
37
|
+
log(:warn, 'SecurityGroup `default` is reserved', :yellow)
|
38
|
+
else
|
39
|
+
unless @options.dry_run
|
40
|
+
@security_group.delete
|
41
|
+
@options.updated = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ingress_ip_permissions
|
47
|
+
PermissionCollection.new(@security_group, :ingress, @options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def egress_ip_permissions
|
51
|
+
PermissionCollection.new(@security_group, :egress, @options)
|
52
|
+
end
|
53
|
+
end # SecurityGroup
|
54
|
+
end # SecurityGroupCollection
|
55
|
+
end # EC2Wrapper
|
56
|
+
end # Piculet
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: piculet
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- winebarrel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.19.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.19.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: term-ansicolor
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.2.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.2.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: pry
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Piculet is a tool to manage Security Group. It defines the state of Security
|
95
|
+
Group using DSL, and updates Security Group according to DSL.
|
96
|
+
email:
|
97
|
+
- sgwr_dts@yahoo.co.jp
|
98
|
+
executables:
|
99
|
+
- piculet
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- README.md
|
104
|
+
- bin/piculet
|
105
|
+
- lib/piculet/client.rb
|
106
|
+
- lib/piculet/dsl/converter.rb
|
107
|
+
- lib/piculet/dsl/ec2.rb
|
108
|
+
- lib/piculet/dsl/permission.rb
|
109
|
+
- lib/piculet/dsl/permissions.rb
|
110
|
+
- lib/piculet/dsl/security-group.rb
|
111
|
+
- lib/piculet/dsl.rb
|
112
|
+
- lib/piculet/exporter.rb
|
113
|
+
- lib/piculet/ext/ec2-owner-id-ext.rb
|
114
|
+
- lib/piculet/ext/ip-permission-collection-ext.rb
|
115
|
+
- lib/piculet/ext/string-ext.rb
|
116
|
+
- lib/piculet/logger.rb
|
117
|
+
- lib/piculet/version.rb
|
118
|
+
- lib/piculet/wrapper/ec2-wrapper.rb
|
119
|
+
- lib/piculet/wrapper/permission-collection.rb
|
120
|
+
- lib/piculet/wrapper/permission.rb
|
121
|
+
- lib/piculet/wrapper/security-group-collection.rb
|
122
|
+
- lib/piculet/wrapper/security-group.rb
|
123
|
+
- lib/piculet.rb
|
124
|
+
homepage: https://bitbucket.org/winebarrel/piculet
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 1.8.23
|
146
|
+
signing_key:
|
147
|
+
specification_version: 3
|
148
|
+
summary: Piculet is a tool to manage Security Group.
|
149
|
+
test_files: []
|