reyes 0.1.1 → 0.2.0

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