reyes 0.1.1 → 0.2.0

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
- NGE0MzE5OTBmNzQ2YWI0YmMxYmJlZGMxMDgxMGEwNDJiOTgzNTg0Mg==
4
+ ZTZhZTI3NWU5Yjk3ZWE1NWI4MzlkYjVjMzg1NDg0NGYwNTAxMzU0OA==
5
5
  data.tar.gz: !binary |-
6
- ZTY4MWFiMmNmMzg5MzEwNDBlYTc4MWUwZGVmYWZhMWYyOGMyZjI3YQ==
6
+ NzE1OGRiMzYxMWE2OWQwNDdhNDc4ZGFhYThlMTZmZDMyMGI1MGQ4NQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTczYjRhNGIzZjZjMDViZjRlZDg4ODc2ZTNhYTQ5YjA4MjBiMDFmMjY5NWIw
10
- NDE5M2ZmYzZkNDgzMDlmNjVlZTcwYjIyMGI2ODdiNzQ0MDg0ZWJmN2M3Y2Qy
11
- MWJhYzljYTM5YzljNDYyMDI2MmQyYTZjODExZDUwZWM1YTFjNjg=
9
+ Y2NhNTZjZDI3NTIzOGFhMzJmY2EyY2JhNjdhZWE1OTQ1OTZlM2Q2ZjBhOGUy
10
+ MzQ5NWRjYzAzNjk2NGZiZTFkYjI5MDczOGZhODA0NTZmYmZmZTZjN2E3ZjM4
11
+ NmIyZGU4YzBjZTI5ZDY0MTVjODI4NTY4ZGJkNjc0ZjYyYWVhZDE=
12
12
  data.tar.gz: !binary |-
13
- OWRhNjJjMWI4YTFjN2UwYmNkNWQxZjllZTRkMDM5ZGM5MWM0YzA0MzQyMDVm
14
- ZDMzZDE2ZTc0ZjE2NzEyOWM1NzhmZjYyNzlmNmYwZWZkMzYzZWMyMjE4OWM5
15
- M2MzM2FiYTRiMDZmZjUwZmQyYjk4MjQyOGFkOThjZGVmYjYwZTk=
13
+ YjE1MjliYjFmMWJhMmI2YTdlZmQzMGMwYmIyOTg0NWZkMjIxYTE3ZjRhNTky
14
+ YTNjYWU5OGYyZDhiZjU1ZDcyOGEzNDZlMjUyNWU5MTEzNjI5MzYwNmE3MmFi
15
+ NmMwOWNmNTQ1NjVhMjRlYmQyYmYwN2I1ZmNiNjlmYjllMWM4OGU=
data/bin/reyes CHANGED
@@ -7,6 +7,27 @@ def command_dump(output_file, options)
7
7
  aws.dump_fake_data(output_file)
8
8
  end
9
9
 
10
+ def command_fetch(instance_id, options)
11
+ region = options.fetch(:region)
12
+ aws = Reyes::AwsManager.new(options[:config])
13
+ s3 = Reyes::S3Loader.new(aws, options[:config])
14
+ wrapper = Reyes::PgpWrapper.new(options[:config])
15
+
16
+ armoured_rules = s3.fetch_rules
17
+ cleartext_rules = wrapper.verify!(armoured_rules)
18
+
19
+ fake = Reyes::FakeAws.new(JSON.load cleartext_rules)
20
+ g = Reyes::GroupManager.new(fake, region, instance_id)
21
+ r = Reyes::RunManager.new(g)
22
+
23
+ data = r.generate_data!(options.fetch(:gen_options))
24
+ r.apply_data!(data, options.fetch(:apply_options))
25
+
26
+ if options[:prune]
27
+ r.prune_ipsets
28
+ end
29
+ end
30
+
10
31
  def command_install(json_file, instance_id, options)
11
32
  region = options.fetch(:region)
12
33
 
@@ -14,8 +35,16 @@ def command_install(json_file, instance_id, options)
14
35
  Reyes::Utils.sleep_random(options[:splay])
15
36
  end
16
37
 
