reyes 0.0.4 → 0.0.5
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 +8 -8
- data/bin/reyes +6 -16
- data/lib/reyes/aws_manager.rb +17 -0
- data/lib/reyes/group_manager.rb +10 -135
- data/lib/reyes/run_manager.rb +138 -0
- data/lib/reyes/set_manager.rb +15 -0
- data/lib/reyes/version.rb +1 -1
- data/lib/reyes.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZmI5ZGU3MDc0MDc1NWQ0ODUzYjNmZjc3MWI2Y2ZlZjdmZjE4YjY5Ng==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzUxNTdlN2MwNTlmYWE4MGI5M2I1YjcyMzQxOWY1ZjhjYjY2ZTNiMg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
N2ViZDI2MjA0YjhjNWNiOWFkNGQzNmQwYzE2ZDQxYWUxNzI4NTI5NTUyNTQ2
|
10
|
+
MTQxYjBkZTk5NWJiOTQ2MjZmZGEzODViODk0N2UwZGY2YmZmZmE1NzdhMGVm
|
11
|
+
Njg0MTg4MmE2MjBhMWJhZTcwYTcwNzhiYjNlNTQ1ZGM0MTQwM2Y=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YmJjYzVkNDEwOGJiYThhNTg3N2VhMmUzNTdlZDBjY2U1ZjRiZjNhMTA2ZWVi
|
14
|
+
YzkyY2Y1ZjgyYzM4NDYyOGYwZjUwMjAwOWEwNzIxYWJmNTg3NDI0ZWUxMzli
|
15
|
+
ZjRmODI5NGYwZjcxMWE5NjgzM2ZjYmQ0N2Y0NjRmNGE3MzYyZTU=
|
data/bin/reyes
CHANGED
@@ -2,16 +2,6 @@
|
|
2
2
|
require 'optparse'
|
3
3
|
require_relative '../lib/reyes'
|
4
4
|
|
5
|
-
def command_dump(options)
|
6
|
-
instance_id = options.fetch(:instance_id)
|
7
|
-
region = options.fetch(:region)
|
8
|
-
g = Reyes::GroupManager.new(region, instance_id, options[:config])
|
9
|
-
|
10
|
-
AWS.memoize do
|
11
|
-
puts g.do_stuff.to_yaml
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
5
|
def command_install(options)
|
16
6
|
instance_id = options.fetch(:instance_id)
|
17
7
|
region = options.fetch(:region)
|
@@ -21,15 +11,17 @@ def command_install(options)
|
|
21
11
|
end
|
22
12
|
|
23
13
|
AWS.memoize do
|
24
|
-
|
14
|
+
aws = Reyes::AwsManager.new(options[:config])
|
15
|
+
g = Reyes::GroupManager.new(aws, region, instance_id, options[:config])
|
16
|
+
r = Reyes::RunManager.new(g)
|
25
17
|
|
26
18
|
options[:run_options][:log_accept] = true # TODO
|
27
19
|
|
28
|
-
|
20
|
+
r.run!(options.fetch(:run_options))
|
29
21
|
|
30
22
|
if options[:prune]
|
31
|
-
|
32
|
-
#
|
23
|
+
r.prune_ipsets
|
24
|
+
# Pruning IPTables rules doesn't make any sense, they are atomically replaced
|
33
25
|
end
|
34
26
|
end
|
35
27
|
|
@@ -93,8 +85,6 @@ Options:
|
|
93
85
|
optparse.parse!
|
94
86
|
|
95
87
|
case options[:command]
|
96
|
-
when :dump
|
97
|
-
command_dump(options)
|
98
88
|
when :install
|
99
89
|
command_install(options)
|
100
90
|
else
|
data/lib/reyes/aws_manager.rb
CHANGED
@@ -77,6 +77,23 @@ module Reyes
|
|
77
77
|
}.flatten
|
78
78
|
end
|
79
79
|
|
80
|
+
# List instances in a given VPC security group. Use this method to get
|
81
|
+
# better cache behavior for repeated calls to list security group members
|
82
|
+
# in a VPC.
|
83
|
+
#
|
84
|
+
# @param sg [AWS::EC2::SecurityGroup]
|
85
|
+
#
|
86
|
+
# @return [Array<AWS::EC2::Instance>]
|
87
|
+
#
|
88
|
+
def instances_in_security_group(sg)
|
89
|
+
unless sg.vpc
|
90
|
+
raise ArgumentError.new("SG #{sg.security_group_id} is not in VPC")
|
91
|
+
end
|
92
|
+
|
93
|
+
sg.vpc.instances.find_all {|i| i.security_groups.include?(sg)}
|
94
|
+
end
|
95
|
+
|
96
|
+
# TODO: remove (probably not needed)
|
80
97
|
def warm_sg_cache
|
81
98
|
connections.fetch(:ec2).each_pair do |region, ec2|
|
82
99
|
log.debug("Warming security group cache for #{region}")
|
data/lib/reyes/group_manager.rb
CHANGED
@@ -23,10 +23,11 @@ module Reyes
|
|
23
23
|
|
24
24
|
attr_reader :aws
|
25
25
|
|
26
|
-
def initialize(region, instance_id, config_file=nil)
|
26
|
+
def initialize(aws, region, instance_id, config_file=nil, generation=nil)
|
27
27
|
log.info("Initializing #{self.class.name} for #{region} #{instance_id}")
|
28
28
|
|
29
|
-
@aws =
|
29
|
+
@aws = aws
|
30
|
+
@generation = generation || RunGeneration.new
|
30
31
|
@instance_id = instance_id
|
31
32
|
@instance = @aws.ec2(region).instances[instance_id]
|
32
33
|
end
|
@@ -37,71 +38,6 @@ module Reyes
|
|
37
38
|
}
|
38
39
|
end
|
39
40
|
|
40
|
-
# @param [Hash] options
|
41
|
-
#
|
42
|
-
# @option options :empty [Boolean] (false) Generate an empty (default DROP)
|
43
|
-
# rule sets without actually looking up security groups
|
44
|
-
# @option options :interactive [Boolean] (false) Whether to prompt for
|
45
|
-
# confirmation before applying rules
|
46
|
-
# @option options :log_accept [Boolean] (false) Whether to log packets on
|
47
|
-
# ACCEPT
|
48
|
-
#
|
49
|
-
def run!(options={})
|
50
|
-
options = {
|
51
|
-
empty: false,
|
52
|
-
interactive: false,
|
53
|
-
log_accept: false,
|
54
|
-
}.merge(options)
|
55
|
-
|
56
|
-
log_accept = options.fetch(:log_accept)
|
57
|
-
|
58
|
-
log.info("Starting iptables rule generation run!")
|
59
|
-
|
60
|
-
if options.fetch(:empty)
|
61
|
-
log.warn("Generating empty (default DROP) rule set")
|
62
|
-
data = generate_rules_empty
|
63
|
-
else
|
64
|
-
data = generate_rules
|
65
|
-
end
|
66
|
-
|
67
|
-
new_rules = generate_iptables_script_file(data, log_accept: log_accept)
|
68
|
-
new_ipsets = generate_ipsets(data)
|
69
|
-
|
70
|
-
show_iptables_diff(new_rules)
|
71
|
-
show_ipsets_diff(new_ipsets)
|
72
|
-
|
73
|
-
if options.fetch(:interactive)
|
74
|
-
puts 'Press enter to continue...'
|
75
|
-
STDIN.gets
|
76
|
-
end
|
77
|
-
|
78
|
-
materialize_ipsets(new_ipsets)
|
79
|
-
iptables_restore(new_rules)
|
80
|
-
|
81
|
-
# XXX(richo) Should we be pruning inside run! ?
|
82
|
-
log.info('Finished firewall configuration run')
|
83
|
-
end
|
84
|
-
|
85
|
-
def show_iptables_diff(new_rules)
|
86
|
-
diff = Diff.new
|
87
|
-
diff.old.puts(Subprocess.check_output(%w{iptables-save}))
|
88
|
-
diff.new.puts(new_rules)
|
89
|
-
|
90
|
-
log.info "Proposed IPTables diff:"
|
91
|
-
puts diff.diff
|
92
|
-
end
|
93
|
-
|
94
|
-
def iptables_restore(new_rules)
|
95
|
-
log.info("Restoring #{new_rules.count("\n")} lines of iptables rules")
|
96
|
-
|
97
|
-
log.info('+ iptables-restore')
|
98
|
-
Subprocess.check_call(['iptables-restore'],
|
99
|
-
stdin: Subprocess::PIPE) do |p|
|
100
|
-
p.communicate(new_rules)
|
101
|
-
end
|
102
|
-
log.info('restored')
|
103
|
-
end
|
104
|
-
|
105
41
|
def generate_rules_empty
|
106
42
|
{:groups => {}, :ipsets => {}}
|
107
43
|
end
|
@@ -119,7 +55,7 @@ module Reyes
|
|
119
55
|
# any changes to the system beyond incrementing the run generation (used
|
120
56
|
# for ipset garbage collection).
|
121
57
|
#
|
122
|
-
def generate_rules
|
58
|
+
def generate_rules!
|
123
59
|
run_generation_increment!
|
124
60
|
log.info("Generating rules for generation #{run_generation}")
|
125
61
|
|
@@ -188,31 +124,6 @@ module Reyes
|
|
188
124
|
end
|
189
125
|
end
|
190
126
|
|
191
|
-
def show_ipsets_diff(new_ipsets)
|
192
|
-
diff = Diff.new
|
193
|
-
|
194
|
-
dump = lambda do |f, ipset|
|
195
|
-
ipset.sort_by(&:name).each do |ip|
|
196
|
-
f.puts(ip.name)
|
197
|
-
ip.members.sort.each do |m|
|
198
|
-
f.puts("\t#{m}")
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
dump.call(diff.old, Reyes::IPSet.load_all)
|
204
|
-
dump.call(diff.new, new_ipsets)
|
205
|
-
|
206
|
-
log.info "Proposed IPSets diff:"
|
207
|
-
puts diff.diff
|
208
|
-
end
|
209
|
-
|
210
|
-
def materialize_ipsets(new_ipsets)
|
211
|
-
new_ipsets.each do |ipset|
|
212
|
-
ipset.build
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
127
|
# TODO: delurk
|
217
128
|
def create_iptables_rules(data)
|
218
129
|
data.fetch(:groups).each do |cluster, items|
|
@@ -227,53 +138,13 @@ module Reyes
|
|
227
138
|
end
|
228
139
|
end
|
229
140
|
|
230
|
-
def prune_ipsets
|
231
|
-
log.info('Pruning old IPSets')
|
232
|
-
old_ipsets = []
|
233
|
-
current_ipsets = []
|
234
|
-
|
235
|
-
current_gen = run_generation
|
236
|
-
Reyes::IPSet.load_all.each do |set|
|
237
|
-
s = Reyes::GroupManager.parse_ipset_name(set.name)
|
238
|
-
unless s
|
239
|
-
log.warn("Skipping unparseable ipset name #{set.name.inspect}")
|
240
|
-
next
|
241
|
-
end
|
242
|
-
|
243
|
-
if s[:generation] < current_gen
|
244
|
-
old_ipsets << set
|
245
|
-
elsif s[:generation] == current_gen
|
246
|
-
current_ipsets << set
|
247
|
-
else
|
248
|
-
log.error("IPSet from a future generation detected: #{set.inspect}")
|
249
|
-
log.error("Cowardly refusing to proceed")
|
250
|
-
raise Reyes::Error.new("IPSet from future generation detected")
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
if current_ipsets.empty?
|
255
|
-
log.error("No IPSets from current generation.")
|
256
|
-
log.error("Cowardly refusing to proceed")
|
257
|
-
raise Reyes::Error.new("Pruning would remove all IPSets")
|
258
|
-
end
|
259
|
-
|
260
|
-
old_ipsets.each do |set|
|
261
|
-
log.info("Pruning IPSet: #{set.name}")
|
262
|
-
set.drop!
|
263
|
-
end
|
264
|
-
|
265
|
-
log.info('Done pruning old IPSets')
|
266
|
-
end
|
267
|
-
|
268
141
|
# @return [Integer]
|
269
142
|
def run_generation
|
270
|
-
@generation ||= Reyes::RunGeneration.new
|
271
143
|
@generation.value
|
272
144
|
end
|
273
145
|
|
274
146
|
# Increment the run generation and persist it to disk
|
275
147
|
def run_generation_increment!
|
276
|
-
run_generation
|
277
148
|
@generation.increment!
|
278
149
|
end
|
279
150
|
|
@@ -309,7 +180,9 @@ module Reyes
|
|
309
180
|
#
|
310
181
|
def addresses_for_group(group)
|
311
182
|
groups = foreign_groups_by_name(group.name)
|
312
|
-
groups.map { |g|
|
183
|
+
groups.map { |g|
|
184
|
+
@aws.instances_in_security_group(g).map(&:private_ip_address)
|
185
|
+
}.flatten
|
313
186
|
end
|
314
187
|
|
315
188
|
# Look up remote VPC security groups by name.
|
@@ -329,7 +202,9 @@ module Reyes
|
|
329
202
|
# @option options [Boolean] log_drop (true)
|
330
203
|
# @option options [Boolean] log_accept (false)
|
331
204
|
#
|
332
|
-
|
205
|
+
# @return [String]
|
206
|
+
#
|
207
|
+
def generate_iptables_script(data, options={})
|
333
208
|
log.info("Generating script for iptables-restore")
|
334
209
|
|
335
210
|
options = {
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Reyes
|
2
|
+
class RunManager
|
3
|
+
include Chalk::Log
|
4
|
+
|
5
|
+
def initialize(group_manager)
|
6
|
+
@group_manager = group_manager
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param [Hash] options
|
10
|
+
#
|
11
|
+
# @option options :empty [Boolean] (false) Generate an empty (default DROP)
|
12
|
+
# rule sets without actually looking up security groups
|
13
|
+
# @option options :interactive [Boolean] (false) Whether to prompt for
|
14
|
+
# confirmation before applying rules
|
15
|
+
# @option options :log_accept [Boolean] (false) Whether to log packets on
|
16
|
+
# ACCEPT
|
17
|
+
#
|
18
|
+
def run!(options={})
|
19
|
+
options = {
|
20
|
+
empty: false,
|
21
|
+
interactive: false,
|
22
|
+
log_accept: false,
|
23
|
+
}.merge(options)
|
24
|
+
|
25
|
+
log_accept = options.fetch(:log_accept)
|
26
|
+
|
27
|
+
log.info("Starting iptables rule generation run!")
|
28
|
+
|
29
|
+
if options.fetch(:empty)
|
30
|
+
log.warn("Generating empty (default DROP) rule set")
|
31
|
+
data = @group_manager.generate_rules_empty
|
32
|
+
else
|
33
|
+
data = @group_manager.generate_rules!
|
34
|
+
end
|
35
|
+
|
36
|
+
new_rules = @group_manager.generate_iptables_script(data, log_accept: log_accept)
|
37
|
+
new_ipsets = @group_manager.generate_ipsets(data)
|
38
|
+
|
39
|
+
show_iptables_diff(new_rules)
|
40
|
+
show_ipsets_diff(new_ipsets)
|
41
|
+
|
42
|
+
if options.fetch(:interactive)
|
43
|
+
puts 'Press enter to continue...'
|
44
|
+
STDIN.gets
|
45
|
+
end
|
46
|
+
|
47
|
+
materialize_ipsets(new_ipsets)
|
48
|
+
iptables_restore(new_rules)
|
49
|
+
|
50
|
+
# XXX(richo) Should we be pruning inside run! ?
|
51
|
+
log.info('Finished firewall configuration run')
|
52
|
+
end
|
53
|
+
|
54
|
+
def materialize_ipsets(new_ipsets)
|
55
|
+
new_ipsets.each do |ipset|
|
56
|
+
ipset.build
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def iptables_restore(new_rules)
|
61
|
+
log.info("Restoring #{new_rules.count("\n")} lines of iptables rules")
|
62
|
+
|
63
|
+
log.info('+ iptables-restore')
|
64
|
+
Subprocess.check_call(['iptables-restore'],
|
65
|
+
stdin: Subprocess::PIPE) do |p|
|
66
|
+
p.communicate(new_rules)
|
67
|
+
end
|
68
|
+
log.info('restored')
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def prune_ipsets
|
73
|
+
log.info('Pruning old IPSets')
|
74
|
+
old_ipsets = []
|
75
|
+
current_ipsets = []
|
76
|
+
|
77
|
+
current_gen = @group_manager.run_generation
|
78
|
+
Reyes::IPSet.load_all.each do |set|
|
79
|
+
s = Reyes::GroupManager.parse_ipset_name(set.name)
|
80
|
+
unless s
|
81
|
+
log.warn("Skipping unparseable ipset name #{set.name.inspect}")
|
82
|
+
next
|
83
|
+
end
|
84
|
+
|
85
|
+
if s[:generation] < current_gen
|
86
|
+
old_ipsets << set
|
87
|
+
elsif s[:generation] == current_gen
|
88
|
+
current_ipsets << set
|
89
|
+
else
|
90
|
+
log.error("IPSet from a future generation detected: #{set.inspect}")
|
91
|
+
log.error("Cowardly refusing to proceed")
|
92
|
+
raise Reyes::Error.new("IPSet from future generation detected")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if current_ipsets.empty?
|
97
|
+
log.error("No IPSets from current generation.")
|
98
|
+
log.error("Cowardly refusing to proceed")
|
99
|
+
raise Reyes::Error.new("Pruning would remove all IPSets")
|
100
|
+
end
|
101
|
+
|
102
|
+
old_ipsets.each do |set|
|
103
|
+
log.info("Pruning IPSet: #{set.name}")
|
104
|
+
set.drop!
|
105
|
+
end
|
106
|
+
|
107
|
+
log.info('Done pruning old IPSets')
|
108
|
+
end
|
109
|
+
|
110
|
+
def show_iptables_diff(new_rules)
|
111
|
+
diff = Diff.new
|
112
|
+
diff.old.puts(Subprocess.check_output(%w{iptables-save}))
|
113
|
+
diff.new.puts(new_rules)
|
114
|
+
|
115
|
+
log.info "Proposed IPTables diff:"
|
116
|
+
puts diff.diff
|
117
|
+
end
|
118
|
+
|
119
|
+
def show_ipsets_diff(new_ipsets)
|
120
|
+
diff = Diff.new
|
121
|
+
|
122
|
+
dump = lambda do |f, ipset|
|
123
|
+
ipset.sort_by(&:name).each do |ip|
|
124
|
+
f.puts(ip.name)
|
125
|
+
ip.members.sort.each do |m|
|
126
|
+
f.puts("\t#{m}")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
dump.call(diff.old, Reyes::IPSet.load_all)
|
132
|
+
dump.call(diff.new, new_ipsets)
|
133
|
+
|
134
|
+
log.info "Proposed IPSets diff:"
|
135
|
+
puts diff.diff
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Reyes
|
2
|
+
class SetPopulator
|
3
|
+
include Chalk::Log
|
4
|
+
|
5
|
+
attr_reader :aws
|
6
|
+
|
7
|
+
def initialize(region, instance_id)
|
8
|
+
@aws = Reyes::AwsManager.new
|
9
|
+
@instance_id = instance_id
|
10
|
+
@instance = @aws.ec2(region).instances[instance_id]
|
11
|
+
@our_groups = @instance.security_groups
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/lib/reyes/version.rb
CHANGED
data/lib/reyes.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reyes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Brody
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-02-
|
12
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -122,6 +122,8 @@ files:
|
|
122
122
|
- lib/reyes/ipset.rb
|
123
123
|
- lib/reyes/iptables.rb
|
124
124
|
- lib/reyes/run_generation.rb
|
125
|
+
- lib/reyes/run_manager.rb
|
126
|
+
- lib/reyes/set_manager.rb
|
125
127
|
- lib/reyes/utils.rb
|
126
128
|
- lib/reyes/version.rb
|
127
129
|
- reyes.gemspec
|