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 +8 -8
- data/bin/reyes +66 -1
- data/config.yaml.example +7 -0
- data/lib/reyes.rb +2 -0
- data/lib/reyes/aws_manager.rb +12 -0
- data/lib/reyes/config.rb +4 -0
- data/lib/reyes/fake_aws.rb +4 -5
- data/lib/reyes/group_manager.rb +10 -0
- data/lib/reyes/pgp_wrapper.rb +73 -0
- data/lib/reyes/run_generation.rb +3 -0
- data/lib/reyes/s3_loader.rb +32 -0
- data/lib/reyes/version.rb +1 -1
- data/reyes.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTZhZTI3NWU5Yjk3ZWE1NWI4MzlkYjVjMzg1NDg0NGYwNTAxMzU0OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzE1OGRiMzYxMWE2OWQwNDdhNDc4ZGFhYThlMTZmZDMyMGI1MGQ4NQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Y2NhNTZjZDI3NTIzOGFhMzJmY2EyY2JhNjdhZWE1OTQ1OTZlM2Q2ZjBhOGUy
|
10
|
+
MzQ5NWRjYzAzNjk2NGZiZTFkYjI5MDczOGZhODA0NTZmYmZmZTZjN2E3ZjM4
|
11
|
+
NmIyZGU4YzBjZTI5ZDY0MTVjODI4NTY4ZGJkNjc0ZjYyYWVhZDE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
data/lib/reyes.rb
CHANGED
data/lib/reyes/aws_manager.rb
CHANGED
@@ -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
data/lib/reyes/fake_aws.rb
CHANGED
@@ -5,11 +5,10 @@ module Reyes
|
|
5
5
|
class FakeAws
|
6
6
|
include Chalk::Log
|
7
7
|
|
8
|
-
# @param
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
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)
|
data/lib/reyes/group_manager.rb
CHANGED
@@ -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
|
data/lib/reyes/run_generation.rb
CHANGED
@@ -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
data/reyes.gemspec
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.
|
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-
|
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:
|