17
- fake = Reyes::FakeAws.new(File.open(json_file, 'r'))
38
+ data = JSON.load(File.open(json_file, 'r'))
39
+ generated_time = data.fetch("metadata").fetch("generated_stamp")
40
+ fake = Reyes::FakeAws.new(data)
18
41
  g = Reyes::GroupManager.new(fake, region, instance_id)
42
+
43
+ if Time.new(generated_time) > g.run_generation_time
44
+ # FIXME Deal gracefully with this case
45
+ # Should bail out, exiting nonzero if there were changes to commit
46
+ end
47
+
19
48
  r = Reyes::RunManager.new(g)
20
49
 
21
50
  data = r.generate_data!(options.fetch(:gen_options))
@@ -26,6 +55,18 @@ def command_install(json_file, instance_id, options)
26
55
  end
27
56
  end
28
57
 
58
+ def command_upload(options)
59
+ aws = Reyes::AwsManager.new(options[:config])
60
+ wrapper = Reyes::PgpWrapper.new(options[:config])
61
+
62
+ data = aws.generate_fake_data_json
63
+
64
+ armoured_data = wrapper.clearsign(data)
65
+
66
+ s3 = Reyes::S3Loader.new(aws, options[:config])
67
+ s3.upload_rules(armoured_data)
68
+ end
69
+
29
70
  def parse_args
30
71
  options = {
31
72
  :region => 'us-west-1', # TODO: make required
@@ -33,6 +74,7 @@ def parse_args
33
74
  :apply_options => {
34
75
  :log_accept => true, # TODO remove this default
35
76
  },
77
+ :fetch_options => {},
36
78
  }
37
79
 
38
80
  optparse = OptionParser.new do |opts|
@@ -51,6 +93,14 @@ Commands:
51
93
 
52
94
  #{File.basename($0)} [options] dump JSON_FILE
53
95
 
96
+ fetch: load new rules from s3 and install them on the local system
97
+
98
+ #{File.basename($0)} [options] fetch INSTANCE_ID
99
+
100
+ upload: generate and sign a full dump of the rule inputs and upload them to s3
101
+
102
+ #{File.basename($0)} [options] upload
103
+
54
104
 
55
105
  Defaults:
56
106
  region: #{options.fetch(:region)}
@@ -127,6 +177,21 @@ Options:
127
177
  exit 2
128
178
  end
129
179
  command_install(ARGV.fetch(0), ARGV.fetch(1), options)
180
+ when 'fetch'
181
+ unless ARGV.length == 1
182
+ STDERR.puts optparse
183
+ exit 2
184
+ end
185
+
186
+ command_fetch(ARGV.fetch(0), options)
187
+ when 'upload'
188
+ unless ARGV.length == 0
189
+ STDERR.puts optparse
190
+ exit 2
191
+ end
192
+
193
+ command_upload(options)
194
+
130
195
  else
131
196
  STDERR.puts optparse
132
197
  STDERR.puts "\nError: Must provide a command"
data/config.yaml.example CHANGED
@@ -11,3 +11,10 @@ aws:
11
11
  vpcs:
12
12
  - [us-west-2, vpc-abcdef12]
13
13
 
14
+ s3:
15
+ bucket: reyes-config
16
+ path: rules.json
17
+
18
+ reyes:
19
+ pgp:
20
+ signing_key: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
data/lib/reyes.rb CHANGED
@@ -22,3 +22,5 @@ require_relative './reyes/iptables'
22
22
  require_relative './reyes/run_generation'
23
23
  require_relative './reyes/run_manager'
24
24
  require_relative './reyes/utils'
25
+ require_relative './reyes/s3_loader'
26
+ require_relative './reyes/pgp_wrapper'
@@ -32,6 +32,14 @@ module Reyes
32
32
  connections.fetch(:ec2).fetch(region)
33
33
  end
34
34
 
35
+ def s3
36
+ @s3 ||= AWS::S3.new({
37
+ access_key_id: @config.aws_credentials.fetch(:access_key_id),
38
+ secret_access_key: @config.aws_credentials.fetch(:secret_access_key),
39
+ logger: Chalk::Log::Logger.new("s3"),
40
+ })
41
+ end
42
+
35
43
  def connections
