vault-provision 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +50 -0
- data/README.md +4 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/examples/basic/auth/.keep +0 -0
- data/examples/basic/auth/ldap/.keep +0 -0
- data/examples/basic/auth/ldap/config.json +13 -0
- data/examples/basic/auth/ldap/groups/admin.json +3 -0
- data/examples/basic/auth/ldap/groups/operators.json +3 -0
- data/examples/basic/auth/token/.keep +0 -0
- data/examples/basic/pki-intermediate/config/.keep +0 -0
- data/examples/basic/pki-intermediate/config/crl.json +3 -0
- data/examples/basic/pki-intermediate/config/urls.json +4 -0
- data/examples/basic/pki-intermediate/intermediate/generate/internal.json +7 -0
- data/examples/basic/pki-intermediate/roles/.keep +0 -0
- data/examples/basic/pki-intermediate/roles/dvcert.json +19 -0
- data/examples/basic/pki-intermediate/roles/unlimited.json +18 -0
- data/examples/basic/pki-root/config/.keep +0 -0
- data/examples/basic/pki-root/config/crl.json +3 -0
- data/examples/basic/pki-root/config/urls.json +4 -0
- data/examples/basic/pki-root/roles/.keep +0 -0
- data/examples/basic/pki-root/roles/unlimited.json +18 -0
- data/examples/basic/pki-root/root/generate/internal.json +7 -0
- data/examples/basic/sys/auth.json +10 -0
- data/examples/basic/sys/auth/.keep +0 -0
- data/examples/basic/sys/auth/ldap.json +4 -0
- data/examples/basic/sys/auth/token.json +4 -0
- data/examples/basic/sys/mounts/.keep +0 -0
- data/examples/basic/sys/mounts/cubbyhole.json +8 -0
- data/examples/basic/sys/mounts/pki-intermediate.json +8 -0
- data/examples/basic/sys/mounts/pki-intermediate/tune.json +3 -0
- data/examples/basic/sys/mounts/pki-root.json +8 -0
- data/examples/basic/sys/mounts/pki-root/tune.json +3 -0
- data/examples/basic/sys/mounts/secret.json +8 -0
- data/examples/basic/sys/mounts/squirrel.json +8 -0
- data/examples/basic/sys/mounts/sys.json +8 -0
- data/examples/basic/sys/policy/.keep +0 -0
- data/examples/basic/sys/policy/default.hcl +21 -0
- data/examples/basic/sys/policy/master_of_secrets.json +21 -0
- data/examples/basic/sys/policy/pki-intermediates.json +21 -0
- data/examples/basic/sys/policy/response-wrapping.hcl +5 -0
- data/examples/basic/sys/policy/root.json +0 -0
- data/lib/vault/provision.rb +52 -0
- data/lib/vault/provision/auth.rb +4 -0
- data/lib/vault/provision/auth/ldap.rb +5 -0
- data/lib/vault/provision/auth/ldap/config.rb +43 -0
- data/lib/vault/provision/auth/ldap/groups.rb +3 -0
- data/lib/vault/provision/generic.rb +8 -0
- data/lib/vault/provision/pki.rb +23 -0
- data/lib/vault/provision/pki/config.rb +4 -0
- data/lib/vault/provision/pki/config/crl.json +24 -0
- data/lib/vault/provision/pki/config/urls.rb +24 -0
- data/lib/vault/provision/pki/intermediate.rb +4 -0
- data/lib/vault/provision/pki/intermediate/generate.rb +5 -0
- data/lib/vault/provision/pki/intermediate/generate/exported.rb +2 -0
- data/lib/vault/provision/pki/intermediate/generate/internal.rb +43 -0
- data/lib/vault/provision/pki/roles.rb +25 -0
- data/lib/vault/provision/pki/root.rb +4 -0
- data/lib/vault/provision/pki/root/generate.rb +5 -0
- data/lib/vault/provision/pki/root/generate/exported.rb +2 -0
- data/lib/vault/provision/pki/root/generate/internal.rb +24 -0
- data/lib/vault/provision/prototype.rb +25 -0
- data/lib/vault/provision/sys.rb +49 -0
- data/lib/vault/provision/sys/auth.rb +22 -0
- data/lib/vault/provision/sys/policy.rb +18 -0
- data/lib/vault_provision.rb +1 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/vault_provision_spec.rb +59 -0
- data/vault-provision.gemspec +16 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77e526d32e962aec4cfdafc7f667c1767211bcae
|
4
|
+
data.tar.gz: 1b6ca464da212e641f8b47119f30203bcb45663d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0b2c5748e7d17721c40954677ae908dbe2f292db74c6e1ec5ede155efb90ece8db460412afea558e11583dd89477777b3c95d314d51cb97e68477683f5b6f950
|
7
|
+
data.tar.gz: e1c9aa9abc26e3a20a110bd5074f8d5231d4cb03e528051d34a7fbf0459d642d7dd24d3b6188df9590111a6a2d8bede0279bb61ad3395e3e01cc9e706bc81bf2
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
vault-provision (0.0.0)
|
5
|
+
vault (~> 0.9.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (5.0.2)
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
minitest (~> 5.1)
|
14
|
+
tzinfo (~> 1.1)
|
15
|
+
concurrent-ruby (1.0.5)
|
16
|
+
diff-lcs (1.3)
|
17
|
+
i18n (0.8.1)
|
18
|
+
minitest (5.10.1)
|
19
|
+
rake (12.0.0)
|
20
|
+
rspec (3.5.0)
|
21
|
+
rspec-core (~> 3.5.0)
|
22
|
+
rspec-expectations (~> 3.5.0)
|
23
|
+
rspec-mocks (~> 3.5.0)
|
24
|
+
rspec-core (3.5.4)
|
25
|
+
rspec-support (~> 3.5.0)
|
26
|
+
rspec-expectations (3.5.0)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.5.0)
|
29
|
+
rspec-mocks (3.5.0)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.5.0)
|
32
|
+
rspec-support (3.5.0)
|
33
|
+
thread_safe (0.3.6)
|
34
|
+
tzinfo (1.2.3)
|
35
|
+
thread_safe (~> 0.1)
|
36
|
+
vault (0.9.0)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
activesupport (~> 5.0.2)
|
43
|
+
rake (~> 12.0)
|
44
|
+
rspec (~> 3.5.0)
|
45
|
+
rspec-core (~> 3.5.4)
|
46
|
+
vault (~> 0.9.0)
|
47
|
+
vault-provision!
|
48
|
+
|
49
|
+
BUNDLED WITH
|
50
|
+
1.14.6
|
data/README.md
ADDED
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
File without changes
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"binddn": "",
|
3
|
+
"bindpass": "",
|
4
|
+
"certificate": "",
|
5
|
+
"discoverdn": false,
|
6
|
+
"groupdn": "ou=groups,dc=example,dc=com",
|
7
|
+
"insecure_tls": false,
|
8
|
+
"starttls": false,
|
9
|
+
"upndomain": "",
|
10
|
+
"url": "ldaps://ldap.example.com",
|
11
|
+
"userattr": "uid",
|
12
|
+
"userdn": "ou=people,dc=example,dc=com"
|
13
|
+
}
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"allow_any_name": false,
|
3
|
+
"allow_bare_domains": true,
|
4
|
+
"allow_ip_sans": true,
|
5
|
+
"allow_localhost": true,
|
6
|
+
"allow_subdomains": true,
|
7
|
+
"allow_token_displayname": false,
|
8
|
+
"allowed_domains": "example.com,example.net,vault.example.com",
|
9
|
+
"client_flag": true,
|
10
|
+
"code_signing_flag": false,
|
11
|
+
"email_protection_flag": false,
|
12
|
+
"enforce_hostnames": true,
|
13
|
+
"key_bits": 4096,
|
14
|
+
"key_type": "rsa",
|
15
|
+
"max_ttl": "4380h0m0s",
|
16
|
+
"server_flag": true,
|
17
|
+
"ttl": "4380h0m0s",
|
18
|
+
"use_csr_common_name": true
|
19
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"allow_any_name": true,
|
3
|
+
"allow_bare_domains": true,
|
4
|
+
"allow_base_domain": true,
|
5
|
+
"allow_ip_sans": true,
|
6
|
+
"allow_localhost": true,
|
7
|
+
"allow_subdomains": true,
|
8
|
+
"client_flag": true,
|
9
|
+
"code_signing_flag": false,
|
10
|
+
"email_protection_flag": false,
|
11
|
+
"enforce_hostnames": false,
|
12
|
+
"key_bits": 4096,
|
13
|
+
"key_type": "rsa",
|
14
|
+
"max_ttl": "4380h0m0s",
|
15
|
+
"server_flag": true,
|
16
|
+
"ttl": "4380h0m0s",
|
17
|
+
"use_csr_common_name": true
|
18
|
+
}
|
File without changes
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"allow_any_name": true,
|
3
|
+
"allow_bare_domains": true,
|
4
|
+
"allow_base_domain": true,
|
5
|
+
"allow_ip_sans": true,
|
6
|
+
"allow_localhost": true,
|
7
|
+
"allow_subdomains": true,
|
8
|
+
"client_flag": true,
|
9
|
+
"code_signing_flag": false,
|
10
|
+
"email_protection_flag": false,
|
11
|
+
"enforce_hostnames": false,
|
12
|
+
"key_bits": 4096,
|
13
|
+
"key_type": "rsa",
|
14
|
+
"max_ttl": "4380h0m0s",
|
15
|
+
"server_flag": true,
|
16
|
+
"ttl": "4380h0m0s",
|
17
|
+
"use_csr_common_name": true
|
18
|
+
}
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
path "auth/token/lookup-self" {
|
3
|
+
capabilities = ["read"]
|
4
|
+
}
|
5
|
+
|
6
|
+
path "auth/token/renew-self" {
|
7
|
+
capabilities = ["update"]
|
8
|
+
}
|
9
|
+
|
10
|
+
path "auth/token/revoke-self" {
|
11
|
+
capabilities = ["update"]
|
12
|
+
}
|
13
|
+
|
14
|
+
path "cubbyhole/*" {
|
15
|
+
capabilities = ["create", "read", "update", "delete", "list"]
|
16
|
+
}
|
17
|
+
|
18
|
+
path "cubbyhole" {
|
19
|
+
capabilities = ["list"]
|
20
|
+
}
|
21
|
+
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'vault'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
class Vault::Provision; end
|
5
|
+
require 'vault/provision/prototype'
|
6
|
+
|
7
|
+
require 'vault/provision/auth'
|
8
|
+
require 'vault/provision/sys'
|
9
|
+
require 'vault/provision/pki'
|
10
|
+
require 'vault/provision/generic'
|
11
|
+
|
12
|
+
# controller for the children
|
13
|
+
class Vault::Provision
|
14
|
+
SYSTEM_POLICIES = ['response-wrapping', 'root'].freeze
|
15
|
+
|
16
|
+
attr_accessor :vault, :instance_dir, :intermediate_issuer
|
17
|
+
|
18
|
+
def initialize instance_dir,
|
19
|
+
address: ENV['VAULT_ADDR'],
|
20
|
+
token: ENV['VAULT_TOKEN'],
|
21
|
+
intermediate_issuer: {},
|
22
|
+
pki_force: false
|
23
|
+
|
24
|
+
@instance_dir = instance_dir
|
25
|
+
@vault = Vault::Client.new address: address, token: token
|
26
|
+
@intermediate_issuer = intermediate_issuer
|
27
|
+
@pki_force = pki_force
|
28
|
+
@handlers = [
|
29
|
+
Sys::Auth,
|
30
|
+
Auth::Ldap::Config,
|
31
|
+
Sys::Mounts,
|
32
|
+
Pki::Root::Generate::Internal,
|
33
|
+
Pki::Intermediate::Generate::Internal,
|
34
|
+
Pki::Config::Urls,
|
35
|
+
Pki::Roles,
|
36
|
+
Generic,
|
37
|
+
Sys::Policy,
|
38
|
+
#Auth::Ldap::Groups,
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def provision!
|
43
|
+
@handlers.each do |handler|
|
44
|
+
puts "* Calling handler #{handler}"
|
45
|
+
handler.new(self).provision!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pki_force?
|
50
|
+
@pki_force
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# config LDAP authn
|
2
|
+
class Vault::Provision::Auth::Ldap::Config < Vault::Provision::Prototype
|
3
|
+
def ap_file auth_point
|
4
|
+
"#{@instance_dir}/auth/#{auth_point}/config.json"
|
5
|
+
end
|
6
|
+
|
7
|
+
def repo_files
|
8
|
+
return @repo_files if @repo_files
|
9
|
+
#puts "*** calling repo_files"
|
10
|
+
|
11
|
+
auths = @vault.sys.auths
|
12
|
+
|
13
|
+
aps = auths.keys.select do |auth_point|
|
14
|
+
next unless auths[auth_point].type == 'ldap'
|
15
|
+
#puts "**** got auth mount #{auth_point}"
|
16
|
+
next unless FileTest.file? ap_file(auth_point)
|
17
|
+
|
18
|
+
repo_config = JSON.parse(File.read(ap_file(auth_point)))
|
19
|
+
vault_config = begin
|
20
|
+
@vault.get("auth/#{auth_point}config")['data']
|
21
|
+
rescue Vault::HTTPClientError => e
|
22
|
+
#puts "**** new #{auth_point} config"
|
23
|
+
raise e unless e.code == 404
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
# for each key in the repo JSON file's hash, compare to current
|
28
|
+
# vault state. If they're identical, go on to the next mount point.
|
29
|
+
!repo_config.keys.inject(true) { |acc,elem| acc && vault_config[elem] == repo_config[elem]}
|
30
|
+
end
|
31
|
+
#puts "**** aps is #{aps}"
|
32
|
+
map_out = aps.map { |auth_point| ap_file(auth_point) }
|
33
|
+
#puts "**** returning map_out of #{map_out}"
|
34
|
+
@repo_files = map_out
|
35
|
+
end
|
36
|
+
|
37
|
+
def provision!
|
38
|
+
repo_files.each do |rf|
|
39
|
+
auth_point = rf.split('/')[-2]
|
40
|
+
@vault.post "v1/auth/#{auth_point}/config", File.read(rf)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Generic secret (k/v pairs) backend provisioning
|
2
|
+
class Vault::Provision::Generic < Vault::Provision::Prototype
|
3
|
+
# the generic secret API doesn't have anything to configure!
|
4
|
+
# https://www.vaultproject.io/api/secret/generic/index.html
|
5
|
+
def provision!
|
6
|
+
true
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# PKI/CA backend provisioning
|
2
|
+
module Vault::Provision::Pki
|
3
|
+
# placeholder
|
4
|
+
class Root; end
|
5
|
+
# placeholder
|
6
|
+
class Intermediate; end
|
7
|
+
|
8
|
+
def generated? path
|
9
|
+
@vault.get "#{path}/ca/pem"
|
10
|
+
true
|
11
|
+
rescue Vault::HTTPClientError
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def ca_type path
|
16
|
+
path.match(/pki-intermediate/) && true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'vault/provision/pki/root'
|
21
|
+
require 'vault/provision/pki/intermediate'
|
22
|
+
require 'vault/provision/pki/config'
|
23
|
+
require 'vault/provision/pki/roles'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# config crl & distribution points for CAs
|
2
|
+
class Vault::Provision::Pki::Config::Urls < Vault::Provision::Prototype
|
3
|
+
include Vault::Provision::Pki
|
4
|
+
|
5
|
+
def provision!
|
6
|
+
paths = @vault.sys.mounts.select { |_,a| a.type == 'pki' } .keys
|
7
|
+
|
8
|
+
paths.each do |path|
|
9
|
+
r_crl_file = "#{@instance_dir}/#{path}/config/crl.json"
|
10
|
+
r_urls_file = "#{@instance_dir}/#{path}/config/urls.json"
|
11
|
+
|
12
|
+
r_crl = JSON.dump(File.read(r_crl_file))
|
13
|
+
r_urls = JSON.dump(File.read(r_urls_file))
|
14
|
+
|
15
|
+
v_crl = @vault.get("#{path}config/crl")['data']
|
16
|
+
same = r_crl.keys.inject(true) {|acc,elem| acc && r_crl[elem] == v_crl[elem] }
|
17
|
+
@vault.post("#{path}config/crl", r_crl) unless same
|
18
|
+
|
19
|
+
v_urls = @vault.get("#{path}config/urls")['data']
|
20
|
+
same = r_urls.keys.inject(true) {|acc,elem| acc && r_urls[elem] == v_urls[elem] }
|
21
|
+
@vault.post("#{path}config/urls", r_urls) unless same
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# config crl & distribution points for CAs
|
2
|
+
class Vault::Provision::Pki::Config::Urls < Vault::Provision::Prototype
|
3
|
+
include Vault::Provision::Pki
|
4
|
+
|
5
|
+
def urls_file mount_point
|
6
|
+
"#{@instance_dir}/#{mount_point}/config/urls.json"
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo_files
|
10
|
+
mounts = @vault.sys.mounts
|
11
|
+
pki_mounts = mounts.keys.select do |mp|
|
12
|
+
mounts[mp].type == 'pki' && FileTest.file?(urls_file(mp))
|
13
|
+
end
|
14
|
+
pki_mounts.map { |mp| urls_file(mp) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def provision!
|
18
|
+
repo_files.each do |rf|
|
19
|
+
mount_point = rf.split('/')[-3]
|
20
|
+
#puts "**** mount #{mount_point} rf => #{rf}"
|
21
|
+
@vault.post "v1/#{mount_point}/config/urls", File.read(rf)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# create the CA
|
2
|
+
class Vault::Provision::Pki::Intermediate::Generate::Internal < Vault::Provision::Prototype
|
3
|
+
include Vault::Provision::Pki
|
4
|
+
|
5
|
+
def gen_file mount_point
|
6
|
+
"#{@instance_dir}/#{mount_point}/intermediate/generate/internal.json"
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo_files
|
10
|
+
mounts = @vault.sys.mounts
|
11
|
+
generators = mounts.keys.select do |mp|
|
12
|
+
mounts[mp].type == 'pki' && FileTest.file?(gen_file(mp))
|
13
|
+
end
|
14
|
+
generators.map { |mp| gen_file(mp) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def provision!
|
18
|
+
repo_files.each do |rf|
|
19
|
+
mount_point = rf.split('/')[-4]
|
20
|
+
next if generated? mount_point
|
21
|
+
resp = @vault.post "v1/#{mount_point}/intermediate/generate/internal",
|
22
|
+
File.read(rf)
|
23
|
+
sign_intermediate_csr(mount_point, resp[:data][:csr])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def sign_intermediate_csr mount_point, csr
|
28
|
+
return if @intermediate_issuer.empty?
|
29
|
+
root = @intermediate_issuer[mount_point.to_sym]
|
30
|
+
return if root.nil?
|
31
|
+
|
32
|
+
req = JSON.parse(File.read(gen_file(mount_point)))
|
33
|
+
resp = @vault.post "v1/#{root}/root/sign-intermediate",
|
34
|
+
JSON.dump(csr: csr,
|
35
|
+
common_name: req['common_name'],
|
36
|
+
ttl: req['ttl'],
|
37
|
+
max_path_length: 0,
|
38
|
+
exclude_cn_from_sans: true)
|
39
|
+
|
40
|
+
@vault.post "v1/#{mount_point}/intermediate/set-signed",
|
41
|
+
JSON.dump(certificate: resp[:data][:certificate])
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# templates for certs
|
2
|
+
class Vault::Provision::Pki::Roles < Vault::Provision::Prototype
|
3
|
+
include Vault::Provision::Pki
|
4
|
+
|
5
|
+
def repo_files
|
6
|
+
mounts = @vault.sys.mounts
|
7
|
+
pki_mounts = mounts.keys.select { |mp| mounts[mp].type == 'pki' }
|
8
|
+
roles = []
|
9
|
+
pki_mounts.each do |mp|
|
10
|
+
Find.find("#{@instance_dir}/#{mp}/roles/").each do |rf|
|
11
|
+
next unless rf.end_with? '.json'
|
12
|
+
roles << rf
|
13
|
+
end
|
14
|
+
end
|
15
|
+
roles
|
16
|
+
end
|
17
|
+
|
18
|
+
def provision!
|
19
|
+
repo_files.each do |rf|
|
20
|
+
mount_point = rf.split('/')[-3]
|
21
|
+
role_name = File.basename(rf, '.json')
|
22
|
+
@vault.post "v1/#{mount_point}/roles/#{role_name}", File.read(rf)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# create the CA
|
2
|
+
class Vault::Provision::Pki::Root::Generate::Internal < Vault::Provision::Prototype
|
3
|
+
include Vault::Provision::Pki
|
4
|
+
|
5
|
+
def gen_file mount_point
|
6
|
+
"#{@instance_dir}/#{mount_point}/root/generate/internal.json"
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo_files
|
10
|
+
mounts = @vault.sys.mounts
|
11
|
+
generators = mounts.keys.select do |mp|
|
12
|
+
mounts[mp].type == 'pki' && FileTest.file?(gen_file(mp))
|
13
|
+
end
|
14
|
+
generators.map { |mp| gen_file(mp) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def provision!
|
18
|
+
repo_files.each do |rf|
|
19
|
+
mount_point = rf.split('/')[-4]
|
20
|
+
next if generated? mount_point
|
21
|
+
@vault.post "v1/#{mount_point}/root/generate/internal", File.read(rf)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# prototype for the individual hierarchy paths
|
2
|
+
class Vault::Provision::Prototype
|
3
|
+
def initialize boss
|
4
|
+
@vault = boss.vault
|
5
|
+
@instance_dir = boss.instance_dir
|
6
|
+
@intermediate_issuer = boss.intermediate_issuer
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo_prefix
|
10
|
+
ActiveSupport::Inflector.underscore(self.class.to_s)
|
11
|
+
.split('/')[2..-1].join('/')
|
12
|
+
end
|
13
|
+
|
14
|
+
def repo_path
|
15
|
+
"#{@instance_dir}/#{repo_prefix}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def repo_files
|
19
|
+
Find.find(repo_path).select { |rf| rf.end_with?('.json') }
|
20
|
+
end
|
21
|
+
|
22
|
+
def provision!
|
23
|
+
puts "#{self.class} says: Go climb a tree!"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'find'
|
2
|
+
|
3
|
+
# systems backend provisioning
|
4
|
+
class Vault::Provision::Sys; end
|
5
|
+
require 'vault/provision/sys/auth'
|
6
|
+
require 'vault/provision/sys/policy'
|
7
|
+
|
8
|
+
# secret mounts
|
9
|
+
class Vault::Provision::Sys::Mounts < Vault::Provision::Prototype
|
10
|
+
SYSTEM_MOUNTS = [
|
11
|
+
'token',
|
12
|
+
'cubbyhole',
|
13
|
+
'sys',
|
14
|
+
'secret'
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
def provision!
|
18
|
+
mounts = @vault.sys.mounts
|
19
|
+
|
20
|
+
repo_path = "#{@instance_dir}/sys/mounts"
|
21
|
+
change = []
|
22
|
+
Find.find(repo_path).each do |rf|
|
23
|
+
next unless rf.end_with?('.json')
|
24
|
+
next if rf.end_with?('/tune.json')
|
25
|
+
|
26
|
+
rf_base = File.basename rf, '.json'
|
27
|
+
next if SYSTEM_MOUNTS.include? rf_base
|
28
|
+
# puts "** processing mount #{rf_base}"
|
29
|
+
|
30
|
+
path = rf[(repo_path.length + 1)..-6].to_sym
|
31
|
+
r_conf = JSON.parse(File.read(rf))
|
32
|
+
rcc = r_conf['config'] || {}
|
33
|
+
|
34
|
+
unless mounts[path]
|
35
|
+
@vault.sys.mount(path.to_s, r_conf['type'], r_conf['description'])
|
36
|
+
@vault.sys.mount_tune(path.to_s, rcc)
|
37
|
+
change << @vault.sys.mounts[path]
|
38
|
+
next
|
39
|
+
end
|
40
|
+
|
41
|
+
vmc = mounts[path].config || {}
|
42
|
+
next if rcc.keys.inject(true) { |acc, elem| acc && (vmc[elem.to_sym] == rcc[elem]) }
|
43
|
+
|
44
|
+
@vault.sys.mount_tune(path.to_s, rcc)
|
45
|
+
change << @vault.sys.mounts[path]
|
46
|
+
end
|
47
|
+
change
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# helps to enable authentication
|
2
|
+
class Vault::Provision::Sys::Auth < Vault::Provision::Prototype
|
3
|
+
def provision!
|
4
|
+
#puts "files: #{repo_files}"
|
5
|
+
auths = @vault.sys.auths
|
6
|
+
|
7
|
+
change = []
|
8
|
+
repo_files.each do |rf|
|
9
|
+
path = rf[(repo_path.length + 1)..-6].to_sym
|
10
|
+
r_conf = JSON.parse(File.read(rf))
|
11
|
+
# puts "** found #{path}"
|
12
|
+
|
13
|
+
next if auths[path]
|
14
|
+
# puts "** processing #{path}"
|
15
|
+
@vault.sys.enable_auth(path.to_s,
|
16
|
+
r_conf['type'], r_conf['description'])
|
17
|
+
change << @vault.sys.auths[path]
|
18
|
+
end
|
19
|
+
|
20
|
+
change
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# for rubocop, this comment is a matter of policy
|
2
|
+
class Vault::Provision::Sys::Policy < Vault::Provision::Prototype
|
3
|
+
def repo_files
|
4
|
+
Find.find(repo_path).select { |rf| rf.end_with?('.json', '.hcl') }
|
5
|
+
end
|
6
|
+
|
7
|
+
def provision!
|
8
|
+
repo_files.each do |rf|
|
9
|
+
policy_name = if rf.end_with? '.json'
|
10
|
+
File.basename(rf, '.json')
|
11
|
+
elsif rf.end_with? '.hcl'
|
12
|
+
File.basename(rf, '.hcl')
|
13
|
+
end
|
14
|
+
next if Vault::Provision::SYSTEM_POLICIES.include? policy_name
|
15
|
+
@vault.sys.put_policy(policy_name, File.read(rf))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'vault/provision'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
GEM_DIR=File.expand_path(File.dirname(__FILE__) + '/../').freeze
|
2
|
+
$: << "#{GEM_DIR}/lib"
|
3
|
+
|
4
|
+
require 'vault_provision'
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
DEV_VAULT_TOKEN = 'kittens'.freeze
|
8
|
+
DEV_VAULT_ADDR = 'http://127.0.0.1:8200'.freeze
|
9
|
+
EXAMPLE_DIR = "#{GEM_DIR}/examples/basic".freeze
|
10
|
+
|
11
|
+
ENV['VAULT_DEV_ROOT_TOKEN_ID'] = DEV_VAULT_TOKEN
|
12
|
+
ENV['VAULT_TOKEN'] = DEV_VAULT_TOKEN
|
13
|
+
ENV['VAULT_ADDR'] = DEV_VAULT_ADDR
|
14
|
+
|
15
|
+
Vault.configure do |config|
|
16
|
+
config.address = DEV_VAULT_ADDR
|
17
|
+
config.token = DEV_VAULT_TOKEN
|
18
|
+
end
|
19
|
+
|
20
|
+
def vault_server
|
21
|
+
stdin, stdout, stderr, server = Open3.popen3('vault server -dev')
|
22
|
+
cleanup = lambda do |_|
|
23
|
+
stdin.close
|
24
|
+
stdout.close
|
25
|
+
stderr.close
|
26
|
+
Process.kill :INT, server.pid
|
27
|
+
end
|
28
|
+
[:INT, :EXIT].each { |sig| trap(sig, cleanup) }
|
29
|
+
puts "server is #{server.pid}"
|
30
|
+
sleep(1) # woo race condition! wait for server to start up
|
31
|
+
server
|
32
|
+
end
|
33
|
+
|
34
|
+
def client
|
35
|
+
@client ||= Vault::Client.new
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec.configure do |config|
|
39
|
+
config.tty = true
|
40
|
+
config.raise_errors_for_deprecations!
|
41
|
+
end
|
42
|
+
|
43
|
+
@server = vault_server
|
44
|
+
signatories = {'pki-intermediate': 'pki-root'}
|
45
|
+
|
46
|
+
Vault::Provision.new(EXAMPLE_DIR, intermediate_issuer: signatories).provision!
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vault::Provision do
|
4
|
+
it "has a cubbyhole" do
|
5
|
+
expect(client.sys.mounts[:cubbyhole].description).to \
|
6
|
+
include 'per-token private secret storage'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has an ldap auth" do
|
10
|
+
expect(client.sys.auths[:ldap].type).to be == 'ldap'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a token auth" do
|
14
|
+
expect(client.sys.auths[:token].type).to be == 'token'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has an ldap config" do
|
18
|
+
config_data = client.get('v1/auth/ldap/config')[:data]
|
19
|
+
expect(config_data[:url]).to be == 'ldaps://ldap.example.com'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has a pki-root mount" do
|
23
|
+
expect(client.sys.mounts.keys).to include :'pki-root'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has a CA" do
|
27
|
+
expect(client.get('v1/pki-root/ca/pem')).to be
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has pki-root config urls" do
|
31
|
+
expect(client.get('v1/pki-root/config/urls')[:data][:crl_distribution_points].to_s).to include 'https://cdn.example.com'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "has pki-intermediate config urls" do
|
35
|
+
expect(client.get('v1/pki-intermediate/config/urls')[:data][:issuing_certificates].to_s).to include 'https://cdn.example.com'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "has pki-intermediate ca" do
|
39
|
+
expect(client.get('v1/pki-intermediate/ca/pem')).to be
|
40
|
+
end
|
41
|
+
|
42
|
+
it "has a dvcert role for intermediate" do
|
43
|
+
expect(client.get('v1/pki-intermediate/roles/dvcert')[:data][:allowed_domains]).to include "vault.example.com"
|
44
|
+
expect(client.get('v1/pki-intermediate/roles/dvcert')[:data][:allow_any_name]).to be_falsey
|
45
|
+
end
|
46
|
+
|
47
|
+
it "has an unlimited role for root" do
|
48
|
+
expect(client.get('v1/pki-root/roles/unlimited')[:data][:allow_any_name]).to be_truthy
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has a master_of_secrets policy" do
|
52
|
+
expect(client.sys.policy('master_of_secrets').rules).to include '"sys/auth/*"'
|
53
|
+
expect(client.sys.policy('master_of_secrets').rules).to include '"secret/*"'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "has a secret squirrel" do
|
57
|
+
expect(client.sys.mounts[:squirrel].type).to be == 'generic'
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'find'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'vault-provision'
|
5
|
+
s.version = File.read("VERSION").chomp
|
6
|
+
s.summary = 'Provisioning utility for HashiCorp\'s Vault'
|
7
|
+
s.description = ''
|
8
|
+
s.authors = ["Tom Maher"]
|
9
|
+
s.email = "tmaher@pw0n.me"
|
10
|
+
s.license = "Apache-2.0"
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.homepage = 'https://github.com/tmaher/vault-provision'
|
13
|
+
s.add_dependency 'vault', '~>0.9.0'
|
14
|
+
s.add_development_dependency "rake", '~>12'
|
15
|
+
s.add_development_dependency "rspec", '~>3'
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vault-provision
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Maher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: vault
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.9.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
55
|
+
description: ''
|
56
|
+
email: tmaher@pw0n.me
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- ".gitignore"
|
62
|
+
- Gemfile
|
63
|
+
- Gemfile.lock
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- VERSION
|
67
|
+
- examples/basic/auth/.keep
|
68
|
+
- examples/basic/auth/ldap/.keep
|
69
|
+
- examples/basic/auth/ldap/config.json
|
70
|
+
- examples/basic/auth/ldap/groups/admin.json
|
71
|
+
- examples/basic/auth/ldap/groups/operators.json
|
72
|
+
- examples/basic/auth/token/.keep
|
73
|
+
- examples/basic/pki-intermediate/config/.keep
|
74
|
+
- examples/basic/pki-intermediate/config/crl.json
|
75
|
+
- examples/basic/pki-intermediate/config/urls.json
|
76
|
+
- examples/basic/pki-intermediate/intermediate/generate/internal.json
|
77
|
+
- examples/basic/pki-intermediate/roles/.keep
|
78
|
+
- examples/basic/pki-intermediate/roles/dvcert.json
|
79
|
+
- examples/basic/pki-intermediate/roles/unlimited.json
|
80
|
+
- examples/basic/pki-root/config/.keep
|
81
|
+
- examples/basic/pki-root/config/crl.json
|
82
|
+
- examples/basic/pki-root/config/urls.json
|
83
|
+
- examples/basic/pki-root/roles/.keep
|
84
|
+
- examples/basic/pki-root/roles/unlimited.json
|
85
|
+
- examples/basic/pki-root/root/generate/internal.json
|
86
|
+
- examples/basic/sys/auth.json
|
87
|
+
- examples/basic/sys/auth/.keep
|
88
|
+
- examples/basic/sys/auth/ldap.json
|
89
|
+
- examples/basic/sys/auth/token.json
|
90
|
+
- examples/basic/sys/mounts/.keep
|
91
|
+
- examples/basic/sys/mounts/cubbyhole.json
|
92
|
+
- examples/basic/sys/mounts/pki-intermediate.json
|
93
|
+
- examples/basic/sys/mounts/pki-intermediate/tune.json
|
94
|
+
- examples/basic/sys/mounts/pki-root.json
|
95
|
+
- examples/basic/sys/mounts/pki-root/tune.json
|
96
|
+
- examples/basic/sys/mounts/secret.json
|
97
|
+
- examples/basic/sys/mounts/squirrel.json
|
98
|
+
- examples/basic/sys/mounts/sys.json
|
99
|
+
- examples/basic/sys/policy/.keep
|
100
|
+
- examples/basic/sys/policy/default.hcl
|
101
|
+
- examples/basic/sys/policy/master_of_secrets.json
|
102
|
+
- examples/basic/sys/policy/pki-intermediates.json
|
103
|
+
- examples/basic/sys/policy/response-wrapping.hcl
|
104
|
+
- examples/basic/sys/policy/root.json
|
105
|
+
- lib/vault/provision.rb
|
106
|
+
- lib/vault/provision/auth.rb
|
107
|
+
- lib/vault/provision/auth/ldap.rb
|
108
|
+
- lib/vault/provision/auth/ldap/config.rb
|
109
|
+
- lib/vault/provision/auth/ldap/groups.rb
|
110
|
+
- lib/vault/provision/generic.rb
|
111
|
+
- lib/vault/provision/pki.rb
|
112
|
+
- lib/vault/provision/pki/config.rb
|
113
|
+
- lib/vault/provision/pki/config/crl.json
|
114
|
+
- lib/vault/provision/pki/config/urls.rb
|
115
|
+
- lib/vault/provision/pki/intermediate.rb
|
116
|
+
- lib/vault/provision/pki/intermediate/generate.rb
|
117
|
+
- lib/vault/provision/pki/intermediate/generate/exported.rb
|
118
|
+
- lib/vault/provision/pki/intermediate/generate/internal.rb
|
119
|
+
- lib/vault/provision/pki/roles.rb
|
120
|
+
- lib/vault/provision/pki/root.rb
|
121
|
+
- lib/vault/provision/pki/root/generate.rb
|
122
|
+
- lib/vault/provision/pki/root/generate/exported.rb
|
123
|
+
- lib/vault/provision/pki/root/generate/internal.rb
|
124
|
+
- lib/vault/provision/prototype.rb
|
125
|
+
- lib/vault/provision/sys.rb
|
126
|
+
- lib/vault/provision/sys/auth.rb
|
127
|
+
- lib/vault/provision/sys/policy.rb
|
128
|
+
- lib/vault_provision.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/vault_provision_spec.rb
|
131
|
+
- vault-provision.gemspec
|
132
|
+
homepage: https://github.com/tmaher/vault-provision
|
133
|
+
licenses:
|
134
|
+
- Apache-2.0
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.4.5.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Provisioning utility for HashiCorp's Vault
|
156
|
+
test_files: []
|