vault_ruby_client 0.18.2
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/CHANGELOG.md +287 -0
- data/LICENSE +364 -0
- data/README.md +223 -0
- data/lib/vault/api/approle.rb +221 -0
- data/lib/vault/api/auth.rb +324 -0
- data/lib/vault/api/auth_tls.rb +95 -0
- data/lib/vault/api/auth_token.rb +245 -0
- data/lib/vault/api/help.rb +36 -0
- data/lib/vault/api/kv.rb +230 -0
- data/lib/vault/api/logical.rb +153 -0
- data/lib/vault/api/secret.rb +171 -0
- data/lib/vault/api/sys/audit.rb +94 -0
- data/lib/vault/api/sys/auth.rb +119 -0
- data/lib/vault/api/sys/health.rb +66 -0
- data/lib/vault/api/sys/init.rb +86 -0
- data/lib/vault/api/sys/leader.rb +51 -0
- data/lib/vault/api/sys/lease.rb +52 -0
- data/lib/vault/api/sys/mount.rb +165 -0
- data/lib/vault/api/sys/namespace.rb +86 -0
- data/lib/vault/api/sys/policy.rb +95 -0
- data/lib/vault/api/sys/quota.rb +110 -0
- data/lib/vault/api/sys/seal.rb +84 -0
- data/lib/vault/api/sys.rb +30 -0
- data/lib/vault/api/transform/alphabet.rb +46 -0
- data/lib/vault/api/transform/role.rb +45 -0
- data/lib/vault/api/transform/template.rb +57 -0
- data/lib/vault/api/transform/transformation.rb +64 -0
- data/lib/vault/api/transform.rb +32 -0
- data/lib/vault/api.rb +17 -0
- data/lib/vault/client.rb +460 -0
- data/lib/vault/configurable.rb +53 -0
- data/lib/vault/defaults.rb +218 -0
- data/lib/vault/encode.rb +22 -0
- data/lib/vault/errors.rb +87 -0
- data/lib/vault/persistent/connection.rb +45 -0
- data/lib/vault/persistent/pool.rb +51 -0
- data/lib/vault/persistent/timed_stack_multi.rb +73 -0
- data/lib/vault/persistent.rb +1161 -0
- data/lib/vault/request.rb +47 -0
- data/lib/vault/response.rb +92 -0
- data/lib/vault/vendor/connection_pool/timed_stack.rb +181 -0
- data/lib/vault/vendor/connection_pool/version.rb +8 -0
- data/lib/vault/vendor/connection_pool.rb +153 -0
- data/lib/vault/version.rb +6 -0
- data/lib/vault_ruby_client.rb +53 -0
- 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
|
data/lib/vault/encode.rb
ADDED
@@ -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
|
data/lib/vault/errors.rb
ADDED
@@ -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
|