muchkeys 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +16 -0
- data/README.md +19 -19
- data/circle.yml +21 -5
- data/docker-compose.yml +15 -0
- data/exe/muchkeys +1 -1
- data/lib/muchkeys/application_client.rb +151 -0
- data/lib/muchkeys/cli.rb +105 -10
- data/lib/muchkeys/consul_client.rb +51 -0
- data/lib/muchkeys/key_validator.rb +34 -30
- data/lib/muchkeys/rails.rb +14 -15
- data/lib/muchkeys/secret.rb +50 -46
- data/lib/muchkeys/version.rb +2 -2
- data/lib/muchkeys.rb +45 -153
- data/muchkeys.gemspec +4 -5
- data/secret.txt +1 -0
- metadata +23 -34
- data/lib/muchkeys/configuration.rb +0 -30
- data/lib/muchkeys/errors.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e81c8deded76555843a5242334ae71417521d1f3
|
4
|
+
data.tar.gz: 3c71a228e84eb175b81cec46fa903140dcf76006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c635fa24d92c441d5e777238c3beed8472942704a8efb0d907e504ec2f72f08ad6ab6c5ee02ffe61bd81405b277c414a8341d1041e998cde631b85b7b16a896d
|
7
|
+
data.tar.gz: 572192537944c62a1960be30603f9be0cc0e6de4a5383af8f4c271a47d9395a1a5cf30137d65260c6f667bff813e3617c061415f860b59ed25ca7e24f122815c
|
data/Dockerfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# pull our ruby version
|
2
|
+
FROM ruby:2.3.1-alpine
|
3
|
+
MAINTAINER goldstarevents
|
4
|
+
|
5
|
+
RUN apk update && apk add bash build-base git gcc abuild binutils binutils-doc gcc-doc
|
6
|
+
RUN gem install bundler
|
7
|
+
|
8
|
+
WORKDIR /gem/
|
9
|
+
ADD . /gem/
|
10
|
+
RUN bundle install
|
11
|
+
|
12
|
+
VOLUME .:/gem/
|
13
|
+
|
14
|
+
ENTRYPOINT ["bundle", "exec"]
|
15
|
+
CMD ["rake", "-T"]
|
16
|
+
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Muchkeys
|
2
2
|
|
3
3
|
─────────▄──────────────▄
|
4
4
|
────────▌▒█───────────▄▀▒▌
|
@@ -21,7 +21,7 @@
|
|
21
21
|
──▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀▀
|
22
22
|
|
23
23
|
|
24
|
-
|
24
|
+
Muchkeys lets you store your application keys in consul and then leverages many conventions to create a
|
25
25
|
pleasant API. Keys in this context can mean application settings, knobs, dials, passwords, api keys
|
26
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
27
|
search consul for a key based on an opinionated hierarchy convention.
|
@@ -29,7 +29,7 @@ search consul for a key based on an opinionated hierarchy convention.
|
|
29
29
|
You will want to read below about the convention and assumptions first to see if this works for
|
30
30
|
you.
|
31
31
|
|
32
|
-
|
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.
|
33
33
|
|
34
34
|
|
35
35
|
## Installation
|
@@ -62,10 +62,10 @@ a central configuration store while retaining control of your local development
|
|
62
62
|
<%= MUCHKEYS['widget_api_key'] %>
|
63
63
|
```
|
64
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"`
|
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
66
|
it sees one set.
|
67
67
|
|
68
|
-
|
68
|
+
Muchkeys will look in consul (a key/value store) for keys (settings/secrets/knobs to turn).
|
69
69
|
It searches in an order of convention. It is also assuming you want to use git2consul
|
70
70
|
to enable your developers to add their own keys. Git2consul syncs a git repo to consul.
|
71
71
|
|
@@ -82,7 +82,7 @@ and this is something new to the app.
|
|
82
82
|
* Ops wasn't involved. Developers are empowered.
|
83
83
|
|
84
84
|
|
85
|
-
|
85
|
+
Muchkeys does this magic through a search order. It searches consul for the key
|
86
86
|
in certain order: (notice that the key isn't prefixed when you look up `twitter_api_key` with
|
87
87
|
`MUCHKEYS['twitter_api_key']`)
|
88
88
|
|
@@ -99,7 +99,7 @@ The above `feed_reader` is detected from `Rails.application`. If you don't have
|
|
99
99
|
the application name in the configure block:
|
100
100
|
|
101
101
|
```
|
102
|
-
|
102
|
+
Muchkeys.configure do |config|
|
103
103
|
config.application_name = "rack_app"
|
104
104
|
end
|
105
105
|
```
|
@@ -107,7 +107,7 @@ end
|
|
107
107
|
The order and paths that it searches is configurable:
|
108
108
|
|
109
109
|
```
|
110
|
-
|
110
|
+
Muchkeys.configure do |config|
|
111
111
|
config.search_paths = %W(
|
112
112
|
app_name/keys app_name/secrets shared/keys shared/secrets
|
113
113
|
)
|
@@ -129,7 +129,7 @@ your keys and secrets organized like this (one deep nesting). This is configura
|
|
129
129
|
```
|
130
130
|
# if you don't put your secrets in something like shared/secrets/
|
131
131
|
# but shared/passwords/
|
132
|
-
|
132
|
+
Muchkeys.configure do |config|
|
133
133
|
config.secrets_path_hint = "passwords/"
|
134
134
|
end
|
135
135
|
```
|
@@ -148,7 +148,7 @@ done. If you need to override defaults, create an initializer.
|
|
148
148
|
|
149
149
|
```
|
150
150
|
# config/initializers/muchkeys.rb
|
151
|
-
|
151
|
+
Muchkeys.configure do |config|
|
152
152
|
# your settings if desired
|
153
153
|
end
|
154
154
|
```
|
@@ -179,12 +179,12 @@ openssl req -new -newkey rsa:4096 -nodes -x509 -keyout /tmp/self_signed_test.pem
|
|
179
179
|
```
|
180
180
|
|
181
181
|
```ruby
|
182
|
-
|
182
|
+
Muchkeys.configure do |config|
|
183
183
|
config.public_key = "/tmp/self_signed_test.pem"
|
184
184
|
config.private_key = "/tmp/self_signed_test.pem"
|
185
185
|
end
|
186
186
|
|
187
|
-
puts
|
187
|
+
puts Muchkeys::Secret.encrypt_string("bacon is just ok").to_s
|
188
188
|
# => -----BEGIN PKCS7-----
|
189
189
|
# => MIICuwYJKoZIhvcNAQcDoIICrDCCAqgCAQAxggJuMIICagIBADBSMEUxCzAJ ...
|
190
190
|
# => ...
|
@@ -192,28 +192,28 @@ puts MuchKeys::Secret.encrypt_string("bacon is just ok").to_s
|
|
192
192
|
# Copy and paste this into consul under a key: ie: secrets/fake
|
193
193
|
|
194
194
|
# Now you can fetch the secret with the same public key.
|
195
|
-
|
195
|
+
Muchkeys::Secret.get_consul_secret('secrets/fake')
|
196
196
|
# => bacon is just ok
|
197
197
|
```
|
198
198
|
|
199
199
|
Inside your app:
|
200
200
|
```ruby
|
201
201
|
# inside a rails initializer or other setup file you have
|
202
|
-
|
202
|
+
Muchkeys.configure do |config|
|
203
203
|
config.consul_url = "http://myrealhost" # Default is "http://localhost:8500"
|
204
204
|
config.public_key = "path to public .pem" # this is only required if you are encrypting secrets
|
205
205
|
config.private_key = "path to private .pem" # this is only required if you are encrypting secrets
|
206
206
|
end
|
207
207
|
|
208
208
|
# then inside your YAML or app:
|
209
|
-
|
209
|
+
Muchkeys.fetch_key('number_of_threads')
|
210
210
|
# goes to git/config/myapp/number_of_threads in consul
|
211
211
|
```
|
212
212
|
|
213
213
|
|
214
214
|
## Automagic Certificates
|
215
215
|
|
216
|
-
|
216
|
+
Muchkeys can find certs automatically in `~/.keys`. `Muchkeys.fetch_key("git/waffles/secrets/a_password")` will try to decrypt `a_password`
|
217
217
|
with a cert called `~/.keys/waffles.pem`. The private key can be in the same file but should be protected
|
218
218
|
with file permissions `0600`.
|
219
219
|
|
@@ -232,12 +232,12 @@ $ muchkeys -d --public_key=~/.keys/staging.pem --private_key=~/.keys/staging.pem
|
|
232
232
|
You can fetch individual keys if you want.
|
233
233
|
|
234
234
|
```bash
|
235
|
-
ruby -e 'require "muchkeys"; puts
|
235
|
+
ruby -e 'require "muchkeys"; puts Muchkeys.fetch_key("mail_server")'
|
236
236
|
# => smtp.example.com (from consul)
|
237
237
|
```
|
238
238
|
|
239
239
|
```bash
|
240
|
-
mail_server=muffin.ninja.local ruby -e 'require "muchkeys"; puts
|
240
|
+
mail_server=muffin.ninja.local ruby -e 'require "muchkeys"; puts Muchkeys.fetch_key("mail_server")'
|
241
241
|
# => muffin.ninja.local (from ENV)
|
242
242
|
```
|
243
243
|
|
@@ -257,7 +257,7 @@ to change a setting and some things might be shared between apps like URLs, api
|
|
257
257
|
|
258
258
|
Searching multiple paths in consul is because consul is hierarchical (or can be) and ENV is not.
|
259
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,
|
260
|
+
it more familiar to developers. So worst case, by default, Muchkeys will search consul 4 times
|
261
261
|
to find a key.
|
262
262
|
|
263
263
|
Encrypted secrets can be slightly more clunky. Developers may not have signing keys,
|
data/circle.yml
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
machine:
|
2
|
-
|
3
|
-
|
2
|
+
pre:
|
3
|
+
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
|
4
|
+
services:
|
5
|
+
- docker
|
4
6
|
timezone:
|
5
7
|
Etc/GMT
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
-
|
9
|
+
dependencies:
|
10
|
+
override:
|
11
|
+
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS quay.io
|
12
|
+
- sudo apt-get install python-dev
|
13
|
+
- sudo pip install --upgrade docker-compose==1.8.0
|
10
14
|
|
15
|
+
database:
|
16
|
+
override:
|
17
|
+
- echo "no db for you"
|
18
|
+
|
19
|
+
test:
|
20
|
+
pre:
|
21
|
+
- docker-compose build
|
22
|
+
override:
|
23
|
+
- docker-compose run default bash -c -l "bundle exec rspec":
|
24
|
+
parallel: true
|
25
|
+
files:
|
26
|
+
- spec/**/*_spec.rb
|
data/docker-compose.yml
ADDED
data/exe/muchkeys
CHANGED
@@ -0,0 +1,151 @@
|
|
1
|
+
require "active_support/configurable"
|
2
|
+
require "active_support/core_ext/object/blank"
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
require "active_support/core_ext/hash"
|
5
|
+
require "active_support/core_ext/enumerable"
|
6
|
+
|
7
|
+
class Muchkeys::ApplicationClient
|
8
|
+
attr_accessor :config, :secret_adapter, :key_validator, :client
|
9
|
+
|
10
|
+
delegate :certfile_name, :encrypt_string, :decrypt_string, :is_secret?, to: :secret_adapter
|
11
|
+
delegate :valid_key_name?, to: :key_validator
|
12
|
+
delegate :application_name, :consul_url, to: :config
|
13
|
+
|
14
|
+
def initialize(config = Muchkeys.config)
|
15
|
+
@config = config
|
16
|
+
@secret_adapter = Muchkeys::Secret.new(self)
|
17
|
+
@key_validator = Muchkeys::KeyValidator.new(self)
|
18
|
+
@client = Muchkeys::ConsulClient.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def allow_unsafe_operation
|
22
|
+
client.unsafe = true
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
client.unsafe = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_app_key(key, value, type: nil, **options)
|
29
|
+
set_key(key, value, scope: :application, type: type, **options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_shared_key(key, value, type: nil, **options)
|
33
|
+
set_key(key, value, scope: :shared, type: type, **options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_key(key, value, scope: nil, type: nil, **options)
|
37
|
+
if scope && type
|
38
|
+
key = construct_key_path(key, scope, type) || key
|
39
|
+
end
|
40
|
+
|
41
|
+
if type == :secret
|
42
|
+
value = secret_adapter.encrypt_string(value.chomp, config.public_key).to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
client.put(value, key, **options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_key(key)
|
49
|
+
client.delete(key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def first(key_name)
|
53
|
+
# http://stackoverflow.com/questions/17853912/ruby-enumerables-is-there-a-detect-for-results-of-block-evaluation
|
54
|
+
# weirdly, this seems to be the most straightforward method of doing this
|
55
|
+
# without a monkey patch, as there is neither a core method that returns
|
56
|
+
# the first non-nil result of evaluating the block, or a lazy compact
|
57
|
+
search_paths(key_name).detect { |path| v = fetch_key(path) and break v }
|
58
|
+
end
|
59
|
+
|
60
|
+
def all(key_name)
|
61
|
+
search_paths(key_name).map { |path| fetch_key(path) }.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
def search_paths(key_name = nil)
|
65
|
+
application_search_paths.map { |p| [p, key_name].join('/') }
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_key(key_name, public_key: nil, private_key: nil)
|
69
|
+
if is_secret?(key_name)
|
70
|
+
fetch_secret_key(key_name, public_key, private_key)
|
71
|
+
else
|
72
|
+
fetch_plain_key(key_name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def known_keys
|
77
|
+
@known_keys ||= application_search_paths
|
78
|
+
.map { |path| client.get(path, recursive: true) }
|
79
|
+
.compact
|
80
|
+
.each_with_object([]) { |response, keys| keys << parse_recurse_response(response) }
|
81
|
+
.flatten
|
82
|
+
.uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
def each_path
|
86
|
+
known_keys.each do |key|
|
87
|
+
search_paths(key).each do |path|
|
88
|
+
yield path if fetch_key(path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def verify_keys(*required_keys)
|
94
|
+
if (required_keys - known_keys).any?
|
95
|
+
# if there are any required keys (in the .env file) that are not known
|
96
|
+
# about by the app's consul space, raise an error
|
97
|
+
raise Muchkeys::KeyNotSet, "Consul isn't set with any keys for #{required_keys - known_keys}."
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def application_search_paths
|
104
|
+
@application_search_paths ||= config.search_paths.collect { |path|
|
105
|
+
path % { application_name: config.application_name }
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def construct_key_path(key, scope, type)
|
110
|
+
found_paths = application_search_paths.select { |path|
|
111
|
+
case
|
112
|
+
when scope == :application && type == :secret
|
113
|
+
path.include?(application_name) && is_secret?(path)
|
114
|
+
when scope == :application && type == :config
|
115
|
+
path.include?(application_name) && !is_secret?(path)
|
116
|
+
when scope == :shared && type == :secret
|
117
|
+
!path.include?(application_name) && is_secret?(path)
|
118
|
+
when scope == :shared && type == :config
|
119
|
+
!path.include?(application_name) && !is_secret?(path)
|
120
|
+
else
|
121
|
+
false
|
122
|
+
end
|
123
|
+
}
|
124
|
+
|
125
|
+
raise AmbigousPath, "This key could go in multiple folders, please provide full path" if found_paths.many?
|
126
|
+
|
127
|
+
[found_paths.first, key].join("/")
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse_recurse_response(response)
|
131
|
+
JSON.parse(response)
|
132
|
+
.collect { |r| r['Key'].rpartition("/").last }
|
133
|
+
.reject(&:empty?)
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_plain_key(key_name)
|
137
|
+
client.get(key_name).presence
|
138
|
+
end
|
139
|
+
|
140
|
+
def fetch_secret_key(key_name, public_key=nil, private_key=nil)
|
141
|
+
result = fetch_plain_key(key_name)
|
142
|
+
|
143
|
+
# Don't try to decrypt if the value is nil
|
144
|
+
return nil if result.blank?
|
145
|
+
|
146
|
+
public_pem = public_key || certfile_name(key_name)
|
147
|
+
private_pem = private_key || certfile_name(key_name)
|
148
|
+
|
149
|
+
decrypt_string(result, public_pem, private_pem)
|
150
|
+
end
|
151
|
+
end
|
data/lib/muchkeys/cli.rb
CHANGED
@@ -1,50 +1,145 @@
|
|
1
1
|
require 'thor'
|
2
2
|
|
3
|
-
module
|
3
|
+
module Muchkeys
|
4
4
|
class CLI < Thor
|
5
5
|
include Thor::Actions
|
6
6
|
|
7
7
|
map %w[--version -v] => :__version
|
8
8
|
|
9
|
-
class_option :
|
9
|
+
class_option :consul_url, type: :string, default: 'http://localhost:8500'
|
10
10
|
|
11
11
|
desc "encrypt FILE", "encrypt keys from a file to put in consul"
|
12
12
|
method_option :public_key, type: :string, required: true
|
13
13
|
def encrypt(file)
|
14
|
-
|
14
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
15
|
+
say MuchkeysExecutor.encrypt(file, options[:public_key])
|
15
16
|
end
|
16
17
|
|
17
18
|
desc "decrypt KEY", "decrypt keys from consul"
|
18
19
|
method_option :public_key, type: :string, required: true
|
19
20
|
method_option :private_key, type: :string, required: true
|
20
21
|
def decrypt(consul_key)
|
21
|
-
|
22
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
23
|
+
say MuchkeysExecutor.decrypt(consul_key, options[:public_key], options[:private_key])
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "check", "ensure that all keys in .env in CWD are present in consul"
|
27
|
+
def check(app_name)
|
28
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
29
|
+
say MuchkeysExecutor.check(app_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "list", "list all keys in Application"
|
33
|
+
def list(app_name)
|
34
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
35
|
+
say MuchkeysExecutor.list(app_name)
|
22
36
|
end
|
23
37
|
|
24
38
|
desc "fetch KEY", "fetch plaintext key from consul"
|
25
39
|
def fetch(consul_key)
|
26
|
-
|
40
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
41
|
+
say MuchkeysExecutor.fetch(consul_key)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "store DATA KEY", "store data a particular key"
|
45
|
+
method_option :public_key, type: :string
|
46
|
+
method_option :private_key, type: :string
|
47
|
+
def store(data, consul_key)
|
48
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
49
|
+
say MuchkeysExecutor.store(consul_key, data, public_key: options[:public_key], private_key: options[:private_key])
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "wipeout", "clear Consul"
|
53
|
+
method_option :app_name, type: :string
|
54
|
+
def wipeout
|
55
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
56
|
+
unless yes?("Really clear consul instance at #{Muchkeys.config.consul_url}?", Thor::Shell::Color::RED)
|
57
|
+
say "Nothing done!"
|
58
|
+
else
|
59
|
+
say MuchkeysExecutor.wipeout(app_name: options[:app_name])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "delete", "remove key"
|
64
|
+
def delete(key)
|
65
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
66
|
+
say MuchkeysExecutor.delete(key)
|
27
67
|
end
|
28
68
|
|
29
69
|
desc "--version", "Print the version"
|
30
70
|
def __version
|
31
|
-
|
71
|
+
Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
|
72
|
+
say Muchkeys::VERSION
|
32
73
|
end
|
33
74
|
|
34
|
-
module
|
75
|
+
module MuchkeysExecutor
|
35
76
|
extend self
|
36
77
|
|
37
78
|
def encrypt(file, public_key)
|
38
79
|
string_to_encrypt = File.read(file)
|
39
|
-
|
80
|
+
Muchkeys::ApplicationClient.new.secret_adapter.encrypt_string(string_to_encrypt.chomp, public_key)
|
40
81
|
end
|
41
82
|
|
42
83
|
def decrypt(consul_key, public_key, private_key)
|
43
|
-
|
84
|
+
Muchkeys::ApplicationClient.new.fetch_key(consul_key, public_key: public_key, private_key: private_key)
|
85
|
+
end
|
86
|
+
|
87
|
+
def store(consul_key, data, public_key: nil, private_key: nil)
|
88
|
+
Muchkeys.configure { |c| c.public_key = public_key; c.private_key = private_key }
|
89
|
+
app = Muchkeys::ApplicationClient.new(Muchkeys.config)
|
90
|
+
|
91
|
+
if data == "-"
|
92
|
+
data = $stdin.read
|
93
|
+
end
|
94
|
+
|
95
|
+
app.allow_unsafe_operation do
|
96
|
+
if public_key && private_key
|
97
|
+
app.set_key(consul_key, data, type: :secret)
|
98
|
+
"Secret '#{data}' stored in #{consul_key}"
|
99
|
+
else
|
100
|
+
app.set_key(consul_key, data)
|
101
|
+
"'#{data}' stored in #{consul_key}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def list(app_name)
|
107
|
+
Muchkeys.configure { |c| c.application_name = app_name }
|
108
|
+
keys = Muchkeys::ApplicationClient.new(Muchkeys.config).known_keys
|
109
|
+
|
110
|
+
keys.join("\n")
|
111
|
+
end
|
112
|
+
|
113
|
+
def check(app_name)
|
114
|
+
Muchkeys.configure { |c| c.application_name = app_name }
|
115
|
+
Muchkeys::ApplicationClient.new(Muchkeys.config).verify_keys(*Muchkeys.env_keys)
|
116
|
+
|
117
|
+
"All keys present and accounted for."
|
118
|
+
end
|
119
|
+
|
120
|
+
def wipeout(app_name: nil)
|
121
|
+
Muchkeys.configure { |c| c.application_name = app_name }
|
122
|
+
app = Muchkeys::ApplicationClient.new
|
123
|
+
app.allow_unsafe_operation do
|
124
|
+
app.each_path do |path|
|
125
|
+
app.delete_key(path)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
"Everything deleted!"
|
130
|
+
end
|
131
|
+
|
132
|
+
def delete(consul_key)
|
133
|
+
app = Muchkeys::ApplicationClient.new
|
134
|
+
app.allow_unsafe_operation do
|
135
|
+
app.delete_key(consul_key)
|
136
|
+
end
|
137
|
+
|
138
|
+
"Key #{consul_key} deleted"
|
44
139
|
end
|
45
140
|
|
46
141
|
def fetch(consul_key)
|
47
|
-
|
142
|
+
Muchkeys::ApplicationClient.new.fetch_key(consul_key)
|
48
143
|
end
|
49
144
|
end
|
50
145
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Muchkeys::ConsulClient
|
2
|
+
delegate :config, to: :application
|
3
|
+
class SafetyViolation < StandardError; end
|
4
|
+
|
5
|
+
attr_accessor :application
|
6
|
+
|
7
|
+
def initialize(application)
|
8
|
+
@application = application
|
9
|
+
end
|
10
|
+
|
11
|
+
def unsafe=(toggle)
|
12
|
+
@unsafe = toggle
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(key, recursive: false)
|
16
|
+
url = recursive ? consul_recurse_url(key) : consul_key_url(key)
|
17
|
+
response = Net::HTTP.get_response(url)
|
18
|
+
|
19
|
+
if response.code == "200"
|
20
|
+
response.body
|
21
|
+
else
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
rescue Errno::ECONNREFUSED
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def put(value, key, **options)
|
29
|
+
url = consul_insert_key_url(key, **options)
|
30
|
+
raise SafetyViolation unless @unsafe
|
31
|
+
Net::HTTP.new(url.host, url.port).send_request('PUT', url.request_uri, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(key)
|
35
|
+
url = consul_key_url(key)
|
36
|
+
raise SafetyViolation unless @unsafe
|
37
|
+
Net::HTTP.new(url.host, url.port).send_request('DELETE', url.request_uri)
|
38
|
+
end
|
39
|
+
|
40
|
+
def consul_key_url(key_name)
|
41
|
+
URI("#{config.consul_url}/v1/kv/#{key_name}?raw")
|
42
|
+
end
|
43
|
+
|
44
|
+
def consul_recurse_url(path)
|
45
|
+
URI("#{config.consul_url}/v1/kv/#{path}?recurse")
|
46
|
+
end
|
47
|
+
|
48
|
+
def consul_insert_key_url(key_name, **query)
|
49
|
+
URI("#{config.consul_url}/v1/kv/#{key_name}?#{query.to_query}")
|
50
|
+
end
|
51
|
+
end
|
@@ -1,43 +1,47 @@
|
|
1
|
-
module
|
1
|
+
module Muchkeys
|
2
2
|
class KeyValidator
|
3
|
+
attr_accessor :app_client
|
4
|
+
delegate :config, to: :app_client
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def valid? keyname
|
8
|
-
exists?(keyname) &&
|
9
|
-
secret_key_has_namespace?(keyname)
|
10
|
-
end
|
6
|
+
def initialize(app_client)
|
7
|
+
@app_client = app_client
|
8
|
+
end
|
11
9
|
|
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
10
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
11
|
+
# key should pass validation rules
|
12
|
+
def valid?(keyname)
|
13
|
+
exists?(keyname) &&
|
14
|
+
secret_key_has_namespace?(keyname)
|
15
|
+
end
|
16
|
+
alias_method :valid_key_name?, :valid?
|
17
|
+
|
18
|
+
def secret_key_namespace(keyname)
|
19
|
+
match = keyname.match(/^secrets\/(.*?)\/.*/)
|
20
|
+
if match
|
21
|
+
match[1]
|
22
|
+
else
|
23
|
+
""
|
29
24
|
end
|
25
|
+
end
|
30
26
|
|
31
|
-
|
32
|
-
|
27
|
+
def secret_key_has_namespace?(keyname)
|
28
|
+
if is_secret?(keyname)
|
29
|
+
namespace = secret_key_namespace(keyname)
|
30
|
+
exists?(namespace)
|
31
|
+
else
|
32
|
+
# a plain key passes, it doesn't need a namespace
|
33
|
+
true
|
33
34
|
end
|
35
|
+
end
|
34
36
|
|
37
|
+
def is_secret?(keyname)
|
38
|
+
keyname.match(/^secret/) != nil
|
39
|
+
end
|
35
40
|
|
36
|
-
|
37
|
-
def exists? keyname
|
38
|
-
!keyname.nil? && !keyname.empty?
|
39
|
-
end
|
41
|
+
private
|
40
42
|
|
43
|
+
def exists?(keyname)
|
44
|
+
!keyname.nil? && !keyname.empty?
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
data/lib/muchkeys/rails.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module Muchkeys
|
2
|
+
class Rails < Rails::Railtie
|
3
|
+
config.before_configuration do
|
4
|
+
app_config = YAML.load(File.read(::Rails.root.join("config", "muchkeys.yml")))
|
5
|
+
Muchkeys.configure do |config|
|
6
|
+
config.application_name ||= app_config[:application_name] || ::Rails.application.class.parent_name.underscore
|
7
|
+
config.consul_url ||= app_config[:consul_url] || "http://localhost:8500"
|
8
|
+
config.keys_dir ||= app_config[:keys_dir]
|
9
|
+
config.private_key ||= app_config[:private_key]
|
10
|
+
config.public_key ||= app_config[:public_key]
|
11
|
+
config.search_paths ||= app_config[:search_paths]
|
12
|
+
config.secrets_hint ||= app_config[:secrets_hint]
|
13
|
+
end
|
9
14
|
|
10
|
-
|
11
|
-
def env_keys
|
12
|
-
# parse all environments found in .env and populate them from consul
|
13
|
-
unless File.exists?(Rails.root.join(".env"))
|
14
|
-
raise IOError, ".env files are required for Muchkeys ENV injection to work"
|
15
|
+
Muchkeys.populate_environment!(*Muchkeys.env_keys)
|
15
16
|
end
|
16
|
-
|
17
|
-
File.read(Rails.root.join(".env")).each_line.map { |x| x.split("=")[0] }
|
18
17
|
end
|
19
18
|
end
|
data/lib/muchkeys/secret.rb
CHANGED
@@ -1,65 +1,69 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
1
2
|
require 'openssl'
|
2
3
|
require 'net/http'
|
3
|
-
require 'muchkeys/configuration'
|
4
|
-
require 'muchkeys/errors'
|
5
4
|
|
6
|
-
|
7
|
-
class MuchKeys::Secret
|
5
|
+
class Muchkeys::Secret
|
8
6
|
CIPHER_SUITE = "AES-256-CFB"
|
9
7
|
|
10
|
-
|
8
|
+
attr_accessor :app_client
|
9
|
+
|
10
|
+
delegate :config, to: :app_client
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
12
|
+
def initialize(app_client)
|
13
|
+
@app_client = app_client
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
16
|
+
# the path that clues Muchkeys that this path contains secrets
|
17
|
+
def secrets_path_hint
|
18
|
+
config.secrets_hint || "secrets"
|
19
|
+
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
MuchKeys.configuration.public_key || "#{ENV['HOME']}/.keys/#{key_base}.pem"
|
29
|
-
end
|
21
|
+
def encrypt_string(val, public_key)
|
22
|
+
cipher = OpenSSL::Cipher.new CIPHER_SUITE
|
23
|
+
cert = OpenSSL::X509::Certificate.new File.read(public_key)
|
24
|
+
OpenSSL::PKCS7::encrypt([cert], val, cipher, OpenSSL::PKCS7::BINARY)
|
25
|
+
end
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
# turn a key_name into a SSL cert file name by convention
|
28
|
+
def certfile_name(key_name)
|
29
|
+
key_parts = key_name.match /(.*)\/#{secrets_path_hint}(.*)/
|
30
|
+
# FIXME this already checked in the secretes validator, we don't need to
|
31
|
+
# check it again
|
32
|
+
raise Muchkeys::InvalidKey, "#{key_name} doesn't look like a secret" if key_parts.nil?
|
33
|
+
key_base = key_parts[1].gsub(/^git\//, "")
|
34
|
+
config.public_key || "#{ENV['HOME']}/.keys/#{key_base}.pem"
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
def is_secret?(key_name)
|
38
|
+
key_name.match(/\/#{secrets_path_hint}/) != nil
|
39
|
+
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
OpenSSL::PKCS7.new(val).decrypt(key, cert)
|
43
|
-
end
|
41
|
+
def auto_certificates_exist_for_key?(key)
|
42
|
+
file_exists?(certfile_name(key))
|
43
|
+
end
|
44
44
|
|
45
|
-
|
45
|
+
def decrypt_string(val, public_key = nil, private_key = nil)
|
46
|
+
cert = OpenSSL::X509::Certificate.new(read_ssl_key(public_key))
|
47
|
+
key = OpenSSL::PKey::RSA.new(read_ssl_key(private_key))
|
48
|
+
OpenSSL::PKCS7.new(val).decrypt(key, cert)
|
49
|
+
end
|
46
50
|
|
47
|
-
|
48
|
-
File.read file_name
|
49
|
-
end
|
51
|
+
private
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
53
|
+
def read_ssl_key(file_name)
|
54
|
+
File.read(file_name)
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
# Why would we even do this? For stubbing.
|
58
|
+
def file_exists?(path)
|
59
|
+
File.exist?(path)
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
def key_validator
|
63
|
+
Muchkeys::KeyValidator
|
64
|
+
end
|
63
65
|
|
66
|
+
def secret_adapter
|
67
|
+
Muchkeys::Secret
|
64
68
|
end
|
65
69
|
end
|
data/lib/muchkeys/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "0.
|
1
|
+
module Muchkeys
|
2
|
+
VERSION = "0.7.0"
|
3
3
|
end
|
data/lib/muchkeys.rb
CHANGED
@@ -1,175 +1,67 @@
|
|
1
|
-
require "
|
2
|
-
require "muchkeys/errors"
|
3
|
-
require "muchkeys/configuration"
|
4
|
-
require "muchkeys/secret"
|
5
|
-
require "muchkeys/key_validator"
|
6
|
-
require "muchkeys/cli"
|
7
|
-
|
1
|
+
require "active_support/configurable"
|
8
2
|
require "net/http"
|
3
|
+
require "json"
|
9
4
|
|
10
|
-
if defined? Rails
|
5
|
+
if defined? ::Rails
|
11
6
|
require 'muchkeys/rails'
|
12
7
|
end
|
13
8
|
|
14
|
-
module
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
class << self
|
21
|
-
attr_accessor :configuration
|
22
|
-
|
23
|
-
def configure
|
24
|
-
self.configuration ||= MuchKeys::Configuration.new
|
25
|
-
if block_given?
|
26
|
-
yield configuration
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def populate_environment!(*keys)
|
31
|
-
keys.each do |key|
|
32
|
-
ENV[key] = MuchKeys.find_first(key)
|
33
|
-
end
|
34
|
-
nil
|
35
|
-
end
|
36
|
-
|
37
|
-
# this is the entry point to the ENV-like object that devs will use
|
38
|
-
def find_first(key_name)
|
39
|
-
unless MuchKeys.configuration.search_paths
|
40
|
-
raise MuchKeys::UnknownApplication, "Can't detect app name and application_name isn't set." if !application_name
|
41
|
-
end
|
42
|
-
|
43
|
-
consul_paths_to_search = search_order(application_name, key_name)
|
44
|
-
|
45
|
-
# search consul in a specific order until we find something
|
46
|
-
response = nil
|
47
|
-
consul_paths_to_search.detect do |consul_path|
|
48
|
-
response = fetch_key(consul_path)
|
49
|
-
response if response != MuchKeys::BlankKey
|
50
|
-
end
|
51
|
-
|
52
|
-
if response == BlankKey
|
53
|
-
raise MuchKeys::NoKeysSet, "Bailing. Consul isn't set with any keys for #{key_name}."
|
54
|
-
end
|
55
|
-
|
56
|
-
response
|
57
|
-
end
|
58
|
-
|
59
|
-
def fetch_key(key_name, public_key:nil, private_key:nil)
|
60
|
-
return ENV[key_name] unless ENV[key_name].nil?
|
61
|
-
|
62
|
-
if secret_adapter.is_secret?(key_name)
|
63
|
-
raise InvalidKey unless validate_key_name(key_name)
|
64
|
-
|
65
|
-
# configure automatic certificates
|
66
|
-
public_key = find_certfile_for_key(key_name) if public_key.nil?
|
67
|
-
private_key = find_certfile_for_key(key_name) if private_key.nil?
|
68
|
-
|
69
|
-
response = fetch_secret_key(key_name, public_key, private_key)
|
70
|
-
else
|
71
|
-
response = fetch_plain_key(key_name)
|
72
|
-
end
|
73
|
-
|
74
|
-
# otherwise, we got consul data so return that
|
75
|
-
response
|
76
|
-
end
|
77
|
-
|
78
|
-
def search_order(application_name, key_name)
|
79
|
-
if MuchKeys.configuration.search_paths
|
80
|
-
search_paths = MuchKeys.configuration.search_paths.collect do |path|
|
81
|
-
"#{path}/#{key_name}"
|
82
|
-
end
|
83
|
-
else
|
84
|
-
search_paths = [
|
85
|
-
"git/#{application_name}/secrets/#{key_name}",
|
86
|
-
"git/#{application_name}/config/#{key_name}",
|
87
|
-
"git/shared/secrets/#{key_name}",
|
88
|
-
"git/shared/config/#{key_name}"
|
89
|
-
]
|
90
|
-
end
|
9
|
+
module Muchkeys
|
10
|
+
class InvalidKey < StandardError; end
|
11
|
+
class CLIOptionsError < StandardError; end
|
12
|
+
class KeyNotSet < StandardError; end
|
13
|
+
class UnknownApplication < StandardError; end
|
14
|
+
class AmbigousPath < StandardError; end
|
91
15
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
def consul_url(key_name)
|
98
|
-
URI("#{configuration.consul_url}/v1/kv/#{key_name}?raw")
|
99
|
-
end
|
100
|
-
|
101
|
-
def fetch_plain_key(key_name)
|
102
|
-
url = consul_url(key_name)
|
103
|
-
begin
|
104
|
-
response = Net::HTTP.get_response url
|
16
|
+
autoload :KeyValidator, 'muchkeys/key_validator'
|
17
|
+
autoload :ApplicationClient, 'muchkeys/application_client'
|
18
|
+
autoload :Secret, 'muchkeys/secret'
|
19
|
+
autoload :CLI, 'muchkeys/cli'
|
20
|
+
autoload :ConsulClient, 'muchkeys/consul_client'
|
105
21
|
|
106
|
-
|
107
|
-
response.body
|
108
|
-
rescue Errno::ECONNREFUSED
|
109
|
-
return nil
|
110
|
-
end
|
111
|
-
end
|
22
|
+
include ActiveSupport::Configurable
|
112
23
|
|
113
|
-
|
114
|
-
result = fetch_plain_key(key_name)
|
24
|
+
config_accessor :application_name
|
115
25
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
secret_adapter.decrypt_string(result, public_pem, private_pem)
|
120
|
-
end
|
121
|
-
|
122
|
-
def find_certfile_for_key(key_name)
|
123
|
-
secret_adapter.certfile_name key_name
|
124
|
-
end
|
26
|
+
config_accessor :consul_url do
|
27
|
+
'http://localhost:8500'
|
28
|
+
end
|
125
29
|
|
126
|
-
|
127
|
-
|
128
|
-
|
30
|
+
config_accessor :private_key
|
31
|
+
config_accessor :public_key
|
32
|
+
config_accessor :application_name
|
33
|
+
config_accessor :secrets_hint
|
34
|
+
config_accessor :search_paths do
|
35
|
+
%w(git/%{application_name}/secrets
|
36
|
+
git/%{application_name}/config
|
37
|
+
git/shared/secrets
|
38
|
+
git/shared/config)
|
39
|
+
end
|
129
40
|
|
130
|
-
|
131
|
-
|
41
|
+
def self.env_keys
|
42
|
+
# parse all environments found in .env and populate them from consul
|
43
|
+
if defined? ::Rails
|
44
|
+
check_dir = ::Rails.root
|
45
|
+
else
|
46
|
+
check_dir = Pathname.getwd
|
132
47
|
end
|
133
48
|
|
134
|
-
# Detecting Rails app names is a known quantity.
|
135
|
-
# Rack apps need to set the name through config.
|
136
|
-
def application_name
|
137
|
-
return configuration.application_name if configuration.application_name
|
138
|
-
|
139
|
-
if defined?(Rails)
|
140
|
-
# Rails.application looks something like "Monorail::Application"
|
141
|
-
application_name = Rails.application.class.to_s.split("::").first
|
142
|
-
else
|
143
|
-
return false
|
144
|
-
end
|
145
|
-
|
146
|
-
snakecase(application_name)
|
147
|
-
end
|
148
49
|
|
149
|
-
|
150
|
-
|
151
|
-
string.gsub(/::/, '/').
|
152
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
153
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
154
|
-
tr("-", "_").
|
155
|
-
downcase
|
50
|
+
unless File.exists?(check_dir.join(".env"))
|
51
|
+
raise IOError, ".env files are required for Muchkeys ENV injection to work"
|
156
52
|
end
|
157
53
|
|
54
|
+
File.read(check_dir.join(".env")).each_line.select(&:presence).map { |x| x.split("=")[0] }
|
158
55
|
end
|
159
|
-
end
|
160
|
-
|
161
|
-
# default configure the gem on gem load
|
162
|
-
MuchKeys.configuration ||= MuchKeys::Configuration.new
|
163
|
-
|
164
56
|
|
165
|
-
|
166
|
-
|
57
|
+
def self.populate_environment!(*required_keys)
|
58
|
+
app = ApplicationClient.new(config)
|
59
|
+
app.verify_keys(*required_keys)
|
167
60
|
|
168
|
-
|
169
|
-
|
170
|
-
|
61
|
+
app.known_keys.each do |key|
|
62
|
+
ENV[key] ||= app.first(key)
|
63
|
+
end
|
171
64
|
|
172
|
-
|
65
|
+
nil
|
173
66
|
end
|
174
|
-
|
175
67
|
end
|
data/muchkeys.gemspec
CHANGED
@@ -5,12 +5,12 @@ require 'muchkeys/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "muchkeys"
|
8
|
-
spec.version =
|
8
|
+
spec.version = Muchkeys::VERSION
|
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{
|
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
|
|
@@ -20,11 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
spec.add_runtime_dependency "thor"
|
23
|
+
spec.add_runtime_dependency "activesupport", "< 5.1"
|
23
24
|
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.10"
|
25
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
27
|
spec.add_development_dependency "rspec", "~> 3.3"
|
27
|
-
spec.add_development_dependency "webmock", "~> 1.20"
|
28
|
-
spec.add_development_dependency "vcr", "~> 2.9"
|
29
28
|
spec.add_development_dependency "aruba", "~> 0.14.2"
|
30
29
|
end
|
data/secret.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
it's a seekwet
|
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.7.0
|
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-
|
12
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: activesupport
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "<"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.1'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "<"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.1'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: bundler
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,34 +81,6 @@ dependencies:
|
|
67
81
|
- - "~>"
|
68
82
|
- !ruby/object:Gem::Version
|
69
83
|
version: '3.3'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: webmock
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - "~>"
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '1.20'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '1.20'
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: vcr
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '2.9'
|
91
|
-
type: :development
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - "~>"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '2.9'
|
98
84
|
- !ruby/object:Gem::Dependency
|
99
85
|
name: aruba
|
100
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +95,7 @@ dependencies:
|
|
109
95
|
- - "~>"
|
110
96
|
- !ruby/object:Gem::Version
|
111
97
|
version: 0.14.2
|
112
|
-
description:
|
98
|
+
description: Muchkeys can handle app configuration and appsecrets
|
113
99
|
email:
|
114
100
|
- pobrien@goldstar.com
|
115
101
|
- cdillon@goldstar.com
|
@@ -122,22 +108,25 @@ files:
|
|
122
108
|
- ".rspec"
|
123
109
|
- ".ruby-version"
|
124
110
|
- ".travis.yml"
|
111
|
+
- Dockerfile
|
125
112
|
- Gemfile
|
126
113
|
- Guardfile
|
127
114
|
- LICENSE.txt
|
128
115
|
- README.md
|
129
116
|
- Rakefile
|
130
117
|
- circle.yml
|
118
|
+
- docker-compose.yml
|
131
119
|
- exe/muchkeys
|
132
120
|
- lib/muchkeys.rb
|
121
|
+
- lib/muchkeys/application_client.rb
|
133
122
|
- lib/muchkeys/cli.rb
|
134
|
-
- lib/muchkeys/
|
135
|
-
- lib/muchkeys/errors.rb
|
123
|
+
- lib/muchkeys/consul_client.rb
|
136
124
|
- lib/muchkeys/key_validator.rb
|
137
125
|
- lib/muchkeys/rails.rb
|
138
126
|
- lib/muchkeys/secret.rb
|
139
127
|
- lib/muchkeys/version.rb
|
140
128
|
- muchkeys.gemspec
|
129
|
+
- secret.txt
|
141
130
|
homepage: https://www.goldstar.com
|
142
131
|
licenses:
|
143
132
|
- MIT
|
@@ -161,5 +150,5 @@ rubyforge_project:
|
|
161
150
|
rubygems_version: 2.5.1
|
162
151
|
signing_key:
|
163
152
|
specification_version: 4
|
164
|
-
summary:
|
153
|
+
summary: Muchkeys fetches keys from the ENV and then falls back to consul
|
165
154
|
test_files: []
|
@@ -1,30 +0,0 @@
|
|
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
|