36
44
  @connections ||= connect!
37
45
  end
@@ -145,6 +153,10 @@ module Reyes
145
153
  data
146
154
  end
147
155
 
156
+ def generate_fake_data_json
157
+ JSON.pretty_generate(generate_fake_data)
158
+ end
159
+
148
160
  def dump_fake_data(filename)
149
161
  log.info("Dumping AWS data to #{filename.inspect}")
150
162
  data = generate_fake_data
data/lib/reyes/config.rb CHANGED
@@ -14,6 +14,10 @@ module Reyes
14
14
  aws_config.fetch('credentials')
15
15
  end
16
16
 
17
+ def reyes_config
18
+ config.fetch('reyes')
19
+ end
20
+
17
21
  def config
18
22
  @config ||= YAML.load_file(@path)
19
23
  end
@@ -5,11 +5,10 @@ module Reyes
5
5
  class FakeAws
6
6
  include Chalk::Log
7
7
 
8
- # @param json_source
9
- def initialize(json_source)
10
- log.info('Loading JSON data')
11
- @data = JSON.load(json_source)
12
- log.info("Loaded JSON with metadata: #{metadata.inspect}")
8
+ # @param data [Hash]
9
+ def initialize(data)
10
+ @data = data
11
+ log.info("Initialized FakeAws with metadata: #{metadata.inspect}")
13
12
  end
14
13
 
15
14
  def region_data(region)
@@ -48,6 +48,11 @@ module Reyes
48
48
  }
49
49
  end
50
50
 
51
+ def load_from_s3(aws, config)
52
+ s3 = S3Loader.new(aws, config)
53
+ data = s3.latest
54
+ end
55
+
51
56
  # Given our instance ID and security group rules, generate IPTables rules
52
57
  # needed to emulate security group behavior for foreign VPCs.
53
58
  #
@@ -147,6 +152,11 @@ module Reyes
147
152
  @generation.value
148
153
  end
149
154
 
155
+ # @return [Time]
156
+ def run_generation_time
157
+ @generation.mtime
158
+ end
159
+
150
160
  # Increment the run generation and persist it to disk
151
161
  def run_generation_increment!
152
162
  @generation.increment!
