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 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