muchkeys 0.0.1 → 0.3.3
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 +4 -4
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/Guardfile +10 -15
- data/README.md +248 -10
- data/Rakefile +17 -2
- data/exe/muchkeys +6 -0
- data/lib/muchkeys/cli/validator.rb +41 -0
- data/lib/muchkeys/cli.rb +103 -0
- data/lib/muchkeys/configuration.rb +30 -0
- data/lib/muchkeys/errors.rb +3 -1
- data/lib/muchkeys/key_validator.rb +43 -0
- data/lib/muchkeys/secret.rb +66 -0
- data/lib/muchkeys/version.rb +1 -1
- data/lib/muchkeys.rb +146 -34
- data/muchkeys.gemspec +12 -7
- metadata +46 -40
- data/bin/console +0 -14
- data/bin/setup +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ff8551deefce6e7a842d98364ea5667fa4532e3
|
4
|
+
data.tar.gz: ff2729f7f21a56f51bb9998729c80a7c96d87ec5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d0541d5c159955b22471d808fb2edb0f042e5b3a03fe725deb7ea7c041a3ed5d92f1f5a120223531742da56478976ecde08e833ae0399564a3f4145b26c67e5
|
7
|
+
data.tar.gz: 413f9778cd45d3aed726c7c23c685a032259f870c021c62b2bbd7f72b9f73f1d307ef568ed12263bc873feb3e5dc2524fd5e51d2803012833e6b2fcc947528a2
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/Guardfile
CHANGED
@@ -1,17 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
guard_options = {
|
2
|
+
cmd: "bundle exec rspec --color --format=doc",
|
3
|
+
all_after_pass: false,
|
4
|
+
all_after_fail: false,
|
5
|
+
all_on_start: false
|
6
|
+
}
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
watch(
|
8
|
-
watch(
|
9
|
-
watch(rspec.spec_files)
|
10
|
-
|
11
|
-
# Ruby files
|
12
|
-
ruby = dsl.ruby
|
13
|
-
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
-
|
15
|
-
# TODO: use the new rspec dsl here, but how?
|
16
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
8
|
+
guard :rspec, guard_options do
|
9
|
+
watch(%r{^spec/.+_spec\.rb$})
|
10
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
11
|
+
watch('spec/spec_helper.rb') { "spec" }
|
17
12
|
end
|
data/README.md
CHANGED
@@ -1,11 +1,35 @@
|
|
1
1
|
# MuchKeys
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
─────────▄──────────────▄
|
4
|
+
────────▌▒█───────────▄▀▒▌
|
5
|
+
────────▌▒▒▀▄───────▄▀▒▒▒▐
|
6
|
+
───────▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐
|
7
|
+
─────▄▄▀▒▒▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐
|
8
|
+
───▄▀▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀██▀▒▌
|
9
|
+
──▐▒▒▒▄▄▄▒▒▒▒▒▒▒▒▒▒▒▒▒▀▄▒▒▌
|
10
|
+
──▌▒▒▐▄█▀▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐
|
11
|
+
─▐▒▒▒▒▒▒▒▒▒▒▒▌██▀▒▒▒▒▒▒▒▒▀▄▌
|
12
|
+
─▌▒▀▄██▄▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌
|
13
|
+
─▌▀▐▄█▄█▌▄▒▀▒▒▒▒▒▒░░░░░░▒▒▒▐
|
14
|
+
▐▒▀▐▀▐▀▒▒▄▄▒▄▒▒▒▒▒░░░░░░▒▒▒▒▌
|
15
|
+
▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒░░░░░░▒▒▒▐
|
16
|
+
─▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌
|
17
|
+
─▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▐
|
18
|
+
──▀▄▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄▒▒▒▒▌
|
19
|
+
────▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀
|
20
|
+
───▐▀▒▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀
|
21
|
+
──▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀▀
|
6
22
|
|
7
|
-
|
8
|
-
|
23
|
+
|
24
|
+
MuchKeys lets you store your application keys in consul and then leverages many conventions to create a
|
25
|
+
pleasant API. Keys in this context can mean application settings, knobs, dials, passwords, api keys
|
26
|
+
or anything. It's primary use is in production where it will check to see if a ENV variable exists first and then
|
27
|
+
search consul for a key based on an opinionated hierarchy convention.
|
28
|
+
|
29
|
+
You will want to read below about the convention and assumptions first to see if this works for
|
30
|
+
you.
|
31
|
+
|
32
|
+
MuchKeys also has a way of using encrypted secrets stored in consul. MuchKeys has a command line interface to help you encrypt or read secrets stored in consul.
|
9
33
|
|
10
34
|
|
11
35
|
## Installation
|
@@ -18,28 +42,242 @@ gem 'muchkeys'
|
|
18
42
|
|
19
43
|
And then execute:
|
20
44
|
|
21
|
-
|
45
|
+
```
|
46
|
+
$ bundle
|
47
|
+
```
|
22
48
|
|
23
49
|
Or install it yourself as:
|
24
50
|
|
25
|
-
|
51
|
+
```
|
52
|
+
$ gem install muchkeys
|
53
|
+
```
|
54
|
+
|
26
55
|
|
27
56
|
## Usage
|
28
57
|
|
58
|
+
Use a snippet like this below in your YAML config files and anywhere else to use
|
59
|
+
a central configuration store while retaining control of your local development environment.
|
60
|
+
|
61
|
+
```
|
62
|
+
<%= MUCHKEYS['widget_api_key'] %>
|
63
|
+
```
|
64
|
+
|
65
|
+
Now the `widget_api_key` is coming from a central location. Devs can override `widget_api_key` with an ENV setting. `export WIDGET_API_KEY="development_key76"` MuchKeys defers to ENV when
|
66
|
+
it sees one set.
|
67
|
+
|
68
|
+
MuchKeys will look in consul (a key/value store) for keys (settings/secrets/knobs to turn).
|
69
|
+
It searches in an order of convention. It is also assuming you want to use git2consul
|
70
|
+
to enable your developers to add their own keys. Git2consul syncs a git repo to consul.
|
71
|
+
|
72
|
+
So let's walk through what happens when a developer is adding a new feature to an app.
|
73
|
+
The developer is integrating an API from reddit into an app called `feed_reader`. So now they have a reddit API key
|
74
|
+
and this is something new to the app.
|
75
|
+
|
76
|
+
* The developer adds twitter_api_key to `.env` from the dotenv gem to control their own development
|
77
|
+
environment. Staging and production have no idea this has happened.
|
78
|
+
* The developer adds/commits a file to `<git2consul repo>/feed_reader/twitter_api_key`
|
79
|
+
* git2consul is sync'd to consul and their new key is there. (This might happen through cron or some other way)
|
80
|
+
* They use their key in a configuration file or in the app: `<%= MUCHKEYS['redis_api_key'] %>`
|
81
|
+
* When they deploy to staging/production the key is there and the `feed_reader` app doesn't explode.
|
82
|
+
* Ops wasn't involved. Developers are empowered.
|
83
|
+
|
84
|
+
|
85
|
+
MuchKeys does this magic through a search order. It searches consul for the key
|
86
|
+
in certain order: (notice that the key isn't prefixed when you look up `twitter_api_key` with
|
87
|
+
`MUCHKEYS['twitter_api_key']`)
|
88
|
+
|
89
|
+
It will search a consul hierarchy looking for `twitter_api_key` in this search order:
|
90
|
+
|
91
|
+
```
|
92
|
+
git/feed_reader/secrets
|
93
|
+
git/feed_reader/config
|
94
|
+
git/shared/secrets
|
95
|
+
git/shared/config
|
96
|
+
```
|
97
|
+
|
98
|
+
The above `feed_reader` is detected from `Rails.application`. If you don't have a Rails app, then you can set
|
99
|
+
the application name in the configure block:
|
100
|
+
|
29
101
|
```
|
102
|
+
MuchKeys.configure do |config|
|
103
|
+
config.application_name = "rack_app"
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
The order and paths that it searches is configurable:
|
108
|
+
|
109
|
+
```
|
110
|
+
MuchKeys.configure do |config|
|
111
|
+
config.search_paths = %W(
|
112
|
+
app_name/keys app_name/secrets shared/keys shared/secrets
|
113
|
+
)
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
This would look for keys and settings in consul under these paths instead:
|
118
|
+
|
119
|
+
```
|
120
|
+
app_name/keys
|
121
|
+
app_name/secrets
|
122
|
+
shared/keys
|
123
|
+
shared/secrets
|
124
|
+
```
|
125
|
+
|
126
|
+
Anything that has /secrets/ in it is going to be assumed to be a secret. There is an assumption that you have
|
127
|
+
your keys and secrets organized like this (one deep nesting). This is configurable.
|
128
|
+
|
129
|
+
```
|
130
|
+
# if you don't put your secrets in something like shared/secrets/
|
131
|
+
# but shared/passwords/
|
132
|
+
MuchKeys.configure do |config|
|
133
|
+
config.secrets_path_hint = "passwords/"
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
### Using in a Rails App
|
138
|
+
|
139
|
+
Add to your Gemfile.
|
140
|
+
```
|
141
|
+
gem 'muchkeys'
|
142
|
+
```
|
143
|
+
|
144
|
+
Run `bundle install`.
|
145
|
+
|
146
|
+
If you don't need to change your hostname (production you do not need to) or any other settings, then you are
|
147
|
+
done. If you need to override defaults, create an initializer.
|
148
|
+
|
149
|
+
```
|
150
|
+
# config/initializers/muchkeys.rb
|
151
|
+
MuchKeys.configure do |config|
|
152
|
+
# your settings if desired
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
Then, use in YAML files, in the app or in other initializers.
|
157
|
+
|
158
|
+
```
|
159
|
+
# example of centralizing a database password in database.yml
|
160
|
+
# blog/config/database.yml
|
161
|
+
production:
|
162
|
+
<<: *default
|
163
|
+
username: MUCHKEYS['database_user'] # set in consul at git/blog/config/database_user
|
164
|
+
password: MUCHKEYS['database_password'] # set in consul at git/blog/secrets/database_password
|
165
|
+
```
|
166
|
+
|
167
|
+
In the above example, `database_password` needs to be encrypted with an SSL certificate
|
168
|
+
and then set in consul (paste the PEM text). The private and public key are needed to decrypt
|
169
|
+
and these keys should be stored in `<deploy_user_home>/.keys/blog.pem`. With these conventions
|
170
|
+
set once, you won't have to do anything else.
|
171
|
+
|
172
|
+
|
173
|
+
### Encrypted Secrets
|
174
|
+
|
175
|
+
Generate an SSL cert or use an existing one. You will need the private and public key to encrypt and the public key to decrypt.
|
176
|
+
|
177
|
+
```bash
|
178
|
+
openssl req -new -newkey rsa:4096 -nodes -x509 -keyout /tmp/self_signed_test.pem -out /tmp/self_signed_test.pem
|
179
|
+
```
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
MuchKeys.configure do |config|
|
183
|
+
config.public_key = "/tmp/self_signed_test.pem"
|
184
|
+
config.private_key = "/tmp/self_signed_test.pem"
|
185
|
+
end
|
186
|
+
|
187
|
+
puts MuchKeys::Secret.encrypt_string("bacon is just ok").to_s
|
188
|
+
# => -----BEGIN PKCS7-----
|
189
|
+
# => MIICuwYJKoZIhvcNAQcDoIICrDCCAqgCAQAxggJuMIICagIBADBSMEUxCzAJ ...
|
190
|
+
# => ...
|
191
|
+
|
192
|
+
# Copy and paste this into consul under a key: ie: secrets/fake
|
193
|
+
|
194
|
+
# Now you can fetch the secret with the same public key.
|
195
|
+
MuchKeys::Secret.get_consul_secret('secrets/fake')
|
196
|
+
# => bacon is just ok
|
197
|
+
```
|
198
|
+
|
199
|
+
Inside your app:
|
200
|
+
```ruby
|
201
|
+
# inside a rails initializer or other setup file you have
|
202
|
+
MuchKeys.configure do |config|
|
203
|
+
config.consul_url = "http://myrealhost" # Default is "http://localhost:8500"
|
204
|
+
config.public_key = "path to public .pem" # this is only required if you are encrypting secrets
|
205
|
+
config.private_key = "path to private .pem" # this is only required if you are encrypting secrets
|
206
|
+
end
|
207
|
+
|
208
|
+
# then inside your YAML or app:
|
209
|
+
MuchKeys.fetch_key('number_of_threads')
|
210
|
+
# goes to git/config/myapp/number_of_threads in consul
|
211
|
+
```
|
212
|
+
|
213
|
+
|
214
|
+
## Automagic Certificates
|
215
|
+
|
216
|
+
MuchKeys can find certs automatically in `~/.keys`. `MuchKeys.fetch_key("git/waffles/secrets/a_password")` will try to decrypt `a_password`
|
217
|
+
with a cert called `~/.keys/waffles.pem`. The private key can be in the same file but should be protected
|
218
|
+
with file permissions `0600`.
|
219
|
+
|
220
|
+
|
221
|
+
## CLI
|
222
|
+
|
223
|
+
Example of decrypted an encrypted key:
|
224
|
+
|
225
|
+
```
|
226
|
+
$ muchkeys -d --public_key=~/.keys/staging.pem --private_key=~/.keys/staging.pem --consul_url=http://consul.domain:8500 --consul_key git/pants/secrets/some_password
|
227
|
+
<unencrypted secret is displayed>
|
228
|
+
```
|
229
|
+
|
230
|
+
### Other Usage
|
231
|
+
|
232
|
+
You can fetch individual keys if you want.
|
233
|
+
|
234
|
+
```bash
|
30
235
|
ruby -e 'require "muchkeys"; puts MuchKeys.fetch_key("mail_server")'
|
31
236
|
# => smtp.example.com (from consul)
|
32
237
|
```
|
33
238
|
|
34
|
-
```
|
239
|
+
```bash
|
35
240
|
mail_server=muffin.ninja.local ruby -e 'require "muchkeys"; puts MuchKeys.fetch_key("mail_server")'
|
36
241
|
# => muffin.ninja.local (from ENV)
|
37
242
|
```
|
38
243
|
|
39
|
-
|
244
|
+
### Keys to Store
|
245
|
+
|
246
|
+
You will probably just want to store things that change between environments in consul.
|
247
|
+
If `database_pool_size` never changes between development and production then don't store it in consul.
|
248
|
+
If `sidekiq_number_of_workers` is 1 in development, 2 in staging and 16 in production, then this is a
|
249
|
+
good thing to store in consul because you'll have a central place that all app servers can
|
250
|
+
read from.
|
251
|
+
|
252
|
+
You could store things in rails' `environments/` path but then you'll need to do a deploy
|
253
|
+
to change a setting and some things might be shared between apps like URLs, api keys, secrets and scaling settings.
|
254
|
+
|
255
|
+
|
256
|
+
### Limits and Caveats
|
257
|
+
|
258
|
+
Searching multiple paths in consul is because consul is hierarchical (or can be) and ENV is not.
|
259
|
+
Making an easy to use API that looks and behaves like `ENV` was one of the goals to make
|
260
|
+
it more familiar to developers. So worst case, by default, MuchKeys will search consul 4 times
|
261
|
+
to find a key.
|
262
|
+
|
263
|
+
Encrypted secrets can be slightly more clunky. Developers may not have signing keys,
|
264
|
+
in that case, an admin or someone from ops maybe has to manage the secrets.
|
265
|
+
|
266
|
+
|
267
|
+
### Compared to Other Tools
|
268
|
+
|
269
|
+
It's sort of the opposite of `envconsul` or similar to `dotenv` designed
|
270
|
+
for use in production.
|
271
|
+
|
272
|
+
* Dotenv doesn't centralize keys.
|
273
|
+
* envconsul has a auto restart the launched process on change feature, this project does not.
|
274
|
+
|
275
|
+
|
276
|
+
### Future
|
277
|
+
|
278
|
+
It'd be nice if this project wasn't so tied to consul. I don't think it's impossible to decouple it. Etcd basically behaves the same afaik so we could add an etcd adapter.
|
40
279
|
|
41
280
|
|
42
281
|
## License
|
43
282
|
|
44
283
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
45
|
-
|
data/Rakefile
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
|
4
|
+
namespace :spec do
|
5
|
+
desc "Run Unit Tests"
|
6
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
7
|
+
t.pattern = 'spec/lib/**/*_spec.rb'
|
8
|
+
end
|
5
9
|
|
6
|
-
|
10
|
+
desc "Run Feature/Integration Tests"
|
11
|
+
RSpec::Core::RakeTask.new(:features) do |t|
|
12
|
+
t.pattern = 'spec/features/**/*.rb'
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run All Tests"
|
16
|
+
RSpec::Core::RakeTask.new(:all) do |t|
|
17
|
+
t.pattern = 'spec/lib/**/*_spec.rb, spec/features/**/*.rb'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => "spec:all"
|
data/exe/muchkeys
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative "../errors"
|
2
|
+
|
3
|
+
class MuchKeys::CLI::Validator
|
4
|
+
|
5
|
+
def self.validate_primary_mode_option(options)
|
6
|
+
raise MuchKeys::CLIOptionsError, primary_mode_error_message unless options_has_one_mode?(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.validate_encrypt(options)
|
10
|
+
abort "--encrypt needs the --file option passed." if !options[:file]
|
11
|
+
abort "--encrypt needs the --public_key option passed." if !options[:public_key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.validate_decrypt(options)
|
15
|
+
key_name = options[:consul_key]
|
16
|
+
abort "--decrypt needs the --consul_key option passed." if !key_name
|
17
|
+
|
18
|
+
if !secret_adapter.auto_certificates_exist_for_key?(key_name)
|
19
|
+
certfile_expected = secret_adapter.certfile_name(key_name)
|
20
|
+
abort "--decrypt needs the --public_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:public_key]
|
21
|
+
abort "--decrypt needs the --private_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:private_key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.validate_plain(options)
|
26
|
+
abort "--plain needs the --consul_key option passed." if !options[:consul_key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.options_has_one_mode?(options)
|
30
|
+
[ options[:encrypt], options[:decrypt], options[:plain] ].count(true) == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.primary_mode_error_message
|
34
|
+
"You must pass only one and at least one of the following flags: --encrypt, --decrypt, or --plain"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.secret_adapter
|
38
|
+
MuchKeys::Secret
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/muchkeys/cli.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require "slop"
|
2
|
+
|
3
|
+
module MuchKeys
|
4
|
+
class CLI
|
5
|
+
attr_reader :mode
|
6
|
+
|
7
|
+
def initialize(arguments)
|
8
|
+
# I'd like to not be doing this kind of check
|
9
|
+
# But this is tricky because we need to know
|
10
|
+
# later if help was invoked so we don't execute run with blank options.
|
11
|
+
@help_invoked = false
|
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
|
+
end
|
24
|
+
|
25
|
+
def parse_options(arguments)
|
26
|
+
@opts = Slop.parse arguments do |o|
|
27
|
+
o.bool "-e", "--encrypt", "Encrypt keys from a file to put in consul."
|
28
|
+
o.bool "-d", "--decrypt", "Decrypt keys from consul."
|
29
|
+
o.bool "-p", "--plain", "Fetch plaintext key from consul."
|
30
|
+
|
31
|
+
o.string "--consul_url", "Consul server http address", default: "http://localhost:8500"
|
32
|
+
o.string "--private_key", "Location of your private key"
|
33
|
+
o.string "--public_key", "Location of your public key"
|
34
|
+
|
35
|
+
# add -f
|
36
|
+
o.string "--file", "File to encrypt"
|
37
|
+
o.string "-c", "--consul_key", "Consul key to decrypt"
|
38
|
+
|
39
|
+
o.on "-h", "--help" do
|
40
|
+
# Save the fact we ran help so when run() runs, it prints help and exits.
|
41
|
+
@help_invoked = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_primary_mode(options=@opts)
|
47
|
+
MuchKeys::CLI::Validator.validate_primary_mode_option(options)
|
48
|
+
@mode = select_primary_mode(options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure_muchkeys(options=@opts)
|
52
|
+
MuchKeys.configure do |config|
|
53
|
+
config.consul_url = options[:consul_url]
|
54
|
+
config.public_key = options[:public_key]
|
55
|
+
config.private_key = options[:private_key]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# this guy figures out the appropriate action to take given CLI options
|
60
|
+
def run
|
61
|
+
if @opts[:help]
|
62
|
+
puts @opts
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
if @opts[:encrypt]
|
67
|
+
MuchKeys::CLI::Validator.validate_encrypt(file: @opts[:file], public_key: @opts[:public_key])
|
68
|
+
encrypt(@opts[:file], @opts[:public_key])
|
69
|
+
elsif @opts[:decrypt]
|
70
|
+
MuchKeys::CLI::Validator.validate_decrypt(consul_key: @opts[:consul_key], public_key: @opts[:public_key], private_key: @opts[:private_key])
|
71
|
+
decrypt(@opts[:consul_key], @opts[:public_key], @opts[:private_key])
|
72
|
+
elsif @opts[:plain]
|
73
|
+
plain(@opts[:consul_key])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def encrypt(file, public_key)
|
78
|
+
string_to_encrypt = File.read(file)
|
79
|
+
puts secret_adapter.encrypt_string(string_to_encrypt, public_key)
|
80
|
+
end
|
81
|
+
|
82
|
+
def decrypt(consul_key, public_key, private_key)
|
83
|
+
puts MuchKeys.fetch_key(consul_key, public_key:public_key, private_key:private_key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def plain(consul_key)
|
87
|
+
puts MuchKeys.fetch_key(consul_key)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
private
|
92
|
+
def select_primary_mode(options)
|
93
|
+
possible_primary_modes = { encrypt: options[:encrypt], decrypt: options[:decrypt], plain: options[:plain] }
|
94
|
+
primary_mode = possible_primary_modes.select {|k,v| v == true } # validation has already happened, we can access modes safely
|
95
|
+
primary_mode.keys.first
|
96
|
+
end
|
97
|
+
|
98
|
+
def secret_adapter
|
99
|
+
MuchKeys::Secret
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'muchkeys'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module MuchKeys
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :consul_url, :private_key, :public_key, :application_name, :search_paths, :secrets_hint
|
7
|
+
|
8
|
+
# sensible defaults
|
9
|
+
def initialize
|
10
|
+
@consul_url = "http://localhost:8500"
|
11
|
+
end
|
12
|
+
|
13
|
+
# url parsing sanity check
|
14
|
+
def consul_url=(url)
|
15
|
+
raise URI::InvalidURIError unless url =~ URI::regexp
|
16
|
+
@consul_url = url
|
17
|
+
end
|
18
|
+
|
19
|
+
def attributes
|
20
|
+
{
|
21
|
+
consul_url: @consul_url,
|
22
|
+
private_key: @private_key,
|
23
|
+
public_key: @public_key,
|
24
|
+
application_name: @application_name,
|
25
|
+
search_paths: @search_paths,
|
26
|
+
secrets_hint: @secrets_hint
|
27
|
+
}.delete_if {|k,v| v.nil? }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/muchkeys/errors.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
module MuchKeys
|
2
|
+
class KeyValidator
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# key should pass validation rules
|
7
|
+
def valid? keyname
|
8
|
+
exists?(keyname) &&
|
9
|
+
secret_key_has_namespace?(keyname)
|
10
|
+
end
|
11
|
+
|
12
|
+
def secret_key_namespace keyname
|
13
|
+
match = keyname.match(/^secrets\/(.*?)\/.*/)
|
14
|
+
if match
|
15
|
+
match[1]
|
16
|
+
else
|
17
|
+
""
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def secret_key_has_namespace? keyname
|
22
|
+
if is_secret?(keyname)
|
23
|
+
namespace = secret_key_namespace(keyname)
|
24
|
+
exists?(namespace)
|
25
|
+
else
|
26
|
+
# a plain key passes, it doesn't need a namespace
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_secret? keyname
|
32
|
+
keyname.match(/^secret/) != nil
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
private
|
37
|
+
def exists? keyname
|
38
|
+
!keyname.nil? && !keyname.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'net/http'
|
3
|
+
require 'muchkeys/configuration'
|
4
|
+
require 'muchkeys/errors'
|
5
|
+
|
6
|
+
|
7
|
+
class MuchKeys::Secret
|
8
|
+
CIPHER_SUITE = "AES-256-CFB"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# the path that clues MuchKeys that this path contains secrets
|
13
|
+
def secrets_path_hint
|
14
|
+
MuchKeys.configuration.secrets_hint || "secrets/"
|
15
|
+
end
|
16
|
+
|
17
|
+
def encrypt_string(val, public_key)
|
18
|
+
cipher = OpenSSL::Cipher.new CIPHER_SUITE
|
19
|
+
cert = OpenSSL::X509::Certificate.new File.read(public_key)
|
20
|
+
OpenSSL::PKCS7::encrypt([cert], val, cipher, OpenSSL::PKCS7::BINARY)
|
21
|
+
end
|
22
|
+
|
23
|
+
# turn a key_name into a SSL cert file name by convention
|
24
|
+
def certfile_name(key_name)
|
25
|
+
key_parts = key_name.match /(.*)\/#{secrets_path_hint}(.*)/
|
26
|
+
raise MuchKeys::InvalidKey, "#{key_name} doesn't look like a secret" if key_parts.nil?
|
27
|
+
key_base = key_parts[1].gsub(/^git\//, "")
|
28
|
+
MuchKeys.configuration.public_key || "#{ENV['HOME']}/.keys/#{key_base}.pem"
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_secret?(key_name)
|
32
|
+
key_name.match(/\/#{secrets_path_hint}/) != nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def auto_certificates_exist_for_key?(key)
|
36
|
+
file_exists?(secret_adapter.certfile_name(key))
|
37
|
+
end
|
38
|
+
|
39
|
+
def decrypt_string(val, public_key, private_key)
|
40
|
+
cert = OpenSSL::X509::Certificate.new(read_ssl_key(public_key))
|
41
|
+
key = OpenSSL::PKey::RSA.new(read_ssl_key(private_key))
|
42
|
+
OpenSSL::PKCS7.new(val).decrypt(key, cert)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def read_ssl_key(file_name)
|
49
|
+
File.read file_name
|
50
|
+
end
|
51
|
+
|
52
|
+
# Why would we even do this? For stubbing.
|
53
|
+
def file_exists?(path)
|
54
|
+
File.exist? path
|
55
|
+
end
|
56
|
+
|
57
|
+
def key_validator
|
58
|
+
MuchKeys::KeyValidator
|
59
|
+
end
|
60
|
+
|
61
|
+
def secret_adapter
|
62
|
+
MuchKeys::Secret
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
data/lib/muchkeys/version.rb
CHANGED
data/lib/muchkeys.rb
CHANGED
@@ -1,59 +1,171 @@
|
|
1
1
|
require "muchkeys/version"
|
2
2
|
require "muchkeys/errors"
|
3
|
+
require "muchkeys/configuration"
|
4
|
+
require "muchkeys/secret"
|
5
|
+
require "muchkeys/key_validator"
|
6
|
+
require "muchkeys/cli"
|
7
|
+
require "muchkeys/cli/validator"
|
8
|
+
|
3
9
|
require "net/http"
|
4
10
|
|
5
11
|
module MuchKeys
|
6
12
|
|
7
13
|
class << self
|
8
14
|
attr_accessor :configuration
|
9
|
-
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def configure
|
17
|
+
self.configuration ||= MuchKeys::Configuration.new
|
18
|
+
if block_given?
|
19
|
+
yield configuration
|
20
|
+
end
|
21
|
+
end
|
15
22
|
|
16
|
-
|
17
|
-
|
23
|
+
def search_order(application_name, key_name)
|
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
|
18
36
|
end
|
19
|
-
end
|
20
37
|
|
21
|
-
|
22
|
-
|
38
|
+
def find_first(key_name)
|
39
|
+
return ENV[key_name] unless ENV[key_name].nil?
|
40
|
+
|
41
|
+
unless MuchKeys.configuration.search_paths
|
42
|
+
application_name = detect_app_name
|
43
|
+
return false if !application_name
|
44
|
+
end
|
23
45
|
|
24
|
-
|
25
|
-
|
26
|
-
|
46
|
+
consul_paths = search_order(application_name, key_name)
|
47
|
+
|
48
|
+
response = nil
|
49
|
+
search_order(application_name, key_name).detect do |consul_path|
|
50
|
+
response = fetch_key(consul_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
if !response
|
54
|
+
raise MuchKeys::NoKeysSet, "Bailing. Consul isn't set with any keys for #{key_name}."
|
55
|
+
end
|
56
|
+
|
57
|
+
response
|
27
58
|
end
|
28
|
-
end
|
29
59
|
|
30
|
-
|
31
|
-
|
32
|
-
return ENV[key_name] unless ENV[key_name].nil?
|
60
|
+
def fetch_key(key_name, public_key:nil, private_key:nil)
|
61
|
+
return ENV[key_name] unless ENV[key_name].nil?
|
33
62
|
|
34
|
-
|
35
|
-
|
36
|
-
fetch_body(response)
|
37
|
-
end
|
63
|
+
if secret_adapter.is_secret?(key_name)
|
64
|
+
raise InvalidKey unless validate_key_name(key_name)
|
38
65
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
66
|
+
# configure automatic certificates
|
67
|
+
public_key = find_certfile_for_key(key_name) if public_key.nil?
|
68
|
+
private_key = find_certfile_for_key(key_name) if private_key.nil?
|
43
69
|
|
44
|
-
|
45
|
-
|
46
|
-
|
70
|
+
response = fetch_secret_key(key_name, public_key, private_key)
|
71
|
+
else
|
72
|
+
response = fetch_plain_key(key_name)
|
73
|
+
end
|
74
|
+
|
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 == ""
|
47
78
|
|
48
|
-
|
49
|
-
|
50
|
-
when (400..599)
|
51
|
-
raise MuchKeys::InvalidKey
|
79
|
+
# otherwise, we got consul data so return that
|
80
|
+
response
|
52
81
|
end
|
82
|
+
|
83
|
+
def consul_url(key_name)
|
84
|
+
URI("#{configuration.consul_url}/v1/kv/#{key_name}?raw")
|
85
|
+
end
|
86
|
+
|
87
|
+
# TODO: inline this
|
88
|
+
def fetch_body(response)
|
89
|
+
response.body
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_response(response_code)
|
93
|
+
case response_code
|
94
|
+
when (400..599)
|
95
|
+
raise MuchKeys::InvalidKey
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
private
|
101
|
+
def fetch_plain_key(key_name)
|
102
|
+
url = consul_url(key_name)
|
103
|
+
response = Net::HTTP.get_response url
|
104
|
+
handle_response(response)
|
105
|
+
fetch_body(response)
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_secret_key(key_name, public_pem=nil, private_pem=nil)
|
109
|
+
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
|
+
|
113
|
+
# we hit a key that doesn't exist, so don't try to decrypt it
|
114
|
+
return "" if !result || result.empty?
|
115
|
+
secret_adapter.decrypt_string(result, public_pem, private_pem)
|
116
|
+
end
|
117
|
+
|
118
|
+
def find_certfile_for_key(key_name)
|
119
|
+
secret_adapter.certfile_name key_name
|
120
|
+
end
|
121
|
+
|
122
|
+
def secret_adapter
|
123
|
+
MuchKeys::Secret
|
124
|
+
end
|
125
|
+
|
126
|
+
def validate_key_name(key_name)
|
127
|
+
MuchKeys::KeyValidator.valid? key_name
|
128
|
+
end
|
129
|
+
|
130
|
+
# Detecting Rails app names is a known quantity.
|
131
|
+
# TODO: figure out how to detect plain Rack apps. :(
|
132
|
+
def detect_app_name
|
133
|
+
return configuration.application_name if configuration.application_name
|
134
|
+
|
135
|
+
# Rails.application is "Monorail::Application".
|
136
|
+
if defined?(Rails)
|
137
|
+
application_name = Rails.application.class.to_s.split("::").first
|
138
|
+
elsif defined?(Rack)
|
139
|
+
application_name = "Asset_Server"
|
140
|
+
else
|
141
|
+
$stderr.puts "can't detect app name"
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
145
|
+
snakecase(application_name)
|
146
|
+
end
|
147
|
+
|
148
|
+
# MyApp should become my_app
|
149
|
+
def snakecase(string)
|
150
|
+
string.gsub(/::/, '/').
|
151
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
152
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
153
|
+
tr("-", "_").
|
154
|
+
downcase
|
155
|
+
end
|
156
|
+
|
53
157
|
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# default configure the gem on gem load
|
161
|
+
MuchKeys.configuration ||= MuchKeys::Configuration.new
|
162
|
+
|
163
|
+
|
164
|
+
# This interface looks like ENV which is more friendly to devs
|
165
|
+
class MUCHKEYS
|
54
166
|
|
55
|
-
def self.
|
56
|
-
|
167
|
+
def self.[](i)
|
168
|
+
MuchKeys.find_first(i)
|
57
169
|
end
|
58
170
|
|
59
171
|
end
|
data/muchkeys.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Pat O'Brien", "Chris Dillon"]
|
10
10
|
spec.email = ["pobrien@goldstar.com", "cdillon@goldstar.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{MuchKeys
|
13
|
-
spec.description = %q{
|
12
|
+
spec.summary = %q{MuchKeys fetches keys from the ENV and then falls back to consul}
|
13
|
+
spec.description = %q{MuchKeys can handle app configuration and appsecrets}
|
14
14
|
spec.homepage = "https://www.goldstar.com"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -19,11 +19,16 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
# slop 4 was a rewrite and it breaks a few gems:
|
23
|
+
# guard / pry. This is sad for development but slop4 is better than slop3.
|
24
|
+
spec.add_runtime_dependency "slop", "~> 4.2"
|
25
|
+
|
22
26
|
spec.add_development_dependency "bundler", "~> 1.10"
|
23
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
-
spec.add_development_dependency "rspec"
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
27
|
-
|
28
|
-
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
29
|
+
spec.add_development_dependency "webmock", "~> 1.20"
|
30
|
+
spec.add_development_dependency "vcr", "~> 2.9"
|
31
|
+
|
32
|
+
# slop 4 really, really needs this version of pry since they vendored it
|
33
|
+
spec.add_development_dependency "pry", '=0.10.3'
|
29
34
|
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.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat O'Brien
|
@@ -9,111 +9,112 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-04-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: slop
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
21
|
-
type: :
|
20
|
+
version: '4.2'
|
21
|
+
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '4.2'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: bundler
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '10
|
34
|
+
version: '1.10'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '10
|
41
|
+
version: '1.10'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: rake
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
48
|
+
version: '10.0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - "
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
55
|
+
version: '10.0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
57
|
+
name: rspec
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- - "
|
60
|
+
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
62
|
+
version: '3.3'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- - "
|
67
|
+
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
69
|
+
version: '3.3'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: webmock
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - "
|
74
|
+
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
76
|
+
version: '1.20'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - "
|
81
|
+
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '1.20'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
|
-
name:
|
85
|
+
name: vcr
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - "
|
88
|
+
- - "~>"
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '
|
90
|
+
version: '2.9'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- - "
|
95
|
+
- - "~>"
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
97
|
+
version: '2.9'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
|
-
name:
|
99
|
+
name: pry
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - '='
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version:
|
104
|
+
version: 0.10.3
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - '='
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version:
|
112
|
-
description:
|
111
|
+
version: 0.10.3
|
112
|
+
description: MuchKeys can handle app configuration and appsecrets
|
113
113
|
email:
|
114
114
|
- pobrien@goldstar.com
|
115
115
|
- cdillon@goldstar.com
|
116
|
-
executables:
|
116
|
+
executables:
|
117
|
+
- muchkeys
|
117
118
|
extensions: []
|
118
119
|
extra_rdoc_files: []
|
119
120
|
files:
|
@@ -125,10 +126,14 @@ files:
|
|
125
126
|
- LICENSE.txt
|
126
127
|
- README.md
|
127
128
|
- Rakefile
|
128
|
-
-
|
129
|
-
- bin/setup
|
129
|
+
- exe/muchkeys
|
130
130
|
- lib/muchkeys.rb
|
131
|
+
- lib/muchkeys/cli.rb
|
132
|
+
- lib/muchkeys/cli/validator.rb
|
133
|
+
- lib/muchkeys/configuration.rb
|
131
134
|
- lib/muchkeys/errors.rb
|
135
|
+
- lib/muchkeys/key_validator.rb
|
136
|
+
- lib/muchkeys/secret.rb
|
132
137
|
- lib/muchkeys/version.rb
|
133
138
|
- muchkeys.gemspec
|
134
139
|
homepage: https://www.goldstar.com
|
@@ -151,8 +156,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
156
|
version: '0'
|
152
157
|
requirements: []
|
153
158
|
rubyforge_project:
|
154
|
-
rubygems_version: 2.
|
159
|
+
rubygems_version: 2.4.5.1
|
155
160
|
signing_key:
|
156
161
|
specification_version: 4
|
157
|
-
summary: MuchKeys
|
162
|
+
summary: MuchKeys fetches keys from the ENV and then falls back to consul
|
158
163
|
test_files: []
|
164
|
+
has_rdoc:
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "muchkeys"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|