vault 0.10.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +68 -0
- data/README.md +31 -7
- data/lib/vault/api.rb +2 -0
- data/lib/vault/api/auth.rb +113 -8
- data/lib/vault/api/auth_token.rb +2 -2
- data/lib/vault/api/kv.rb +207 -0
- data/lib/vault/api/secret.rb +12 -0
- data/lib/vault/api/sys.rb +3 -0
- data/lib/vault/api/sys/audit.rb +18 -1
- data/lib/vault/api/sys/health.rb +63 -0
- data/lib/vault/api/sys/mount.rb +7 -2
- data/lib/vault/api/sys/namespace.rb +83 -0
- data/lib/vault/api/sys/quota.rb +107 -0
- data/lib/vault/api/transform.rb +29 -0
- data/lib/vault/api/transform/alphabet.rb +43 -0
- data/lib/vault/api/transform/role.rb +42 -0
- data/lib/vault/api/transform/template.rb +54 -0
- data/lib/vault/api/transform/transformation.rb +61 -0
- data/lib/vault/client.rb +19 -5
- data/lib/vault/configurable.rb +2 -0
- data/lib/vault/defaults.rb +27 -2
- data/lib/vault/persistent.rb +2 -7
- data/lib/vault/persistent/pool.rb +1 -1
- data/lib/vault/request.rb +1 -0
- data/lib/vault/version.rb +1 -1
- metadata +40 -24
- data/.gitignore +0 -41
- data/.rspec +0 -2
- data/.travis.yml +0 -24
- data/Gemfile +0 -3
- data/Rakefile +0 -6
- data/vault.gemspec +0 -28
data/lib/vault/api/secret.rb
CHANGED
@@ -32,6 +32,18 @@ module Vault
|
|
32
32
|
# @return [Hash<Symbol, Object>]
|
33
33
|
field :data, freeze: true
|
34
34
|
|
35
|
+
# @!attribute [r] metadata
|
36
|
+
# Read-only metadata information related to the secret.
|
37
|
+
#
|
38
|
+
# @example Reading metadata
|
39
|
+
# secret = Vault.logical(:versioned).read("secret", "foo")
|
40
|
+
# secret.metadata[:created_time] #=> "2018-12-08T04:22:54.168065Z"
|
41
|
+
# secret.metadata[:version] #=> 1
|
42
|
+
# secret.metadata[:destroyed] #=> false
|
43
|
+
#
|
44
|
+
# @return [Hash<Symbol, Object>]
|
45
|
+
field :metadata, freeze: true
|
46
|
+
|
35
47
|
# @!attribute [r] lease_duration
|
36
48
|
# The number of seconds this lease is valid. If this number is 0 or nil,
|
37
49
|
# the secret does not expire.
|
data/lib/vault/api/sys.rb
CHANGED
@@ -16,9 +16,12 @@ end
|
|
16
16
|
|
17
17
|
require_relative "sys/audit"
|
18
18
|
require_relative "sys/auth"
|
19
|
+
require_relative "sys/health"
|
19
20
|
require_relative "sys/init"
|
20
21
|
require_relative "sys/leader"
|
21
22
|
require_relative "sys/lease"
|
22
23
|
require_relative "sys/mount"
|
24
|
+
require_relative "sys/namespace"
|
23
25
|
require_relative "sys/policy"
|
26
|
+
require_relative "sys/quota"
|
24
27
|
require_relative "sys/seal"
|
data/lib/vault/api/sys/audit.rb
CHANGED
@@ -19,7 +19,7 @@ module Vault
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class Sys
|
22
|
-
# List all
|
22
|
+
# List all audits for the vault.
|
23
23
|
#
|
24
24
|
# @example
|
25
25
|
# Vault.sys.audits #=> { :file => #<Audit> }
|
@@ -70,5 +70,22 @@ module Vault
|
|
70
70
|
client.delete("/v1/sys/audit/#{encode_path(path)}")
|
71
71
|
return true
|
72
72
|
end
|
73
|
+
|
74
|
+
# Generates a HMAC verifier for a given input.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# Vault.sys.audit_hash("file-audit", "my input") #=> "hmac-sha256:30aa7de18a5e90bbc1063db91e7c387b32b9fa895977eb8c177bbc91e7d7c542"
|
78
|
+
#
|
79
|
+
# @param [String] path
|
80
|
+
# the path of the audit backend
|
81
|
+
# @param [String] input
|
82
|
+
# the input to generate a HMAC for
|
83
|
+
#
|
84
|
+
# @return [String]
|
85
|
+
def audit_hash(path, input)
|
86
|
+
json = client.post("/v1/sys/audit-hash/#{encode_path(path)}", JSON.fast_generate(input: input))
|
87
|
+
json = json[:data] if json[:data]
|
88
|
+
json[:hash]
|
89
|
+
end
|
73
90
|
end
|
74
91
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Vault
|
4
|
+
class HealthStatus < Response
|
5
|
+
# @!attribute [r] initialized
|
6
|
+
# Whether the Vault server is Initialized.
|
7
|
+
# @return [Boolean]
|
8
|
+
field :initialized, as: :initialized?
|
9
|
+
|
10
|
+
# @!attribute [r] sealed
|
11
|
+
# Whether the Vault server is Sealed.
|
12
|
+
# @return [Boolean]
|
13
|
+
field :sealed, as: :sealed?
|
14
|
+
|
15
|
+
# @!attribute [r] standby
|
16
|
+
# Whether the Vault server is in Standby mode.
|
17
|
+
# @return [Boolean]
|
18
|
+
field :standby, as: :standby?
|
19
|
+
|
20
|
+
# @!attribute [r] replication_performance_mode
|
21
|
+
# Verbose description of DR mode (added in 0.9.2)
|
22
|
+
# @return [String]
|
23
|
+
field :replication_performance_mode
|
24
|
+
|
25
|
+
# @!attribute [r] replication_dr_mode
|
26
|
+
# Verbose description of DR mode (added in 0.9.2)
|
27
|
+
# @return [String]
|
28
|
+
field :replication_dr_mode
|
29
|
+
|
30
|
+
# @!attribute [r] server_time_utc
|
31
|
+
# Server time in Unix seconds, UTC
|
32
|
+
# @return [Fixnum]
|
33
|
+
field :server_time_utc
|
34
|
+
|
35
|
+
# @!attribute [r] version
|
36
|
+
# Server Vault version string (added in 0.6.1)
|
37
|
+
# @return [String]
|
38
|
+
field :version
|
39
|
+
|
40
|
+
# @!attribute [r] cluster_name
|
41
|
+
# Server cluster name
|
42
|
+
# @return [String]
|
43
|
+
field :cluster_name
|
44
|
+
|
45
|
+
# @!attribute [r] cluster_id
|
46
|
+
# Server cluster UUID
|
47
|
+
# @return [String]
|
48
|
+
field :cluster_id
|
49
|
+
end
|
50
|
+
|
51
|
+
class Sys
|
52
|
+
# Show the health status for this vault.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# Vault.sys.health_status #=> #Vault::HealthStatus @initialized=true, @sealed=false, @standby=false, @replication_performance_mode="disabled", @replication_dr_mode="disabled", @server_time_utc=1519776728, @version="0.9.3", @cluster_name="vault-cluster-997f514e", @cluster_id="c2dad70a-6d88-a06d-69f6-9ae7f5485998">
|
56
|
+
#
|
57
|
+
# @return [HealthStatus]
|
58
|
+
def health_status
|
59
|
+
json = client.get("/v1/sys/health", {:sealedcode => 200, :uninitcode => 200, :standbycode => 200})
|
60
|
+
return HealthStatus.decode(json)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/vault/api/sys/mount.rb
CHANGED
@@ -16,6 +16,11 @@ module Vault
|
|
16
16
|
# Type of the mount.
|
17
17
|
# @return [String]
|
18
18
|
field :type
|
19
|
+
|
20
|
+
# @!attribute [r] type
|
21
|
+
# Options given to the mount.
|
22
|
+
# @return [Hash<Symbol, Object>]
|
23
|
+
field :options
|
19
24
|
end
|
20
25
|
|
21
26
|
class Sys < Request
|
@@ -44,8 +49,8 @@ module Vault
|
|
44
49
|
# the type of mount
|
45
50
|
# @param [String] description
|
46
51
|
# a human-friendly description (optional)
|
47
|
-
def mount(path, type, description = nil)
|
48
|
-
payload =
|
52
|
+
def mount(path, type, description = nil, options = {})
|
53
|
+
payload = options.merge type: type
|
49
54
|
payload[:description] = description if !description.nil?
|
50
55
|
|
51
56
|
client.post("/v1/sys/mounts/#{encode_path(path)}", JSON.fast_generate(payload))
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Vault
|
2
|
+
class Namespace < Response
|
3
|
+
# @!attribute [r] id
|
4
|
+
# ID of the namespace
|
5
|
+
# @return [String]
|
6
|
+
field :id
|
7
|
+
|
8
|
+
# @!attribute [r] path
|
9
|
+
# Path of the namespace, includes parent paths if nested.
|
10
|
+
# @return [String]
|
11
|
+
field :path
|
12
|
+
end
|
13
|
+
|
14
|
+
class Sys
|
15
|
+
# List all namespaces in a given scope. Ignores nested namespaces.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# Vault.sys.namespaces #=> { :foo => #<struct Vault::Namespace id="xxxx1", path="foo/" }
|
19
|
+
#
|
20
|
+
# @return [Hash<Symbol, Namespace>]
|
21
|
+
def namespaces(scoped=nil)
|
22
|
+
path = ["v1", scoped, "sys", "namespaces"].compact
|
23
|
+
json = client.list(path.join("/"))
|
24
|
+
json = json[:data] if json[:data]
|
25
|
+
if json[:key_info]
|
26
|
+
json = json[:key_info]
|
27
|
+
hash = {}
|
28
|
+
json.each do |k,v|
|
29
|
+
hash[k.to_s.chomp("/").to_sym] = Namespace.decode(v)
|
30
|
+
end
|
31
|
+
hash
|
32
|
+
else
|
33
|
+
json
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create a namespace. Nests the namespace if a namespace header is provided.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# Vault.sys.create_namespace("foo")
|
41
|
+
#
|
42
|
+
# @param [String] namespace
|
43
|
+
# the potential path of the namespace, without any parent path provided
|
44
|
+
#
|
45
|
+
# @return [true]
|
46
|
+
def create_namespace(namespace)
|
47
|
+
client.put("/v1/sys/namespaces/#{namespace}", {})
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delete a namespace. Raises an error if the namespace provided is not empty.
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# Vault.sys.delete_namespace("foo")
|
55
|
+
#
|
56
|
+
# @param [String] namespace
|
57
|
+
# the path of the namespace to be deleted
|
58
|
+
#
|
59
|
+
# @return [true]
|
60
|
+
def delete_namespace(namespace)
|
61
|
+
client.delete("/v1/sys/namespaces/#{namespace}")
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
# Retrieve a namespace by path.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# Vault.sys.get_namespace("foo")
|
69
|
+
#
|
70
|
+
# @param [String] namespace
|
71
|
+
# the path of the namespace ot be retrieved
|
72
|
+
#
|
73
|
+
# @return [Namespace]
|
74
|
+
def get_namespace(namespace)
|
75
|
+
json = client.get("/v1/sys/namespaces/#{namespace}")
|
76
|
+
if data = json.dig(:data)
|
77
|
+
Namespace.decode(data)
|
78
|
+
else
|
79
|
+
json
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Vault
|
2
|
+
class Quota < Response
|
3
|
+
# @!attribute [r] name
|
4
|
+
# Name of the quota rule.
|
5
|
+
# @return [String]
|
6
|
+
field :name
|
7
|
+
|
8
|
+
# @!attribute [r] path
|
9
|
+
# Namespace/Path combination the quota applies to.
|
10
|
+
# @return [String]
|
11
|
+
field :path
|
12
|
+
|
13
|
+
# @!attribute [r] type
|
14
|
+
# Type of the quota rule, must be one of "lease-count" or "rate-limit"
|
15
|
+
# @return [String]
|
16
|
+
field :type
|
17
|
+
end
|
18
|
+
|
19
|
+
class RateLimitQuota < Quota
|
20
|
+
# @!attribute [r] rate
|
21
|
+
# The rate at which allowed requests are refilled per second by the quota
|
22
|
+
# rule.
|
23
|
+
# @return [Float]
|
24
|
+
field :rate
|
25
|
+
|
26
|
+
# @!attribute [r] burst
|
27
|
+
# The maximum number of requests at any given second allowed by the quota
|
28
|
+
# rule.
|
29
|
+
# @return [Int]
|
30
|
+
field :burst
|
31
|
+
end
|
32
|
+
|
33
|
+
class LeaseCountQuota < Quota
|
34
|
+
# @!attribute [r] counter
|
35
|
+
# Number of currently active leases for the quota.
|
36
|
+
# @return [Int]
|
37
|
+
field :counter
|
38
|
+
|
39
|
+
# @!attribute [r] max_leases
|
40
|
+
# The maximum number of allowed leases for this quota.
|
41
|
+
# @return [Int]
|
42
|
+
field :max_leases
|
43
|
+
end
|
44
|
+
|
45
|
+
class Sys
|
46
|
+
def quotas(type)
|
47
|
+
path = generate_path(type)
|
48
|
+
json = client.list(path)
|
49
|
+
if data = json.dig(:data, :key_info)
|
50
|
+
data.map do |item|
|
51
|
+
type_class(type).decode(item)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
json
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_quota(type, name, opts={})
|
59
|
+
path = generate_path(type, name)
|
60
|
+
client.post(path, JSON.fast_generate(opts))
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_quota(type, name)
|
65
|
+
path = generate_path(type, name)
|
66
|
+
client.delete(path)
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_quota(type, name)
|
71
|
+
path = generate_path(type, name)
|
72
|
+
response = client.get(path)
|
73
|
+
if data = response[:data]
|
74
|
+
type_class(type).decode(data)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_quota_config
|
79
|
+
client.get("v1/sys/quotas/config")
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_quota_config(opts={})
|
83
|
+
client.post("v1/sys/quotas/config", JSON.fast_generate(opts))
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def generate_path(type, name=nil)
|
90
|
+
verify_type(type)
|
91
|
+
path = ["v1", "sys", "quotas", type, name].compact
|
92
|
+
path.join("/")
|
93
|
+
end
|
94
|
+
|
95
|
+
def verify_type(type)
|
96
|
+
return if ["rate-limit", "lease-count"].include?(type)
|
97
|
+
raise ArgumentError, "type must be one of \"rate-limit\" or \"lease-count\""
|
98
|
+
end
|
99
|
+
|
100
|
+
def type_class(type)
|
101
|
+
case type
|
102
|
+
when "lease-count" then LeaseCountQuota
|
103
|
+
when "rate-limit" then RateLimitQuota
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative '../client'
|
2
|
+
require_relative '../request'
|
3
|
+
|
4
|
+
module Vault
|
5
|
+
class Client
|
6
|
+
# A proxy to the {Transform} methods.
|
7
|
+
# @return [Transform]
|
8
|
+
def transform
|
9
|
+
@transform ||= Transform.new(self)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Transform < Request
|
14
|
+
def encode(role_name:, **opts)
|
15
|
+
opts ||= {}
|
16
|
+
client.post("/v1/transform/encode/#{encode_path(role_name)}", JSON.fast_generate(opts))
|
17
|
+
end
|
18
|
+
|
19
|
+
def decode(role_name:, **opts)
|
20
|
+
opts ||= {}
|
21
|
+
client.post("/v1/transform/decode/#{encode_path(role_name)}", JSON.fast_generate(opts))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require_relative 'transform/alphabet'
|
27
|
+
require_relative 'transform/role'
|
28
|
+
require_relative 'transform/template'
|
29
|
+
require_relative 'transform/transformation'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../../request'
|
2
|
+
require_relative '../../response'
|
3
|
+
|
4
|
+
module Vault
|
5
|
+
class Transform < Request
|
6
|
+
class Alphabet < Response
|
7
|
+
# @!attribute [r] id
|
8
|
+
# String listing all possible characters of the alphabet
|
9
|
+
# @return [String]
|
10
|
+
field :alphabet
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_alphabet(name, alphabet:, **opts)
|
14
|
+
opts ||= {}
|
15
|
+
opts[:alphabet] = alphabet
|
16
|
+
client.post("/v1/transform/alphabet/#{encode_path(name)}", JSON.fast_generate(opts))
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_alphabet(name)
|
21
|
+
json = client.get("/v1/transform/alphabet/#{encode_path(name)}")
|
22
|
+
if data = json.dig(:data)
|
23
|
+
Alphabet.decode(data)
|
24
|
+
else
|
25
|
+
json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_alphabet(name)
|
30
|
+
client.delete("/v1/transform/alphabet/#{encode_path(name)}")
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def alphabets
|
35
|
+
json = client.list("/v1/transform/alphabet")
|
36
|
+
if keys = json.dig(:data, :keys)
|
37
|
+
keys
|
38
|
+
else
|
39
|
+
json
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative '../../request'
|
2
|
+
require_relative '../../response'
|
3
|
+
|
4
|
+
module Vault
|
5
|
+
class Transform < Request
|
6
|
+
class Role < Response
|
7
|
+
# @!attribute [r] transformations
|
8
|
+
# Array of all transformations the role has access to
|
9
|
+
# @return [Array<String>]
|
10
|
+
field :transformations
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_role(name, **opts)
|
14
|
+
opts ||= {}
|
15
|
+
client.post("/v1/transform/role/#{encode_path(name)}", JSON.fast_generate(opts))
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_role(name)
|
20
|
+
json = client.get("/v1/transform/role/#{encode_path(name)}")
|
21
|
+
if data = json.dig(:data)
|
22
|
+
Role.decode(data)
|
23
|
+
else
|
24
|
+
json
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_role(name)
|
29
|
+
client.delete("/v1/transform/role/#{encode_path(name)}")
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def roles
|
34
|
+
json = client.list("/v1/transform/role")
|
35
|
+
if keys = json.dig(:data, :keys)
|
36
|
+
keys
|
37
|
+
else
|
38
|
+
json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|