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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +26 -0
  5. data/CHANGELOG.md +228 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +362 -0
  8. data/README.md +214 -0
  9. data/Rakefile +6 -0
  10. data/lib/vault/api/approle.rb +218 -0
  11. data/lib/vault/api/auth.rb +316 -0
  12. data/lib/vault/api/auth_tls.rb +92 -0
  13. data/lib/vault/api/auth_token.rb +242 -0
  14. data/lib/vault/api/help.rb +33 -0
  15. data/lib/vault/api/logical.rb +150 -0
  16. data/lib/vault/api/secret.rb +156 -0
  17. data/lib/vault/api/sys/audit.rb +91 -0
  18. data/lib/vault/api/sys/auth.rb +116 -0
  19. data/lib/vault/api/sys/health.rb +63 -0
  20. data/lib/vault/api/sys/init.rb +83 -0
  21. data/lib/vault/api/sys/leader.rb +48 -0
  22. data/lib/vault/api/sys/lease.rb +49 -0
  23. data/lib/vault/api/sys/mount.rb +103 -0
  24. data/lib/vault/api/sys/policy.rb +92 -0
  25. data/lib/vault/api/sys/seal.rb +81 -0
  26. data/lib/vault/api/sys.rb +25 -0
  27. data/lib/vault/api.rb +12 -0
  28. data/lib/vault/client.rb +447 -0
  29. data/lib/vault/configurable.rb +48 -0
  30. data/lib/vault/defaults.rb +197 -0
  31. data/lib/vault/encode.rb +19 -0
  32. data/lib/vault/errors.rb +72 -0
  33. data/lib/vault/persistent/connection.rb +42 -0
  34. data/lib/vault/persistent/pool.rb +48 -0
  35. data/lib/vault/persistent/timed_stack_multi.rb +70 -0
  36. data/lib/vault/persistent.rb +1158 -0
  37. data/lib/vault/request.rb +43 -0
  38. data/lib/vault/response.rb +89 -0
  39. data/lib/vault/vendor/connection_pool/timed_stack.rb +178 -0
  40. data/lib/vault/vendor/connection_pool/version.rb +5 -0
  41. data/lib/vault/vendor/connection_pool.rb +150 -0
  42. data/lib/vault/version.rb +3 -0
  43. data/lib/vault.rb +49 -0
  44. data/vault.gemspec +30 -0
  45. metadata +185 -0
@@ -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
@@ -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