dd-vault 0.12.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 +7 -0
- data/.gitignore +42 -0
- data/.rspec +2 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +228 -0
- data/Gemfile +3 -0
- data/LICENSE +362 -0
- data/README.md +214 -0
- data/Rakefile +6 -0
- data/lib/vault/api/approle.rb +218 -0
- data/lib/vault/api/auth.rb +316 -0
- data/lib/vault/api/auth_tls.rb +92 -0
- data/lib/vault/api/auth_token.rb +242 -0
- data/lib/vault/api/help.rb +33 -0
- data/lib/vault/api/logical.rb +150 -0
- data/lib/vault/api/secret.rb +156 -0
- data/lib/vault/api/sys/audit.rb +91 -0
- data/lib/vault/api/sys/auth.rb +116 -0
- data/lib/vault/api/sys/health.rb +63 -0
- data/lib/vault/api/sys/init.rb +83 -0
- data/lib/vault/api/sys/leader.rb +48 -0
- data/lib/vault/api/sys/lease.rb +49 -0
- data/lib/vault/api/sys/mount.rb +103 -0
- data/lib/vault/api/sys/policy.rb +92 -0
- data/lib/vault/api/sys/seal.rb +81 -0
- data/lib/vault/api/sys.rb +25 -0
- data/lib/vault/api.rb +12 -0
- data/lib/vault/client.rb +447 -0
- data/lib/vault/configurable.rb +48 -0
- data/lib/vault/defaults.rb +197 -0
- data/lib/vault/encode.rb +19 -0
- data/lib/vault/errors.rb +72 -0
- data/lib/vault/persistent/connection.rb +42 -0
- data/lib/vault/persistent/pool.rb +48 -0
- data/lib/vault/persistent/timed_stack_multi.rb +70 -0
- data/lib/vault/persistent.rb +1158 -0
- data/lib/vault/request.rb +43 -0
- data/lib/vault/response.rb +89 -0
- data/lib/vault/vendor/connection_pool/timed_stack.rb +178 -0
- data/lib/vault/vendor/connection_pool/version.rb +5 -0
- data/lib/vault/vendor/connection_pool.rb +150 -0
- data/lib/vault/version.rb +3 -0
- data/lib/vault.rb +49 -0
- data/vault.gemspec +30 -0
- metadata +185 -0
data/lib/vault/client.rb
ADDED
@@ -0,0 +1,447 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "json"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
require_relative "persistent"
|
6
|
+
require_relative "configurable"
|
7
|
+
require_relative "errors"
|
8
|
+
require_relative "version"
|
9
|
+
require_relative "encode"
|
10
|
+
|
11
|
+
module Vault
|
12
|
+
class Client
|
13
|
+
# The user agent for this client.
|
14
|
+
USER_AGENT = "VaultRuby/#{Vault::VERSION} (+github.com/hashicorp/vault-ruby)".freeze
|
15
|
+
|
16
|
+
# The name of the header used to hold the Vault token.
|
17
|
+
TOKEN_HEADER = "X-Vault-Token".freeze
|
18
|
+
|
19
|
+
# The name of the header used to hold the wrapped request ttl.
|
20
|
+
WRAP_TTL_HEADER = "X-Vault-Wrap-TTL".freeze
|
21
|
+
|
22
|
+
# The name of the header used for redirection.
|
23
|
+
LOCATION_HEADER = "location".freeze
|
24
|
+
|
25
|
+
# The default headers that are sent with every request.
|
26
|
+
DEFAULT_HEADERS = {
|
27
|
+
"Content-Type" => "application/json",
|
28
|
+
"Accept" => "application/json",
|
29
|
+
"User-Agent" => USER_AGENT,
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# The default list of options to use when parsing JSON.
|
33
|
+
JSON_PARSE_OPTIONS = {
|
34
|
+
max_nesting: false,
|
35
|
+
create_additions: false,
|
36
|
+
symbolize_names: true,
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
RESCUED_EXCEPTIONS = [].tap do |a|
|
40
|
+
# Failure to even open the socket (usually permissions)
|
41
|
+
a << SocketError
|
42
|
+
|
43
|
+
# Failed to reach the server (aka bad URL)
|
44
|
+
a << Errno::ECONNREFUSED
|
45
|
+
|
46
|
+
# Failed to read body or no response body given
|
47
|
+
a << EOFError
|
48
|
+
|
49
|
+
# Timeout (Ruby 1.9-)
|
50
|
+
a << Timeout::Error
|
51
|
+
|
52
|
+
# Timeout (Ruby 1.9+) - Ruby 1.9 does not define these constants so we
|
53
|
+
# only add them if they are defiend
|
54
|
+
a << Net::ReadTimeout if defined?(Net::ReadTimeout)
|
55
|
+
a << Net::OpenTimeout if defined?(Net::OpenTimeout)
|
56
|
+
|
57
|
+
a << PersistentHTTP::Error
|
58
|
+
end.freeze
|
59
|
+
|
60
|
+
# Indicates a requested operation is not possible due to security
|
61
|
+
# concerns.
|
62
|
+
class SecurityError < RuntimeError
|
63
|
+
end
|
64
|
+
|
65
|
+
include Vault::Configurable
|
66
|
+
|
67
|
+
# Create a new Client with the given options. Any options given take
|
68
|
+
# precedence over the default options.
|
69
|
+
#
|
70
|
+
# @return [Vault::Client]
|
71
|
+
def initialize(options = {})
|
72
|
+
# Use any options given, but fall back to the defaults set on the module
|
73
|
+
Vault::Configurable.keys.each do |key|
|
74
|
+
value = options.key?(key) ? options[key] : Defaults.public_send(key)
|
75
|
+
instance_variable_set(:"@#{key}", value)
|
76
|
+
end
|
77
|
+
|
78
|
+
@lock = Mutex.new
|
79
|
+
@nhp = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def pool
|
83
|
+
@lock.synchronize do
|
84
|
+
return @nhp if @nhp
|
85
|
+
|
86
|
+
@nhp = PersistentHTTP.new("vault-ruby", nil, pool_size)
|
87
|
+
|
88
|
+
if proxy_address
|
89
|
+
proxy_uri = URI.parse "http://#{proxy_address}"
|
90
|
+
|
91
|
+
proxy_uri.port = proxy_port if proxy_port
|
92
|
+
|
93
|
+
if proxy_username
|
94
|
+
proxy_uri.user = proxy_username
|
95
|
+
proxy_uri.password = proxy_password
|
96
|
+
end
|
97
|
+
|
98
|
+
@nhp.proxy = proxy_uri
|
99
|
+
end
|
100
|
+
|
101
|
+
# Use a custom open timeout
|
102
|
+
if open_timeout || timeout
|
103
|
+
@nhp.open_timeout = (open_timeout || timeout).to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
# Use a custom read timeout
|
107
|
+
if read_timeout || timeout
|
108
|
+
@nhp.read_timeout = (read_timeout || timeout).to_i
|
109
|
+
end
|
110
|
+
|
111
|
+
@nhp.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
112
|
+
|
113
|
+
# Vault requires TLS1.2
|
114
|
+
@nhp.ssl_version = "TLSv1_2"
|
115
|
+
|
116
|
+
# Only use secure ciphers
|
117
|
+
@nhp.ciphers = ssl_ciphers
|
118
|
+
|
119
|
+
# Custom pem files, no problem!
|
120
|
+
pem = ssl_pem_contents || (ssl_pem_file ? File.read(ssl_pem_file) : nil)
|
121
|
+
if pem
|
122
|
+
@nhp.cert = OpenSSL::X509::Certificate.new(pem)
|
123
|
+
@nhp.key = OpenSSL::PKey::RSA.new(pem, ssl_pem_passphrase)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Use custom CA cert for verification
|
127
|
+
if ssl_ca_cert
|
128
|
+
@nhp.ca_file = ssl_ca_cert
|
129
|
+
end
|
130
|
+
|
131
|
+
# Use custom CA path that contains CA certs
|
132
|
+
if ssl_ca_path
|
133
|
+
@nhp.ca_path = ssl_ca_path
|
134
|
+
end
|
135
|
+
|
136
|
+
if ssl_cert_store
|
137
|
+
@nhp.cert_store = ssl_cert_store
|
138
|
+
end
|
139
|
+
|
140
|
+
# Naughty, naughty, naughty! Don't blame me when someone hops in
|
141
|
+
# and executes a MITM attack!
|
142
|
+
if !ssl_verify
|
143
|
+
@nhp.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
144
|
+
end
|
145
|
+
|
146
|
+
# Use custom timeout for connecting and verifying via SSL
|
147
|
+
if ssl_timeout || timeout
|
148
|
+
@nhp.ssl_timeout = (ssl_timeout || timeout).to_i
|
149
|
+
end
|
150
|
+
|
151
|
+
@nhp
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private :pool
|
156
|
+
|
157
|
+
# Shutdown any open pool connections. Pool will be recreated upon next request.
|
158
|
+
def shutdown
|
159
|
+
@nhp.shutdown()
|
160
|
+
@nhp = nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# Creates and yields a new client object with the given token. This may be
|
164
|
+
# used safely in a threadsafe manner because the original client remains
|
165
|
+
# unchanged. The value of the block is returned.
|
166
|
+
#
|
167
|
+
# @yield [Vault::Client]
|
168
|
+
def with_token(token)
|
169
|
+
client = self.dup
|
170
|
+
client.token = token
|
171
|
+
return yield client if block_given?
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
|
175
|
+
# Determine if the given options are the same as ours.
|
176
|
+
# @return [true, false]
|
177
|
+
def same_options?(opts)
|
178
|
+
options.hash == opts.hash
|
179
|
+
end
|
180
|
+
|
181
|
+
# Perform a GET request.
|
182
|
+
# @see Client#request
|
183
|
+
def get(path, params = {}, headers = {})
|
184
|
+
request(:get, path, params, headers)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Perform a LIST request.
|
188
|
+
# @see Client#request
|
189
|
+
def list(path, params = {}, headers = {})
|
190
|
+
params = params.merge(list: true)
|
191
|
+
request(:get, path, params, headers)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Perform a POST request.
|
195
|
+
# @see Client#request
|
196
|
+
def post(path, data = {}, headers = {})
|
197
|
+
request(:post, path, data, headers)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Perform a PUT request.
|
201
|
+
# @see Client#request
|
202
|
+
def put(path, data, headers = {})
|
203
|
+
request(:put, path, data, headers)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Perform a PATCH request.
|
207
|
+
# @see Client#request
|
208
|
+
def patch(path, data, headers = {})
|
209
|
+
request(:patch, path, data, headers)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Perform a DELETE request.
|
213
|
+
# @see Client#request
|
214
|
+
def delete(path, params = {}, headers = {})
|
215
|
+
request(:delete, path, params, headers)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Make an HTTP request with the given verb, data, params, and headers. If
|
219
|
+
# the response has a return type of JSON, the JSON is automatically parsed
|
220
|
+
# and returned as a hash; otherwise it is returned as a string.
|
221
|
+
#
|
222
|
+
# @raise [HTTPError]
|
223
|
+
# if the request is not an HTTP 200 OK
|
224
|
+
#
|
225
|
+
# @param [Symbol] verb
|
226
|
+
# the lowercase symbol of the HTTP verb (e.g. :get, :delete)
|
227
|
+
# @param [String] path
|
228
|
+
# the absolute or relative path from {Defaults.address} to make the
|
229
|
+
# request against
|
230
|
+
# @param [#read, Hash, nil] data
|
231
|
+
# the data to use (varies based on the +verb+)
|
232
|
+
# @param [Hash] headers
|
233
|
+
# the list of headers to use
|
234
|
+
#
|
235
|
+
# @return [String, Hash]
|
236
|
+
# the response body
|
237
|
+
def request(verb, path, data = {}, headers = {})
|
238
|
+
# Build the URI and request object from the given information
|
239
|
+
uri = build_uri(verb, path, data)
|
240
|
+
request = class_for_request(verb).new(uri.request_uri)
|
241
|
+
if uri.userinfo()
|
242
|
+
request.basic_auth uri.user, uri.password
|
243
|
+
end
|
244
|
+
|
245
|
+
if proxy_address and uri.scheme.downcase == "https"
|
246
|
+
raise SecurityError, "no direct https connection to vault"
|
247
|
+
end
|
248
|
+
|
249
|
+
# Get a list of headers
|
250
|
+
headers = DEFAULT_HEADERS.merge(headers)
|
251
|
+
|
252
|
+
# Add the Vault token header - users could still override this on a
|
253
|
+
# per-request basis
|
254
|
+
if !token.nil?
|
255
|
+
headers[TOKEN_HEADER] ||= token
|
256
|
+
end
|
257
|
+
|
258
|
+
# Add headers
|
259
|
+
headers.each do |key, value|
|
260
|
+
request.add_field(key, value)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Setup PATCH/POST/PUT
|
264
|
+
if [:patch, :post, :put].include?(verb)
|
265
|
+
if data.respond_to?(:read)
|
266
|
+
request.content_length = data.size
|
267
|
+
request.body_stream = data
|
268
|
+
elsif data.is_a?(Hash)
|
269
|
+
request.form_data = data
|
270
|
+
else
|
271
|
+
request.body = data
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
begin
|
276
|
+
# Create a connection using the block form, which will ensure the socket
|
277
|
+
# is properly closed in the event of an error.
|
278
|
+
response = pool.request(uri, request)
|
279
|
+
|
280
|
+
case response
|
281
|
+
when Net::HTTPRedirection
|
282
|
+
# On a redirect of a GET or HEAD request, the URL already contains
|
283
|
+
# the data as query string parameters.
|
284
|
+
if [:head, :get].include?(verb)
|
285
|
+
data = {}
|
286
|
+
end
|
287
|
+
request(verb, response[LOCATION_HEADER], data, headers)
|
288
|
+
when Net::HTTPSuccess
|
289
|
+
success(response)
|
290
|
+
else
|
291
|
+
error(response)
|
292
|
+
end
|
293
|
+
rescue *RESCUED_EXCEPTIONS => e
|
294
|
+
raise HTTPConnectionError.new(address, e)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Construct a URL from the given verb and path. If the request is a GET or
|
299
|
+
# DELETE request, the params are assumed to be query params are are
|
300
|
+
# converted as such using {Client#to_query_string}.
|
301
|
+
#
|
302
|
+
# If the path is relative, it is merged with the {Defaults.address}
|
303
|
+
# attribute. If the path is absolute, it is converted to a URI object and
|
304
|
+
# returned.
|
305
|
+
#
|
306
|
+
# @param [Symbol] verb
|
307
|
+
# the lowercase HTTP verb (e.g. :+get+)
|
308
|
+
# @param [String] path
|
309
|
+
# the absolute or relative HTTP path (url) to get
|
310
|
+
# @param [Hash] params
|
311
|
+
# the list of params to build the URI with (for GET and DELETE requests)
|
312
|
+
#
|
313
|
+
# @return [URI]
|
314
|
+
def build_uri(verb, path, params = {})
|
315
|
+
# Add any query string parameters
|
316
|
+
if [:delete, :get].include?(verb)
|
317
|
+
path = [path, to_query_string(params)].compact.join("?")
|
318
|
+
end
|
319
|
+
|
320
|
+
# Parse the URI
|
321
|
+
uri = URI.parse(path)
|
322
|
+
|
323
|
+
# Don't merge absolute URLs
|
324
|
+
uri = URI.parse(File.join(address, path)) unless uri.absolute?
|
325
|
+
|
326
|
+
# Return the URI object
|
327
|
+
uri
|
328
|
+
end
|
329
|
+
|
330
|
+
# Helper method to get the corresponding {Net::HTTP} class from the given
|
331
|
+
# HTTP verb.
|
332
|
+
#
|
333
|
+
# @param [#to_s] verb
|
334
|
+
# the HTTP verb to create a class from
|
335
|
+
#
|
336
|
+
# @return [Class]
|
337
|
+
def class_for_request(verb)
|
338
|
+
Net::HTTP.const_get(verb.to_s.capitalize)
|
339
|
+
end
|
340
|
+
|
341
|
+
# Convert the given hash to a list of query string parameters. Each key and
|
342
|
+
# value in the hash is URI-escaped for safety.
|
343
|
+
#
|
344
|
+
# @param [Hash] hash
|
345
|
+
# the hash to create the query string from
|
346
|
+
#
|
347
|
+
# @return [String, nil]
|
348
|
+
# the query string as a string, or +nil+ if there are no params
|
349
|
+
def to_query_string(hash)
|
350
|
+
hash.map do |key, value|
|
351
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
352
|
+
end.join('&')[/.+/]
|
353
|
+
end
|
354
|
+
|
355
|
+
# Parse the response object and manipulate the result based on the given
|
356
|
+
# +Content-Type+ header. For now, this method only parses JSON, but it
|
357
|
+
# could be expanded in the future to accept other content types.
|
358
|
+
#
|
359
|
+
# @param [HTTP::Message] response
|
360
|
+
# the response object from the request
|
361
|
+
#
|
362
|
+
# @return [String, Hash]
|
363
|
+
# the parsed response, as an object
|
364
|
+
def success(response)
|
365
|
+
if response.body && (response.content_type || '').include?("json")
|
366
|
+
JSON.parse(response.body, JSON_PARSE_OPTIONS)
|
367
|
+
else
|
368
|
+
response.body
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Raise a response error, extracting as much information from the server's
|
373
|
+
# response as possible.
|
374
|
+
#
|
375
|
+
# @raise [HTTPError]
|
376
|
+
#
|
377
|
+
# @param [HTTP::Message] response
|
378
|
+
# the response object from the request
|
379
|
+
def error(response)
|
380
|
+
if response.body && response.body.match("missing client token")
|
381
|
+
raise MissingTokenError
|
382
|
+
end
|
383
|
+
|
384
|
+
# Use the correct exception class
|
385
|
+
case response
|
386
|
+
when Net::HTTPClientError
|
387
|
+
klass = HTTPClientError
|
388
|
+
when Net::HTTPServerError
|
389
|
+
klass = HTTPServerError
|
390
|
+
else
|
391
|
+
klass = HTTPError
|
392
|
+
end
|
393
|
+
|
394
|
+
if (response.content_type || '').include?("json")
|
395
|
+
# Attempt to parse the error as JSON
|
396
|
+
begin
|
397
|
+
json = JSON.parse(response.body, JSON_PARSE_OPTIONS)
|
398
|
+
|
399
|
+
if json[:errors]
|
400
|
+
raise klass.new(address, response, json[:errors])
|
401
|
+
end
|
402
|
+
rescue JSON::ParserError; end
|
403
|
+
end
|
404
|
+
|
405
|
+
raise klass.new(address, response, [response.body])
|
406
|
+
end
|
407
|
+
|
408
|
+
# Execute the given block with retries and exponential backoff.
|
409
|
+
#
|
410
|
+
# @param [Array<Exception>] rescued
|
411
|
+
# the list of exceptions to rescue
|
412
|
+
def with_retries(*rescued, &block)
|
413
|
+
options = rescued.last.is_a?(Hash) ? rescued.pop : {}
|
414
|
+
exception = nil
|
415
|
+
retries = 0
|
416
|
+
|
417
|
+
rescued = Defaults::RETRIED_EXCEPTIONS if rescued.empty?
|
418
|
+
|
419
|
+
max_attempts = options[:attempts] || Defaults::RETRY_ATTEMPTS
|
420
|
+
backoff_base = options[:base] || Defaults::RETRY_BASE
|
421
|
+
backoff_max = options[:max_wait] || Defaults::RETRY_MAX_WAIT
|
422
|
+
|
423
|
+
begin
|
424
|
+
return yield retries, exception
|
425
|
+
rescue *rescued => e
|
426
|
+
exception = e
|
427
|
+
|
428
|
+
retries += 1
|
429
|
+
raise if retries > max_attempts
|
430
|
+
|
431
|
+
# Calculate the exponential backoff combined with an element of
|
432
|
+
# randomness.
|
433
|
+
backoff = [backoff_base * (2 ** (retries - 1)), backoff_max].min
|
434
|
+
backoff = backoff * (0.5 * (1 + Kernel.rand))
|
435
|
+
|
436
|
+
# Ensure we are sleeping at least the minimum interval.
|
437
|
+
backoff = [backoff_base, backoff].max
|
438
|
+
|
439
|
+
# Exponential backoff.
|
440
|
+
Kernel.sleep(backoff)
|
441
|
+
|
442
|
+
# Now retry
|
443
|
+
retry
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative "defaults"
|
2
|
+
|
3
|
+
module Vault
|
4
|
+
module Configurable
|
5
|
+
def self.keys
|
6
|
+
@keys ||= [
|
7
|
+
:address,
|
8
|
+
:token,
|
9
|
+
:hostname,
|
10
|
+
:open_timeout,
|
11
|
+
:proxy_address,
|
12
|
+
:proxy_password,
|
13
|
+
:proxy_port,
|
14
|
+
:proxy_username,
|
15
|
+
:pool_size,
|
16
|
+
:read_timeout,
|
17
|
+
:ssl_ciphers,
|
18
|
+
:ssl_pem_contents,
|
19
|
+
:ssl_pem_file,
|
20
|
+
:ssl_pem_passphrase,
|
21
|
+
:ssl_ca_cert,
|
22
|
+
:ssl_ca_path,
|
23
|
+
:ssl_cert_store,
|
24
|
+
:ssl_verify,
|
25
|
+
:ssl_timeout,
|
26
|
+
:timeout,
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
Vault::Configurable.keys.each(&method(:attr_accessor))
|
31
|
+
|
32
|
+
# Configure yields self for block-style configuration.
|
33
|
+
#
|
34
|
+
# @yield [self]
|
35
|
+
def configure
|
36
|
+
yield self
|
37
|
+
end
|
38
|
+
|
39
|
+
# The list of options for this configurable.
|
40
|
+
#
|
41
|
+
# @return [Hash<Symbol, Object>]
|
42
|
+
def options
|
43
|
+
Hash[*Vault::Configurable.keys.map do |key|
|
44
|
+
[key, instance_variable_get(:"@#{key}")]
|
45
|
+
end.flatten]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "base64"
|
3
|
+
|
4
|
+
module Vault
|
5
|
+
module Defaults
|
6
|
+
# The default vault address.
|
7
|
+
# @return [String]
|
8
|
+
VAULT_ADDRESS = "https://127.0.0.1:8200".freeze
|
9
|
+
|
10
|
+
# The path to the vault token on disk.
|
11
|
+
# @return [String]
|
12
|
+
VAULT_DISK_TOKEN = Pathname.new("#{ENV["HOME"]}/.vault-token").expand_path.freeze
|
13
|
+
|
14
|
+
# The list of SSL ciphers to allow. You should not change this value unless
|
15
|
+
# you absolutely know what you are doing!
|
16
|
+
# @return [String]
|
17
|
+
SSL_CIPHERS = "TLSv1.2:!aNULL:!eNULL".freeze
|
18
|
+
|
19
|
+
# The default number of attempts.
|
20
|
+
# @return [Fixnum]
|
21
|
+
RETRY_ATTEMPTS = 2
|
22
|
+
|
23
|
+
# The default backoff interval.
|
24
|
+
# @return [Fixnum]
|
25
|
+
RETRY_BASE = 0.05
|
26
|
+
|
27
|
+
# The maximum amount of time for a single exponential backoff to sleep.
|
28
|
+
RETRY_MAX_WAIT = 2.0
|
29
|
+
|
30
|
+
# The default size of the connection pool
|
31
|
+
DEFAULT_POOL_SIZE = 16
|
32
|
+
|
33
|
+
# The set of exceptions that are detect and retried by default
|
34
|
+
# with `with_retries`
|
35
|
+
RETRIED_EXCEPTIONS = [HTTPServerError]
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# The list of calculated options for this configurable.
|
39
|
+
# @return [Hash]
|
40
|
+
def options
|
41
|
+
Hash[*Configurable.keys.map { |key| [key, public_send(key)] }.flatten]
|
42
|
+
end
|
43
|
+
|
44
|
+
# The address to communicate with Vault.
|
45
|
+
# @return [String]
|
46
|
+
def address
|
47
|
+
ENV["VAULT_ADDR"] || VAULT_ADDRESS
|
48
|
+
end
|
49
|
+
|
50
|
+
# The vault token to use for authentiation.
|
51
|
+
# @return [String, nil]
|
52
|
+
def token
|
53
|
+
if !ENV["VAULT_TOKEN"].nil?
|
54
|
+
return ENV["VAULT_TOKEN"]
|
55
|
+
end
|
56
|
+
|
57
|
+
if VAULT_DISK_TOKEN.exist? && VAULT_DISK_TOKEN.readable?
|
58
|
+
return VAULT_DISK_TOKEN.read.chomp
|
59
|
+
end
|
60
|
+
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# The SNI host to use when connecting to Vault via TLS.
|
65
|
+
# @return [String, nil]
|
66
|
+
def hostname
|
67
|
+
ENV["VAULT_TLS_SERVER_NAME"]
|
68
|
+
end
|
69
|
+
|
70
|
+
# The number of seconds to wait when trying to open a connection before
|
71
|
+
# timing out
|
72
|
+
# @return [String, nil]
|
73
|
+
def open_timeout
|
74
|
+
ENV["VAULT_OPEN_TIMEOUT"]
|
75
|
+
end
|
76
|
+
|
77
|
+
# The size of the connection pool to communicate with Vault
|
78
|
+
# @return Integer
|
79
|
+
def pool_size
|
80
|
+
if var = ENV["VAULT_POOL_SIZE"]
|
81
|
+
return var.to_i
|
82
|
+
else
|
83
|
+
DEFAULT_POOL_SIZE
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# The HTTP Proxy server address as a string
|
88
|
+
# @return [String, nil]
|
89
|
+
def proxy_address
|
90
|
+
ENV["VAULT_PROXY_ADDRESS"]
|
91
|
+
end
|
92
|
+
|
93
|
+
# The HTTP Proxy server username as a string
|
94
|
+
# @return [String, nil]
|
95
|
+
def proxy_username
|
96
|
+
ENV["VAULT_PROXY_USERNAME"]
|
97
|
+
end
|
98
|
+
|
99
|
+
# The HTTP Proxy user password as a string
|
100
|
+
# @return [String, nil]
|
101
|
+
def proxy_password
|
102
|
+
ENV["VAULT_PROXY_PASSWORD"]
|
103
|
+
end
|
104
|
+
|
105
|
+
# The HTTP Proxy server port as a string
|
106
|
+
# @return [String, nil]
|
107
|
+
def proxy_port
|
108
|
+
ENV["VAULT_PROXY_PORT"]
|
109
|
+
end
|
110
|
+
|
111
|
+
# The number of seconds to wait when reading a response before timing out
|
112
|
+
# @return [String, nil]
|
113
|
+
def read_timeout
|
114
|
+
ENV["VAULT_READ_TIMEOUT"]
|
115
|
+
end
|
116
|
+
|
117
|
+
# The ciphers that will be used when communicating with vault over ssl
|
118
|
+
# You should only change the defaults if the ciphers are not available on
|
119
|
+
# your platform and you know what you are doing
|
120
|
+
# @return [String]
|
121
|
+
def ssl_ciphers
|
122
|
+
ENV["VAULT_SSL_CIPHERS"] || SSL_CIPHERS
|
123
|
+
end
|
124
|
+
|
125
|
+
# The raw contents (as a string) for the pem file. To specify the path to
|
126
|
+
# the pem file, use {#ssl_pem_file} instead. This value is preferred over
|
127
|
+
# the value for {#ssl_pem_file}, if set.
|
128
|
+
# @return [String, nil]
|
129
|
+
def ssl_pem_contents
|
130
|
+
if ENV["VAULT_SSL_PEM_CONTENTS_BASE64"]
|
131
|
+
Base64.decode64(ENV["VAULT_SSL_PEM_CONTENTS_BASE64"])
|
132
|
+
else
|
133
|
+
ENV["VAULT_SSL_PEM_CONTENTS"]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# The path to a pem on disk to use with custom SSL verification
|
138
|
+
# @return [String, nil]
|
139
|
+
def ssl_pem_file
|
140
|
+
ENV["VAULT_SSL_CERT"] || ENV["VAULT_SSL_PEM_FILE"]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Passphrase to the pem file on disk to use with custom SSL verification
|
144
|
+
# @return [String, nil]
|
145
|
+
def ssl_pem_passphrase
|
146
|
+
ENV["VAULT_SSL_CERT_PASSPHRASE"]
|
147
|
+
end
|
148
|
+
|
149
|
+
# The path to the CA cert on disk to use for certificate verification
|
150
|
+
# @return [String, nil]
|
151
|
+
def ssl_ca_cert
|
152
|
+
ENV["VAULT_CACERT"]
|
153
|
+
end
|
154
|
+
|
155
|
+
# The CA cert store to use for certificate verification
|
156
|
+
# @return [OpenSSL::X509::Store, nil]
|
157
|
+
def ssl_cert_store
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
|
161
|
+
# The path to the directory on disk holding CA certs to use
|
162
|
+
# for certificate verification
|
163
|
+
# @return [String, nil]
|
164
|
+
def ssl_ca_path
|
165
|
+
ENV["VAULT_CAPATH"]
|
166
|
+
end
|
167
|
+
|
168
|
+
# Verify SSL requests (default: true)
|
169
|
+
# @return [true, false]
|
170
|
+
def ssl_verify
|
171
|
+
# Vault CLI uses this envvar, so accept it by precedence
|
172
|
+
if !ENV["VAULT_SKIP_VERIFY"].nil?
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
|
176
|
+
if ENV["VAULT_SSL_VERIFY"].nil?
|
177
|
+
true
|
178
|
+
else
|
179
|
+
%w[t y].include?(ENV["VAULT_SSL_VERIFY"].downcase[0])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# The number of seconds to wait for connecting and verifying SSL
|
184
|
+
# @return [String, nil]
|
185
|
+
def ssl_timeout
|
186
|
+
ENV["VAULT_SSL_TIMEOUT"]
|
187
|
+
end
|
188
|
+
|
189
|
+
# A default meta-attribute to set all timeout values - individually set
|
190
|
+
# timeout values will take precedence
|
191
|
+
# @return [String, nil]
|
192
|
+
def timeout
|
193
|
+
ENV["VAULT_TIMEOUT"]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/lib/vault/encode.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Vault
|
2
|
+
module EncodePath
|
3
|
+
|
4
|
+
# Encodes a string according to the rules for URL paths. This is
|
5
|
+
# used as opposed to CGI.escape because in a URL path, space
|
6
|
+
# needs to be escaped as %20 and CGI.escapes a space as +.
|
7
|
+
#
|
8
|
+
# @param [String]
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
def encode_path(path)
|
12
|
+
path.b.gsub(%r!([^a-zA-Z0-9_.-/]+)!) { |m|
|
13
|
+
'%' + m.unpack('H2' * m.bytesize).join('%').upcase
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
module_function :encode_path
|
18
|
+
end
|
19
|
+
end
|