@@ -0,0 +1,73 @@
1
+ module Reyes
2
+ class PgpWrapper
3
+ class VerificationFailed < StandardError
4
+ end
5
+
6
+ attr_reader :key_id, :keyring_directory
7
+
8
+ # Create a PgpVerifier
9
+ #
10
+ # @param key_id [String] 40 digit key fingerprint
11
+ def initialize(config_path=nil)
12
+ @config ||= Reyes::Config.new(config_path)
13
+ @key_id = @config.reyes_config.fetch("signing_key").upcase
14
+ @keyring_directory = @config.reyes_config.fetch("keyring_directory")
15
+ end
16
+
17
+ # Pattern for parsing gpg --status-fd signature output
18
+ PATTERN = /^\[GNUPG:\] VALIDSIG ([A-F0-9]{40}) .+ ([A-F0-9]{40})$/
19
+
20
+ # Verifies +data+ against +@keyid+
21
+ #
22
+ # @param data [String] the data to verify
23
+ #
24
+ # @raise [VerificationFailed]
25
+ #
26
+ # @return [String] the stripped cleartext data
27
+ #
28
+ def verify!(data)
29
+ gpg_cmd = %w{gpg --batch --decrypt --status-fd 2} + keyring_args + ['-']
30
+ Subprocess.check_call(gpg_cmd,
31
+ :stdin => Subprocess::PIPE,
32
+ :stdout => Subprocess::PIPE,
33
+ :stderr => Subprocess::PIPE) do |child|
34
+ out, err = child.communicate(data)
35
+
36
+ if err =~ PATTERN
37
+ raise VerificationFailed.new("Bad key match") unless $1 == $2
38
+ raise VerificationFailed.new("Bad Key ID") unless $1 == key_id
39
+ else
40
+ raise VerificationFailed.new("Pattern does not match")
41
+ end
42
+
43
+ # Sig looks ok
44
+ return out
45
+ end
46
+ end
47
+
48
+ def clearsign(data)
49
+ gpg_cmd = %W{gpg --batch --clearsign -u #{key_id}} + keyring_args + ['-']
50
+ Subprocess.check_call(gpg_cmd,
51
+ :stdin => Subprocess::PIPE,
52
+ :stdout => Subprocess::PIPE) do |child|
53
+ out, _ = child.communicate(data)
54
+ return out
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def keyring_args
61
+ [
62
+ '--no-default-keyring',
63
+ '--keyring', keyring_file('pubring.gpg'),
64
+ '--secret-keyring', keyring_file('secring.gpg'),
65
+ '--trustdb-name', keyring_file('trustdb.gpg'),
66
+ ]
67
+ end
68
+
69
+ def keyring_file(name)
70
+ File.join(@keyring_directory, name)
71
+ end
72
+ end
73
+ end
@@ -8,6 +8,7 @@ module Reyes
8
8
  DefaultGenerationPath = "/tmp/reyes.u#{Process.euid}.generation"
9
9
 
10
10
  attr_reader :value
11
+ attr_reader :mtime
11
12
 
12
13
  # @param path [String] Path to generation file
13
14
  #
@@ -52,8 +53,10 @@ module Reyes
52
53
  @fh = open(@path)
53
54
  if @fh.size == 0
54
55
  @value = 0
56
+ @mtime = Time.at(0)
55
57
  else
56
58
  @value = load_value
59
+ @mtime = @fh.mtime
57
60
  end
58
61
  log.debug("Loaded generation value #{@value.inspect}")
59
62
  end
@@ -0,0 +1,32 @@
1
+ module Reyes
2
+ class S3Loader
3
+
4
+ include Chalk::Log
5
+
6
+ def initialize(aws, config)
7
+ @aws = aws
8
+ @config = Reyes::Config.new(config)
9
+
10
+ log.info("Initialized S3Loader: #{bucket.inspect}/#{path.inspect}")
11
+ end
12
+
13
+ def fetch_rules
14
+ @aws.s3.buckets[bucket].objects[path].read
15
+ end
16
+
17
+ def upload_rules(data)
18
+ @aws.s3.buckets[bucket].objects[path].write(data)
19
+ end
20
+
21
+ private
22
+
23
+ def bucket
24
+ @config.aws_config.fetch("s3").fetch("bucket")
25
+ end
26
+
27
+ def path
28
+ @config.aws_config.fetch("s3").fetch("path")
29
+ end
30
+
31
+ end
32
+ end
data/lib/reyes/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Reyes
2
- VERSION = '0.1.1' unless defined?(self::VERSION)
2
+ VERSION = '0.2.0' unless defined?(self::VERSION)
3
3
  end
data/reyes.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |gem|
24
24
 
25
25
  TO DO
26
26
  EOM
27
- gem.homepage = ""
27
+ gem.homepage = "https://github.com/stripe/reyes/"
28
28
 
29
29
  gem.files = list_files
30
30
  gem.executables = find_binaries
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.1.1
4
+ version: 0.2.0
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-19 00:00:00.000000000 Z
12
+ date: 2015-02-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -122,13 +122,15 @@ files:
122
122
  - lib/reyes/group_tools.rb
123
123
  - lib/reyes/ipset.rb
124
124
  - lib/reyes/iptables.rb
125
+ - lib/reyes/pgp_wrapper.rb
125
126
  - lib/reyes/run_generation.rb
126
127
  - lib/reyes/run_manager.rb
128
+ - lib/reyes/s3_loader.rb
127
129
  - lib/reyes/set_manager.rb
128
130
  - lib/reyes/utils.rb
129
131
  - lib/reyes/version.rb
130
132
  - reyes.gemspec
131
- homepage: ''
133
+ homepage: https://github.com/stripe/reyes/
132
134
  licenses: []
133
135
  metadata: {}
134
136
  post_install_message: