muchkeys 0.0.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|