muchkeys 0.3.3 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/muchkeys/cli/validation.rb +17 -0
- data/lib/muchkeys/cli/validator.rb +34 -9
- data/lib/muchkeys/cli.rb +65 -24
- data/lib/muchkeys/errors.rb +4 -3
- data/lib/muchkeys/version.rb +1 -1
- data/lib/muchkeys.rb +43 -53
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b1691f5996a3bbe70a2dc689f6168b08859f5a7
|
4
|
+
data.tar.gz: 1d49e1b24e67b72c55adf5ed724e0269e0ac0a0a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4ec21f49f82ce55a894ce5fbfaf8b5a567fe56dd0313576e17d7a1cc553dd08a6895877df76176694e90e890e8d6063ba146f1032e482ed437bbca2c54a9a3c
|
7
|
+
data.tar.gz: c49e8fe3eab027bfe23c032eba3bae91dd54e9320035221a5fac78471150f4f646cb9bd8628730a20298a799ca64dfa8e0fab4d61f2bd97037f721ab6b00c300
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative "../errors"
|
2
|
+
require_relative "./validation"
|
2
3
|
|
3
4
|
class MuchKeys::CLI::Validator
|
4
5
|
|
@@ -6,24 +7,48 @@ class MuchKeys::CLI::Validator
|
|
6
7
|
raise MuchKeys::CLIOptionsError, primary_mode_error_message unless options_has_one_mode?(options)
|
7
8
|
end
|
8
9
|
|
9
|
-
def self.
|
10
|
-
|
11
|
-
|
10
|
+
def self.validate_encrypt_options(options)
|
11
|
+
validation = MuchKeys::CLI::Validation.new
|
12
|
+
if !options[:file] || !options[:public_key]
|
13
|
+
validation.errors << "--decrypt needs the --file and --public_key set."
|
14
|
+
end
|
15
|
+
|
16
|
+
validation
|
12
17
|
end
|
13
18
|
|
14
|
-
def self.
|
19
|
+
def self.validate_decrypt_options(options)
|
20
|
+
validation = MuchKeys::CLI::Validation.new
|
21
|
+
if options[:consul_key] && options[:public_key] && options[:private_key]
|
22
|
+
validate_automatic_certificate(validation, options)
|
23
|
+
else
|
24
|
+
validation.errors << "--decrypt needs the --consul_key, --public_key and --private_key set."
|
25
|
+
end
|
26
|
+
|
27
|
+
validation
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.validate_automatic_certificate(chained_validation, options)
|
31
|
+
# i won't mutate chained_validation on principle
|
32
|
+
validation = MuchKeys::CLI::Validation.new
|
33
|
+
validation.errors = chained_validation.errors.dup
|
15
34
|
key_name = options[:consul_key]
|
16
|
-
abort "--decrypt needs the --consul_key option passed." if !key_name
|
17
35
|
|
18
36
|
if !secret_adapter.auto_certificates_exist_for_key?(key_name)
|
19
37
|
certfile_expected = secret_adapter.certfile_name(key_name)
|
20
|
-
|
21
|
-
|
38
|
+
validation.errors << "--decrypt needs the --public_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:public_key]
|
39
|
+
validation.errors << "--decrypt needs the --private_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:private_key]
|
22
40
|
end
|
41
|
+
|
42
|
+
validation
|
23
43
|
end
|
24
44
|
|
25
|
-
def self.
|
26
|
-
|
45
|
+
def self.validate_plain_options(options)
|
46
|
+
validation = MuchKeys::CLI::Validation.new
|
47
|
+
if !options[:consul_key]
|
48
|
+
validation.errors << "--plain needs the --consul_key option passed."
|
49
|
+
end
|
50
|
+
|
51
|
+
validation
|
27
52
|
end
|
28
53
|
|
29
54
|
def self.options_has_one_mode?(options)
|
data/lib/muchkeys/cli.rb
CHANGED
@@ -8,18 +8,8 @@ module MuchKeys
|
|
8
8
|
# I'd like to not be doing this kind of check
|
9
9
|
# But this is tricky because we need to know
|
10
10
|
# later if help was invoked so we don't execute run with blank options.
|
11
|
-
@
|
11
|
+
@cli_should_exit = false
|
12
12
|
parsed = parse_options(arguments)
|
13
|
-
return if @help_invoked
|
14
|
-
|
15
|
-
begin
|
16
|
-
set_primary_mode
|
17
|
-
rescue MuchKeys::CLIOptionsError => e
|
18
|
-
puts e.message
|
19
|
-
puts @opts
|
20
|
-
end
|
21
|
-
|
22
|
-
configure_muchkeys
|
23
13
|
end
|
24
14
|
|
25
15
|
def parse_options(arguments)
|
@@ -32,14 +22,11 @@ module MuchKeys
|
|
32
22
|
o.string "--private_key", "Location of your private key"
|
33
23
|
o.string "--public_key", "Location of your public key"
|
34
24
|
|
35
|
-
|
36
|
-
o.string "--file", "File to encrypt"
|
25
|
+
o.string "-f", "--file", "File to encrypt"
|
37
26
|
o.string "-c", "--consul_key", "Consul key to decrypt"
|
38
27
|
|
39
|
-
o.
|
40
|
-
|
41
|
-
@help_invoked = true
|
42
|
-
end
|
28
|
+
o.bool "-h", "--help", "Shows usage"
|
29
|
+
o.bool "-v", "--version", "Prints the version number"
|
43
30
|
end
|
44
31
|
end
|
45
32
|
|
@@ -58,20 +45,53 @@ module MuchKeys
|
|
58
45
|
|
59
46
|
# this guy figures out the appropriate action to take given CLI options
|
60
47
|
def run
|
61
|
-
|
48
|
+
|
49
|
+
check_for_early_exit_actions
|
50
|
+
return if @cli_should_exit
|
51
|
+
|
52
|
+
begin
|
53
|
+
set_primary_mode
|
54
|
+
rescue MuchKeys::CLIOptionsError => e
|
55
|
+
puts e.message
|
62
56
|
puts @opts
|
63
|
-
return
|
64
57
|
end
|
65
58
|
|
59
|
+
return if @cli_should_exit
|
60
|
+
|
61
|
+
configure_muchkeys
|
62
|
+
|
66
63
|
if @opts[:encrypt]
|
67
|
-
|
68
|
-
|
64
|
+
options = { file: @opts[:file], public_key: @opts[:public_key] }
|
65
|
+
validation = MuchKeys::CLI::Validator.validate_encrypt_options(options)
|
66
|
+
|
67
|
+
if validation.valid?
|
68
|
+
encrypt(@opts[:file], @opts[:public_key])
|
69
|
+
else
|
70
|
+
puts validation.errors
|
71
|
+
return
|
72
|
+
end
|
69
73
|
elsif @opts[:decrypt]
|
70
|
-
|
71
|
-
|
74
|
+
options = { consul_key: @opts[:consul_key], public_key: @opts[:public_key], private_key: @opts[:private_key] }
|
75
|
+
validation = MuchKeys::CLI::Validator.validate_decrypt_options(options)
|
76
|
+
|
77
|
+
if validation.valid?
|
78
|
+
decrypt(@opts[:consul_key], @opts[:public_key], @opts[:private_key])
|
79
|
+
else
|
80
|
+
puts validation.errors
|
81
|
+
return
|
82
|
+
end
|
72
83
|
elsif @opts[:plain]
|
73
|
-
|
84
|
+
options = { consul_key: @opts[:consul_key] }
|
85
|
+
validation = MuchKeys::CLI::Validator.validate_plain_options(options)
|
86
|
+
|
87
|
+
if validation.valid?
|
88
|
+
plain(@opts[:consul_key])
|
89
|
+
else
|
90
|
+
puts validation.errors
|
91
|
+
return
|
92
|
+
end
|
74
93
|
end
|
94
|
+
|
75
95
|
end
|
76
96
|
|
77
97
|
def encrypt(file, public_key)
|
@@ -87,6 +107,17 @@ module MuchKeys
|
|
87
107
|
puts MuchKeys.fetch_key(consul_key)
|
88
108
|
end
|
89
109
|
|
110
|
+
def print_version_number
|
111
|
+
puts MuchKeys::VERSION
|
112
|
+
@cli_should_exit = true
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_help
|
116
|
+
# this is how the slop gem prints help
|
117
|
+
puts @opts
|
118
|
+
@cli_should_exit = true
|
119
|
+
end
|
120
|
+
|
90
121
|
|
91
122
|
private
|
92
123
|
def select_primary_mode(options)
|
@@ -99,5 +130,15 @@ module MuchKeys
|
|
99
130
|
MuchKeys::Secret
|
100
131
|
end
|
101
132
|
|
133
|
+
def check_for_early_exit_actions
|
134
|
+
if @opts[:help]
|
135
|
+
print_help
|
136
|
+
end
|
137
|
+
|
138
|
+
if @opts[:version]
|
139
|
+
print_version_number
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
102
143
|
end
|
103
144
|
end
|
data/lib/muchkeys/errors.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module MuchKeys
|
2
|
-
InvalidKey
|
3
|
-
CLIOptionsError
|
4
|
-
NoKeysSet
|
2
|
+
InvalidKey = Class.new(StandardError)
|
3
|
+
CLIOptionsError = Class.new(StandardError)
|
4
|
+
NoKeysSet = Class.new(StandardError)
|
5
|
+
UnknownApplication = Class.new(StandardError)
|
5
6
|
end
|
data/lib/muchkeys/version.rb
CHANGED
data/lib/muchkeys.rb
CHANGED
@@ -10,6 +10,10 @@ require "net/http"
|
|
10
10
|
|
11
11
|
module MuchKeys
|
12
12
|
|
13
|
+
# revealing intention
|
14
|
+
module BlankKey; false; end
|
15
|
+
|
16
|
+
|
13
17
|
class << self
|
14
18
|
attr_accessor :configuration
|
15
19
|
|
@@ -20,37 +24,22 @@ module MuchKeys
|
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
|
-
|
24
|
-
if MuchKeys.configuration.search_paths
|
25
|
-
search_paths = MuchKeys.configuration.search_paths.collect do |path|
|
26
|
-
"#{path}/#{key_name}"
|
27
|
-
end
|
28
|
-
else
|
29
|
-
search_paths = [
|
30
|
-
"git/#{application_name}/secrets/#{key_name}",
|
31
|
-
"git/#{application_name}/config/#{key_name}",
|
32
|
-
"git/shared/secrets/#{key_name}",
|
33
|
-
"git/shared/config/#{key_name}"
|
34
|
-
]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
27
|
+
# this is the entry point to the ENV-like object that devs will use
|
38
28
|
def find_first(key_name)
|
39
|
-
return ENV[key_name] unless ENV[key_name].nil?
|
40
|
-
|
41
29
|
unless MuchKeys.configuration.search_paths
|
42
|
-
application_name
|
43
|
-
return false if !application_name
|
30
|
+
raise MuchKeys::UnknownApplication, "Can't detect app name and application_name isn't set." if !application_name
|
44
31
|
end
|
45
32
|
|
46
|
-
|
33
|
+
consul_paths_to_search = search_order(application_name, key_name)
|
47
34
|
|
35
|
+
# search consul in a specific order until we find something
|
48
36
|
response = nil
|
49
|
-
|
37
|
+
consul_paths_to_search.detect do |consul_path|
|
50
38
|
response = fetch_key(consul_path)
|
39
|
+
response if response != MuchKeys::BlankKey
|
51
40
|
end
|
52
41
|
|
53
|
-
if
|
42
|
+
if response == BlankKey
|
54
43
|
raise MuchKeys::NoKeysSet, "Bailing. Consul isn't set with any keys for #{key_name}."
|
55
44
|
end
|
56
45
|
|
@@ -72,46 +61,47 @@ module MuchKeys
|
|
72
61
|
response = fetch_plain_key(key_name)
|
73
62
|
end
|
74
63
|
|
75
|
-
# empty and unset keys from consul are empty strings, so return false
|
76
|
-
# here TODO: UnsetKey would be better here.
|
77
|
-
return false if response == ""
|
78
|
-
|
79
64
|
# otherwise, we got consul data so return that
|
80
65
|
response
|
81
66
|
end
|
82
67
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
raise MuchKeys::InvalidKey
|
68
|
+
def search_order(application_name, key_name)
|
69
|
+
if MuchKeys.configuration.search_paths
|
70
|
+
search_paths = MuchKeys.configuration.search_paths.collect do |path|
|
71
|
+
"#{path}/#{key_name}"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
search_paths = [
|
75
|
+
"git/#{application_name}/secrets/#{key_name}",
|
76
|
+
"git/#{application_name}/config/#{key_name}",
|
77
|
+
"git/shared/secrets/#{key_name}",
|
78
|
+
"git/shared/config/#{key_name}"
|
79
|
+
]
|
96
80
|
end
|
81
|
+
|
82
|
+
search_paths
|
97
83
|
end
|
98
84
|
|
99
85
|
|
100
86
|
private
|
87
|
+
def consul_url(key_name)
|
88
|
+
URI("#{configuration.consul_url}/v1/kv/#{key_name}?raw")
|
89
|
+
end
|
90
|
+
|
101
91
|
def fetch_plain_key(key_name)
|
102
92
|
url = consul_url(key_name)
|
103
93
|
response = Net::HTTP.get_response url
|
104
|
-
|
105
|
-
|
94
|
+
|
95
|
+
return MuchKeys::BlankKey if !response.body || response.body.empty?
|
96
|
+
response.body
|
106
97
|
end
|
107
98
|
|
108
99
|
def fetch_secret_key(key_name, public_pem=nil, private_pem=nil)
|
109
100
|
result = fetch_plain_key(key_name)
|
110
|
-
# FIXME: omg use a class like MuchKeys::UnsetKey instead of "" as a
|
111
|
-
# return value -- there are other places like this too.
|
112
101
|
|
113
102
|
# we hit a key that doesn't exist, so don't try to decrypt it
|
114
|
-
return
|
103
|
+
return MuchKeys::BlankKey if result == MuchKeys::BlankKey
|
104
|
+
|
115
105
|
secret_adapter.decrypt_string(result, public_pem, private_pem)
|
116
106
|
end
|
117
107
|
|
@@ -128,18 +118,15 @@ module MuchKeys
|
|
128
118
|
end
|
129
119
|
|
130
120
|
# Detecting Rails app names is a known quantity.
|
131
|
-
#
|
132
|
-
def
|
121
|
+
# Rack apps need to set the name through config.
|
122
|
+
def application_name
|
133
123
|
return configuration.application_name if configuration.application_name
|
134
124
|
|
135
|
-
# Rails.application is "Monorail::Application".
|
136
125
|
if defined?(Rails)
|
126
|
+
# Rails.application looks something like "Monorail::Application"
|
137
127
|
application_name = Rails.application.class.to_s.split("::").first
|
138
|
-
elsif defined?(Rack)
|
139
|
-
application_name = "Asset_Server"
|
140
128
|
else
|
141
|
-
|
142
|
-
return
|
129
|
+
return false
|
143
130
|
end
|
144
131
|
|
145
132
|
snakecase(application_name)
|
@@ -164,8 +151,11 @@ MuchKeys.configuration ||= MuchKeys::Configuration.new
|
|
164
151
|
# This interface looks like ENV which is more friendly to devs
|
165
152
|
class MUCHKEYS
|
166
153
|
|
167
|
-
def self.[](
|
168
|
-
|
154
|
+
def self.[](key_name)
|
155
|
+
# if an ENV key is set, use that first
|
156
|
+
return ENV[key_name] unless ENV[key_name].nil?
|
157
|
+
|
158
|
+
MuchKeys.find_first(key_name)
|
169
159
|
end
|
170
160
|
|
171
161
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: muchkeys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat O'Brien
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-04
|
12
|
+
date: 2016-05-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: slop
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- exe/muchkeys
|
130
130
|
- lib/muchkeys.rb
|
131
131
|
- lib/muchkeys/cli.rb
|
132
|
+
- lib/muchkeys/cli/validation.rb
|
132
133
|
- lib/muchkeys/cli/validator.rb
|
133
134
|
- lib/muchkeys/configuration.rb
|
134
135
|
- lib/muchkeys/errors.rb
|