vault_ruby_client 0.18.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +287 -0
  3. data/LICENSE +364 -0
  4. data/README.md +223 -0
  5. data/lib/vault/api/approle.rb +221 -0
  6. data/lib/vault/api/auth.rb +324 -0
  7. data/lib/vault/api/auth_tls.rb +95 -0
  8. data/lib/vault/api/auth_token.rb +245 -0
  9. data/lib/vault/api/help.rb +36 -0
  10. data/lib/vault/api/kv.rb +230 -0
  11. data/lib/vault/api/logical.rb +153 -0
  12. data/lib/vault/api/secret.rb +171 -0
  13. data/lib/vault/api/sys/audit.rb +94 -0
  14. data/lib/vault/api/sys/auth.rb +119 -0
  15. data/lib/vault/api/sys/health.rb +66 -0
  16. data/lib/vault/api/sys/init.rb +86 -0
  17. data/lib/vault/api/sys/leader.rb +51 -0
  18. data/lib/vault/api/sys/lease.rb +52 -0
  19. data/lib/vault/api/sys/mount.rb +165 -0
  20. data/lib/vault/api/sys/namespace.rb +86 -0
  21. data/lib/vault/api/sys/policy.rb +95 -0
  22. data/lib/vault/api/sys/quota.rb +110 -0
  23. data/lib/vault/api/sys/seal.rb +84 -0
  24. data/lib/vault/api/sys.rb +30 -0
  25. data/lib/vault/api/transform/alphabet.rb +46 -0
  26. data/lib/vault/api/transform/role.rb +45 -0
  27. data/lib/vault/api/transform/template.rb +57 -0
  28. data/lib/vault/api/transform/transformation.rb +64 -0
  29. data/lib/vault/api/transform.rb +32 -0
  30. data/lib/vault/api.rb +17 -0
  31. data/lib/vault/client.rb +460 -0
  32. data/lib/vault/configurable.rb +53 -0
  33. data/lib/vault/defaults.rb +218 -0
  34. data/lib/vault/encode.rb +22 -0
  35. data/lib/vault/errors.rb +87 -0
  36. data/lib/vault/persistent/connection.rb +45 -0
  37. data/lib/vault/persistent/pool.rb +51 -0
  38. data/lib/vault/persistent/timed_stack_multi.rb +73 -0
  39. data/lib/vault/persistent.rb +1161 -0
  40. data/lib/vault/request.rb +47 -0
  41. data/lib/vault/response.rb +92 -0
  42. data/lib/vault/vendor/connection_pool/timed_stack.rb +181 -0
  43. data/lib/vault/vendor/connection_pool/version.rb +8 -0
  44. data/lib/vault/vendor/connection_pool.rb +153 -0
  45. data/lib/vault/version.rb +6 -0
  46. data/lib/vault_ruby_client.rb +53 -0
  47. metadata +158 -0
