reyes 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTNjMTNjOGU3ZmM0YTBhMWNhNjE1YTYxNjY5ODczYmJiNDRlZTc5YQ==
4
+ ZmI5ZGU3MDc0MDc1NWQ0ODUzYjNmZjc3MWI2Y2ZlZjdmZjE4YjY5Ng==
5
5
  data.tar.gz: !binary |-
6
- MjdlNzBkMDBlY2I1MTkyZTJiNGY0ODBkOTI0MzVmZDNlNDkwM2I2Ng==
6
+ NzUxNTdlN2MwNTlmYWE4MGI5M2I1YjcyMzQxOWY1ZjhjYjY2ZTNiMg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MzI2MjVhMDFiMjBmZDZjYzFkMTVkMjMzZmZhMTU3NjE0NWMxNDU3OTY5N2U1
10
- Y2E0YmFhY2E1ZDRlZmMzNTM3Yjk3NGIwNDFkZTk0MGZhZWYwNDgzOTlmZjc2
11
- MWNmMjc2ZjBjNjJhNDA3YWQwYjdkYjM3NTBjNDhmNzNmZDM3N2E=
9
+ N2ViZDI2MjA0YjhjNWNiOWFkNGQzNmQwYzE2ZDQxYWUxNzI4NTI5NTUyNTQ2
10
+ MTQxYjBkZTk5NWJiOTQ2MjZmZGEzODViODk0N2UwZGY2YmZmZmE1NzdhMGVm
11
+ Njg0MTg4MmE2MjBhMWJhZTcwYTcwNzhiYjNlNTQ1ZGM0MTQwM2Y=
12
12
  data.tar.gz: !binary |-
13
- ZTU5MTM0ODAwNDM5ZjkzNThhOWU1YzMxNTg0NDNmYzc5MzkzMjdmM2Y5YzFi
14
- ZjJlZjFkYTMwNzFiMWJmZDhiYmU2ODAwMzRmMGJlMTE3Y2UxY2ExNmUyYWQ3
15
- ZmNlZTUyZDVlMTM2ZTM0YzNiMDJkMDc2ODBmZTNkYmVkMTc4NzU=
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
- g = Reyes::GroupManager.new(region, instance_id, options[:config])
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
- g.run!(options.fetch(:run_options))
20
+ r.run!(options.fetch(:run_options))
29
21
 
30
22
  if options[:prune]
31
- g.prune_ipsets
32
- # g.prune_iptables_rules
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
@@ -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}")
@@ -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 = Reyes::AwsManager.new(config_file)
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| g.instances.map(&:private_ip_address) }.flatten
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
- def generate_iptables_script_file(data, options={})
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
@@ -1,3 +1,3 @@
1
1
  module Reyes
2
- VERSION = '0.0.4' unless defined?(self::VERSION)
2
+ VERSION = '0.0.5' unless defined?(self::VERSION)
3
3
  end
data/lib/reyes.rb CHANGED
@@ -20,3 +20,4 @@ require_relative './reyes/ipset'
20
20
  require_relative './reyes/iptables'
21
21
  require_relative './reyes/run_generation'
22
22
  require_relative './reyes/utils'
23
+ require_relative './reyes/run_manager'
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
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-17 00:00:00.000000000 Z
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