muchkeys 0.5.0 → 0.7.0
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/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
|