@@ -0,0 +1,218 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ require "pathname"
5
+ require "base64"
6
+
7
+ module Vault
8
+ module Defaults
9
+ # The default vault address.
10
+ # @return [String]
11
+ VAULT_ADDRESS = "https://127.0.0.1:8200".freeze
12
+
13
+ # The default path to the vault token on disk.
14
+ # @return [String]
15
+ DEFAULT_VAULT_DISK_TOKEN = Pathname.new("#{ENV["HOME"]}/.vault-token").expand_path.freeze
16
+
17
+ # The list of SSL ciphers to allow. You should not change this value unless
18
+ # you absolutely know what you are doing!
19
+ # @return [String]
20
+ SSL_CIPHERS = "TLSv1.2:!aNULL:!eNULL".freeze
21
+
22
+ # The default number of attempts.
23
+ # @return [Fixnum]
24
+ RETRY_ATTEMPTS = 2
25
+
26
+ # The default backoff interval.
27
+ # @return [Fixnum]
28
+ RETRY_BASE = 0.05
29
+
30
+ # The maximum amount of time for a single exponential backoff to sleep.
31
+ RETRY_MAX_WAIT = 2.0
32
+
33
+ # The default size of the connection pool
34
+ DEFAULT_POOL_SIZE = 16
35
+
36
+ # The default timeout in seconds for retrieving a connection from the connection pool
37
+ DEFAULT_POOL_TIMEOUT = 0.5
38
+
39
+ # The set of exceptions that are detect and retried by default
40
+ # with `with_retries`
41
+ RETRIED_EXCEPTIONS = [HTTPServerError, MissingRequiredStateError]
42
+
43
+ class << self
44
+ # The list of calculated options for this configurable.
45
+ # @return [Hash]
46
+ def options
47
+ Hash[*Configurable.keys.map { |key| [key, public_send(key)] }.flatten]
48
+ end
49
+
50
+ # The address to communicate with Vault.
51
+ # @return [String]
52
+ def address
53
+ ENV["VAULT_ADDR"] || VAULT_ADDRESS
54
+ end
55
+
56
+ # The vault token to use for authentiation.
57
+ # @return [String, nil]
58
+ def token
59
+ ENV["VAULT_TOKEN"] || fetch_from_disk("VAULT_TOKEN_FILE")
60
+ end
61
+
62
+ def fetch_from_disk(env_var)
63
+ path = ENV[env_var] ? Pathname.new(ENV[env_var]) : DEFAULT_VAULT_DISK_TOKEN
64
+ if path.exist? && path.readable?
65
+ path.read.chomp
66
+ end
67
+ end
68
+
69
+ # Vault Namespace, if any.
70
+ # @return [String, nil]
71
+ def namespace
72
+ ENV["VAULT_NAMESPACE"]
73
+ end
74
+
75
+ # The SNI host to use when connecting to Vault via TLS.
76
+ # @return [String, nil]
77
+ def hostname
78
+ ENV["VAULT_TLS_SERVER_NAME"]
79
+ end
80
+
81
+ # The number of seconds to wait when trying to open a connection before
82
+ # timing out
83
+ # @return [String, nil]
84
+ def open_timeout
85
+ ENV["VAULT_OPEN_TIMEOUT"]
86
+ end
87
+
88
+ # The size of the connection pool to communicate with Vault
89
+ # @return Integer
90
+ def pool_size
91
+ if var = ENV["VAULT_POOL_SIZE"]
92
+ var.to_i
93
+ else
94
+ DEFAULT_POOL_SIZE
95
+ end
96
+ end
97
+
98
+ # The timeout for getting a connection from the connection pool that communicates with Vault
99
+ # @return Float
100
+ def pool_timeout
101
+ if var = ENV["VAULT_POOL_TIMEOUT"]
102
+ var.to_f
103
+ else
104
+ DEFAULT_POOL_TIMEOUT
105
+ end
106
+ end
107
+
108
+ # The HTTP Proxy server address as a string
109
+ # @return [String, nil]
110
+ def proxy_address
111
+ ENV["VAULT_PROXY_ADDRESS"]
112
+ end
113
+
114
+ # The HTTP Proxy server username as a string
115
+ # @return [String, nil]
116
+ def proxy_username
117
+ ENV["VAULT_PROXY_USERNAME"]
118
+ end
119
+
120
+ # The HTTP Proxy user password as a string
121
+ # @return [String, nil]
122
+ def proxy_password
123
+ ENV["VAULT_PROXY_PASSWORD"]
124
+ end
125
+
126
+ # The HTTP Proxy server port as a string
127
+ # @return [String, nil]
128
+ def proxy_port
129
+ ENV["VAULT_PROXY_PORT"]
130
+ end
131
+
132
+ # The number of seconds to wait when reading a response before timing out
133
+ # @return [String, nil]
134
+ def read_timeout
135
+ ENV["VAULT_READ_TIMEOUT"]
136
+ end
137
+
138
+ # The ciphers that will be used when communicating with vault over ssl
139
+ # You should only change the defaults if the ciphers are not available on
140
+ # your platform and you know what you are doing
141
+ # @return [String]
142
+ def ssl_ciphers
143
+ ENV["VAULT_SSL_CIPHERS"] || SSL_CIPHERS
144
+ end
145
+
146
+ # The raw contents (as a string) for the pem file. To specify the path to
147
+ # the pem file, use {#ssl_pem_file} instead. This value is preferred over
148
+ # the value for {#ssl_pem_file}, if set.
149
+ # @return [String, nil]
150
+ def ssl_pem_contents
151
+ if ENV["VAULT_SSL_PEM_CONTENTS_BASE64"]
152
+ Base64.decode64(ENV["VAULT_SSL_PEM_CONTENTS_BASE64"])
153
+ else
154
+ ENV["VAULT_SSL_PEM_CONTENTS"]
155
+ end
156
+ end
157
+
158
+ # The path to a pem on disk to use with custom SSL verification
159
+ # @return [String, nil]
160
+ def ssl_pem_file
161
+ ENV["VAULT_SSL_CERT"] || ENV["VAULT_SSL_PEM_FILE"]
162
+ end
163
+
164
+ # Passphrase to the pem file on disk to use with custom SSL verification
165
+ # @return [String, nil]
166
+ def ssl_pem_passphrase
167
+ ENV["VAULT_SSL_CERT_PASSPHRASE"]
168
+ end
169
+
170
+ # The path to the CA cert on disk to use for certificate verification
171
+ # @return [String, nil]
172
+ def ssl_ca_cert
173
+ ENV["VAULT_CACERT"]
174
+ end
175
+
176
+ # The CA cert store to use for certificate verification
177
+ # @return [OpenSSL::X509::Store, nil]
178
+ def ssl_cert_store
179
+ nil
180
+ end
181
+
182
+ # The path to the directory on disk holding CA certs to use
183
+ # for certificate verification
184
+ # @return [String, nil]
185
+ def ssl_ca_path
186
+ ENV["VAULT_CAPATH"]
187
+ end
188
+
189
+ # Verify SSL requests (default: true)
190
+ # @return [true, false]
191
+ def ssl_verify
192
+ # Vault CLI uses this envvar, so accept it by precedence
193
+ if !ENV["VAULT_SKIP_VERIFY"].nil?
194
+ return false
195
+ end
196
+
197
+ if ENV["VAULT_SSL_VERIFY"].nil?
198
+ true
199
+ else
200
+ %w[t y].include?(ENV["VAULT_SSL_VERIFY"].downcase[0])
201
+ end
202
+ end
203
+
204
+ # The number of seconds to wait for connecting and verifying SSL
205
+ # @return [String, nil]
206
+ def ssl_timeout
207
+ ENV["VAULT_SSL_TIMEOUT"]
208
+ end
209
+
210
+ # A default meta-attribute to set all timeout values - individually set
211
+ # timeout values will take precedence
212
+ # @return [String, nil]
213
+ def timeout
214
+ ENV["VAULT_TIMEOUT"]
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ module Vault
5
+ module EncodePath
6
+
7
+ # Encodes a string according to the rules for URL paths. This is
8
+ # used as opposed to CGI.escape because in a URL path, space
9
+ # needs to be escaped as %20 and CGI.escapes a space as +.
10
+ #
11
+ # @param [String]
12
+ #
13
+ # @return [String]
14
+ def encode_path(path)
15
+ path.b.gsub(%r!([^a-zA-Z0-9_.-/]+)!) { |m|
16
+ '%' + m.unpack('H2' * m.bytesize).join('%').upcase
17
+ }
18
+ end
19
+
20
+ module_function :encode_path
21
+ end
22
+ end
@@ -0,0 +1,87 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ module Vault
5
+ class VaultError < RuntimeError; end
6
+
7
+ class MissingTokenError < VaultError
8
+ def initialize
9
+ super <<-EOH
10
+ Missing Vault token! I cannot make requests to Vault without a token. Please
11
+ set a Vault token in the client:
12
+
13
+ Vault.token = "42d1dee5-eb6e-102c-8d23-cc3ba875da51"
14
+
15
+ or authenticate with Vault using the Vault CLI:
16
+
17
+ $ vault auth ...
18
+
19
+ or set the environment variable $VAULT_TOKEN to the token value:
20
+
21
+ $ export VAULT_TOKEN="..."
22
+
23
+ Please refer to the documentation for more examples.
24
+ EOH
25
+ end
26
+ end
27
+
28
+ class MissingRequiredStateError < VaultError
29
+ def initialize
30
+ super <<-EOH
31
+ The performance standby node does not yet have the
32
+ most recent index state required to authenticate
33
+ the request.
34
+
35
+ Generally, the request should be retried with the with_retries clause.
36
+ EOH
37
+ end
38
+ end
39
+
40
+ class HTTPConnectionError < VaultError
41
+ attr_reader :address
42
+
43
+ def initialize(address, exception)
44
+ @address = address
45
+ @exception = exception
46
+
47
+ super <<-EOH
48
+ The Vault server at `#{address}' is not currently
49
+ accepting connections. Please ensure that the server is running and that your
50
+ authentication information is correct.
51
+
52
+ The original error was `#{exception.class}'. Additional information (if any) is
53
+ shown below:
54
+
55
+ #{exception.message}
56
+
57
+ Please refer to the documentation for more help.
58
+ EOH
59
+ end
60
+
61
+ def original_exception
62
+ @exception
63
+ end
64
+ end
65
+
66
+ class HTTPError < VaultError
67
+ attr_reader :address, :response, :code, :errors
68
+
69
+ def initialize(address, response, errors = [])
70
+ @address, @response, @errors = address, response, errors
71
+ @code = response.code.to_i
72
+ errors = errors.map { |error| " * #{error}" }
73
+
74
+ super <<-EOH
75
+ The Vault server at `#{address}' responded with a #{code}.
76
+ Any additional information the server supplied is shown below:
77
+
78
+ #{errors.join("\n").rstrip}
79
+
80
+ Please refer to the documentation for help.
81
+ EOH
82
+ end
83
+ end
84
+
85
+ class HTTPClientError < HTTPError; end
86
+ class HTTPServerError < HTTPError; end
87
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ ##
5
+ # A Net::HTTP connection wrapper that holds extra information for managing the
6
+ # connection's lifetime.
7
+
8
+ module Vault
9
+ class PersistentHTTP::Connection # :nodoc:
10
+
11
+ attr_accessor :http
12
+
13
+ attr_accessor :last_use
14
+
15
+ attr_accessor :requests
16
+
17
+ attr_accessor :ssl_generation
18
+
19
+ def initialize http_class, http_args, ssl_generation
20
+ @http = http_class.new(*http_args)
21
+ @ssl_generation = ssl_generation
22
+
23
+ reset
24
+ end
25
+
26
+ def finish
27
+ @http.finish
28
+ rescue IOError
29
+ ensure
30
+ reset
31
+ end
32
+
33
+ def reset
34
+ @last_use = PersistentHTTP::EPOCH
35
+ @requests = 0
36
+ end
37
+
38
+ def ressl ssl_generation
39
+ @ssl_generation = ssl_generation
40
+
41
+ finish
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ module Vault
5
+ class PersistentHTTP::Pool < Vault::ConnectionPool # :nodoc:
6
+
7
+ attr_reader :available # :nodoc:
8
+ attr_reader :key # :nodoc:
9
+
10
+ def initialize(options = {}, &block)
11
+ super
12
+
13
+ @available = PersistentHTTP::TimedStackMulti.new(@size, &block)
14
+ @key = :"current-#{@available.object_id}"
15
+ end
16
+
17
+ def checkin net_http_args
18
+ stack = Thread.current[@key][net_http_args]
19
+
20
+ raise ConnectionPool::Error, 'no connections are checked out' if
21
+ stack.empty?
22
+
23
+ conn = stack.pop
24
+
25
+ if stack.empty?
26
+ @available.push conn, connection_args: net_http_args
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ def checkout net_http_args
33
+ stacks = Thread.current[@key] ||= Hash.new { |h, k| h[k] = [] }
34
+ stack = stacks[net_http_args]
35
+
36
+ if stack.empty? then
37
+ conn = @available.pop @timeout, connection_args: net_http_args
38
+ else
39
+ conn = stack.last
40
+ end
41
+
42
+ stack.push conn
43
+
44
+ conn
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ require_relative 'timed_stack_multi'
51
+
@@ -0,0 +1,73 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ module Vault
5
+ class PersistentHTTP::TimedStackMulti < ConnectionPool::TimedStack # :nodoc:
6
+
7
+ def initialize(size = 0, &block)
8
+ super
9
+
10
+ @enqueued = 0
11
+ @ques = Hash.new { |h, k| h[k] = [] }
12
+ @lru = {}
13
+ @key = :"connection_args-#{object_id}"
14
+ end
15
+
16
+ def empty?
17
+ (@created - @enqueued) >= @max
18
+ end
19
+
20
+ def length
21
+ @max - @created + @enqueued
22
+ end
23
+
24
+ private
25
+
26
+ def connection_stored? options = {} # :nodoc:
27
+ !@ques[options[:connection_args]].empty?
28
+ end
29
+
30
+ def fetch_connection options = {} # :nodoc:
31
+ connection_args = options[:connection_args]
32
+
33
+ @enqueued -= 1
34
+ lru_update connection_args
35
+ @ques[connection_args].pop
36
+ end
37
+
38
+ def lru_update connection_args # :nodoc:
39
+ @lru.delete connection_args
40
+ @lru[connection_args] = true
41
+ end
42
+
43
+ def shutdown_connections # :nodoc:
44
+ @ques.each_key do |key|
45
+ super connection_args: key
46
+ end
47
+ end
48
+
49
+ def store_connection obj, options = {} # :nodoc:
50
+ @ques[options[:connection_args]].push obj
51
+ @enqueued += 1
52
+ end
53
+
54
+ def try_create options = {} # :nodoc:
55
+ connection_args = options[:connection_args]
56
+
57
+ if @created >= @max && @enqueued >= 1
58
+ oldest, = @lru.first
59
+ @lru.delete oldest
60
+ @ques[oldest].pop
61
+
62
+ @created -= 1
63
+ end
64
+
65
+ if @created < @max
66
+ @created += 1
67
+ lru_update connection_args
68
+ return @create_block.call(connection_args)
69
+ end
70
+ end
71
+
72
+ end
73
+ end