foreman_vault 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +74 -14
- data/app/models/concerns/foreman_vault/host_extensions.rb +31 -0
- data/app/models/concerns/foreman_vault/orchestration/vault_policy.rb +59 -0
- data/app/models/setting/vault.rb +73 -0
- data/app/models/vault_connection.rb +3 -1
- data/app/services/foreman_vault/vault_auth_method.rb +58 -0
- data/app/services/foreman_vault/vault_client.rb +4 -2
- data/app/services/foreman_vault/vault_policy.rb +65 -0
- data/app/views/unattended/provisioning_templates/VaultPolicy/default.erb +6 -0
- data/db/seeds.d/103-provisioning_templates.rb +25 -0
- data/lib/foreman_vault/engine.rb +13 -2
- data/lib/foreman_vault/version.rb +1 -1
- data/test/factories/{foreman_vault_factories.rb → vault_connection.rb} +0 -0
- data/test/factories/vault_policy_template.rb +10 -0
- data/test/factories/vault_setting.rb +10 -0
- data/test/fixtures/ca.crt +21 -0
- data/test/models/foreman_vault/orchestration/vault_policy_test.rb +125 -0
- data/test/test_plugin_helper.rb +0 -1
- data/test/unit/services/foreman_vault/vault_auth_method_test.rb +130 -0
- data/test/unit/services/foreman_vault/vault_policy_test.rb +171 -0
- metadata +24 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a11fc63d2584b7fd9f4ad6b561cdbdd471503d3de307d4a77610094d67d26b62
|
4
|
+
data.tar.gz: 215b5f6e87ed318a16b7623bedccb25d17dd001ee73281f144b6726a44aee28b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b73b1a230f9982752e8ede2a38b2afd1a071a7ca397fe5f518d6447dbd30ca4f94efcbf29d4c39430cb8f8bf5b9508d9c4edd542aead2ebde60d00e82f889833
|
7
|
+
data.tar.gz: 3b85d123010e4ff958ed12885792b759cddd81b550c4f3cc287cce45b38881ea6589e700caa99751f0f0baf79002a89c2089f5a5784b23d6dafbf3509dc938cd
|
data/README.md
CHANGED
@@ -2,31 +2,63 @@
|
|
2
2
|
|
3
3
|
[<img src="https://opensourcelogos.aws.dmtech.cloud/dmTECH_opensource_logo.svg" height="21" width="130">](https://www.dmtech.de/)
|
4
4
|
|
5
|
-
|
5
|
+
**Foreman Vault** is a plugin for Foreman that integrates with Hashicorp Vault for different things. Currently, it offers two distinct features.
|
6
6
|
|
7
|
-
##
|
7
|
+
## 1. Vault secrets in Foreman templates
|
8
8
|
|
9
|
-
|
9
|
+
This adds two new macros which can be used in Foreman templates:
|
10
|
+
|
11
|
+
- `vault_secret` - Retreive secrets at a given Vault path
|
12
|
+
- `vault_issue_certificate` - Issues new certificates
|
13
|
+
|
14
|
+
## 2. Management of Vault Policies and AuthMethods
|
15
|
+
|
16
|
+
Vault [policies](https://www.vaultproject.io/docs/concepts/policies) and [auth methods](https://www.vaultproject.io/docs/concepts/auth) (of type _cert_) can be created automatically as part of the **host orchestration**.
|
17
|
+
Auth methods also get deleted after the host is removed from Foreman.
|
18
|
+
|
19
|
+
This allows Foreman to create everything needed to access Hashicorp Vault directly from a VM using it's Puppet certificate (e.g. for _Deferred functions_ in Puppet or other CLI tools).
|
20
|
+
|
21
|
+
## Requirements
|
22
|
+
|
23
|
+
- Foreman >= 1.20
|
24
|
+
- Working Vault instance
|
25
|
+
- with _cert_ auth enabled
|
26
|
+
- with _kv_ secret store enabled
|
27
|
+
- valid Vault Token
|
10
28
|
|
11
|
-
|
29
|
+
**Dev Vault Instance**
|
12
30
|
|
13
|
-
|
31
|
+
To run a local Vault dev environment on MacOS use:
|
14
32
|
|
15
33
|
```
|
16
34
|
$ brew install vault
|
17
35
|
$ vault server -dev
|
18
36
|
$ export VAULT_ADDR='http://127.0.0.1:8200'
|
19
37
|
$ vault secrets enable kv
|
38
|
+
$ vault auth enable cert
|
39
|
+
|
40
|
+
$ vault token create -period=60m
|
41
|
+
[...]
|
20
42
|
```
|
21
43
|
|
22
|
-
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
See [Plugins install instructions](https://theforeman.org/plugins/) for how to install Foreman plugins.
|
47
|
+
|
48
|
+
## Basic configuration
|
49
|
+
|
50
|
+
To create a new Vault connection navigate to `Infrastructure -> Vault Connections` and hit the `Create Vault Connection` button. There you can enter a name, the Vault URL and a secret token.
|
51
|
+
|
52
|
+
## Vault secrets in templates
|
23
53
|
|
24
|
-
|
25
|
-
|
26
|
-
|
54
|
+
At this point you can utilize two new macros in your templates:
|
55
|
+
|
56
|
+
- vault_secret(vault_connection_name, secret_path)
|
57
|
+
- vault_issue_certificate(vault_connection_name, pki_role_path, options...)
|
27
58
|
|
28
59
|
### vault_secret(vault_connection_name, secret_path)
|
29
|
-
|
60
|
+
|
61
|
+
To fetch secrets from Vault (you can write secrets with the `vault write kv/my_secret foo=bar` command), e.g.
|
30
62
|
|
31
63
|
```
|
32
64
|
<%= vault_secret('MyVault', 'kv/my_secret') %>
|
@@ -39,14 +71,42 @@ As result you should get secret data, e.g.
|
|
39
71
|
```
|
40
72
|
|
41
73
|
### vault_issue_certificate(vault_connection_name, pki_role_path, options...)
|
42
|
-
Issueing certificates is just as easy. Be sure to have a correctly set-up PKI, meaning, configure it so you can generate certificates from within the Vault UI. This means that you'll have had to set-up a CA or Intermediate CA. Once done, you can generate a certificate like this:
|
43
74
|
|
75
|
+
Issueing certificates is just as easy. Be sure to have a correctly set-up PKI, meaning, configure it so you can generate certificates from within the Vault UI. This means that you'll have to set-up a CA or Intermediate CA. Once done, you can generate a certificate like this:
|
44
76
|
|
45
77
|
```
|
46
78
|
<%= vault_issue_certificate('MyVault', 'pkiEngine/issue/testRole', common_name: 'test.mydomain.com', ttl: '10s') %>
|
47
79
|
```
|
48
80
|
|
49
|
-
The
|
81
|
+
The _common_name_ and _ttl_ are optional, but there are [more options to configure](https://www.vaultproject.io/api/secret/pki/index.html#generate-certificate)
|
82
|
+
|
83
|
+
## Vault policies and auth methods
|
84
|
+
|
85
|
+
### Policies
|
86
|
+
|
87
|
+
The policy is based on a new template kind `VaultPolicy` which is basically a [Vault Policy](https://www.vaultproject.io/docs/concepts/policies#policy-syntax) extended with ERB.
|
88
|
+
|
89
|
+
The name of the policy is extracted from a _Magic comment_ within the template. Therefore you can use ERB to influence the name:
|
90
|
+
|
91
|
+
```
|
92
|
+
# NAME: <%= @host.owner %>-<%= @host.environment %>
|
93
|
+
|
94
|
+
path "secret/foo" {
|
95
|
+
capabilities = ["read"]
|
96
|
+
}
|
97
|
+
```
|
98
|
+
|
99
|
+
You can create multiple `VaultPolicy` templates and configure the default policy used in host orchestration by setting the Foreman Setting `vault_policy_template` to the desired one.
|
100
|
+
|
101
|
+
**Note: If the policy renders empty (yes, you can use conditions within ERB), the orchestration is skipped!**
|
102
|
+
|
103
|
+
### Auth methods
|
104
|
+
|
105
|
+
[Auth methods of type `cert`](https://www.vaultproject.io/docs/auth/cert) are created with three attributes:
|
106
|
+
|
107
|
+
- **certificate**: content of the Foreman setting `ssl_ca_file`
|
108
|
+
- **allowed_common_names**: FQDN of the host which triggered the orchestration
|
109
|
+
- **token_policies**: This is automatically linked to the policy from above
|
50
110
|
|
51
111
|
## Contributing
|
52
112
|
|
@@ -63,8 +123,8 @@ the Free Software Foundation, either version 3 of the License, or
|
|
63
123
|
|
64
124
|
This program is distributed in the hope that it will be useful,
|
65
125
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
66
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
126
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
67
127
|
GNU General Public License for more details.
|
68
128
|
|
69
129
|
You should have received a copy of the GNU General Public License
|
70
|
-
along with this program.
|
130
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanVault
|
4
|
+
module HostExtensions
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
include ForemanVault::Orchestration::VaultPolicy
|
9
|
+
end
|
10
|
+
|
11
|
+
def vault_policy
|
12
|
+
VaultPolicy.new(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def vault_auth_method
|
16
|
+
VaultAuthMethod.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def vault_connection
|
20
|
+
return unless vault_connection_name
|
21
|
+
|
22
|
+
::VaultConnection.find_by(name: vault_connection_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def vault_connection_name
|
28
|
+
params['vault_connection'] || Setting['vault_connection']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanVault
|
4
|
+
module Orchestration
|
5
|
+
module VaultPolicy
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
MAGIC_COMMENT_PREFIX = '# NAME: '
|
9
|
+
|
10
|
+
included do
|
11
|
+
after_validation :queue_vault_push
|
12
|
+
before_destroy :queue_vault_destroy
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def queue_vault_push
|
18
|
+
return if !managed? || errors.any?
|
19
|
+
return unless vault_policy.valid?
|
20
|
+
return unless vault_auth_method.valid?
|
21
|
+
|
22
|
+
queue.create(name: _('Push %s data to Vault') % self, priority: 100,
|
23
|
+
action: [self, :set_vault])
|
24
|
+
end
|
25
|
+
|
26
|
+
def queue_vault_destroy
|
27
|
+
return if !managed? || errors.any?
|
28
|
+
|
29
|
+
queue.create(name: _('Clear %s Vault data') % self, priority: 60,
|
30
|
+
action: [self, :del_vault])
|
31
|
+
end
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/AbcSize
|
34
|
+
def set_vault
|
35
|
+
logger.info "Pushing #{name} data to Vault"
|
36
|
+
|
37
|
+
vault_policy.save if vault_policy.new?
|
38
|
+
|
39
|
+
if vault_auth_method.name != old&.vault_auth_method&.name
|
40
|
+
old&.vault_auth_method&.delete
|
41
|
+
vault_auth_method.save
|
42
|
+
end
|
43
|
+
rescue StandardError => e
|
44
|
+
Foreman::Logging.exception("Failed to push #{name} data to Vault.", e)
|
45
|
+
failure format(_('Failed to push %{name} data to Vault: %{message}\n '), name: name, message: e.message), e
|
46
|
+
end
|
47
|
+
# rubocop:enable Metrics/AbcSize
|
48
|
+
|
49
|
+
def del_vault
|
50
|
+
logger.info "Clearing #{name} Vault data"
|
51
|
+
|
52
|
+
vault_auth_method&.delete
|
53
|
+
rescue StandardError => e
|
54
|
+
Foreman::Logging.exception("Failed to clear #{name} Vault data", e)
|
55
|
+
failure format(_("Failed to clear %{name} Vault data: %{message}\n "), name: name, message: e.message), e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Setting
|
4
|
+
class Vault < ::Setting
|
5
|
+
BLANK_ATTRS << 'vault_connection'
|
6
|
+
BLANK_ATTRS << 'vault_policy_template'
|
7
|
+
|
8
|
+
def self.default_settings
|
9
|
+
[set_vault_connection, set_vault_policy_template]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load_defaults
|
13
|
+
# Check the table exists
|
14
|
+
return unless super
|
15
|
+
|
16
|
+
transaction do
|
17
|
+
default_settings.each { |s| create! s.update(category: 'Setting::Vault') }
|
18
|
+
end
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.humanized_category
|
24
|
+
N_('Vault')
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
private
|
29
|
+
|
30
|
+
def set_vault_connection
|
31
|
+
set(
|
32
|
+
'vault_connection',
|
33
|
+
N_('Default Vault Connection that can be override using parameters'),
|
34
|
+
default_vault_connection,
|
35
|
+
N_('Default Vault Connection'),
|
36
|
+
nil,
|
37
|
+
collection: vault_connections_collection,
|
38
|
+
include_blank: _('Select Vault Connection')
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_vault_connection
|
43
|
+
return unless VaultConnection.unscoped.count == 1
|
44
|
+
|
45
|
+
VaultConnection.unscoped.first.name
|
46
|
+
end
|
47
|
+
|
48
|
+
def vault_connections_collection
|
49
|
+
proc { Hash[VaultConnection.unscoped.all.map { |vc| [vc.name, vc.name] }] }
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_vault_policy_template
|
53
|
+
set(
|
54
|
+
'vault_policy_template',
|
55
|
+
N_('The name of the ProvisioningTemplate that will be used for Vault Policy'),
|
56
|
+
default_vault_policy_template,
|
57
|
+
N_('Vault Policy template name'),
|
58
|
+
nil,
|
59
|
+
collection: vault_policy_templates_collection,
|
60
|
+
include_blank: _('Select Template')
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_vault_policy_template
|
65
|
+
ProvisioningTemplate.unscoped.of_kind(:VaultPolicy).find_by(name: 'Default Vault Policy')&.name
|
66
|
+
end
|
67
|
+
|
68
|
+
def vault_policy_templates_collection
|
69
|
+
proc { Hash[ProvisioningTemplate.unscoped.of_kind(:VaultPolicy).map { |tmpl| [tmpl.name, tmpl.name] }] }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -13,7 +13,9 @@ class VaultConnection < ApplicationRecord
|
|
13
13
|
|
14
14
|
scope :with_valid_token, -> { where(vault_error: nil).where('expire_time > ?', Time.zone.now) }
|
15
15
|
|
16
|
-
delegate :fetch_expire_time, :fetch_secret, :issue_certificate,
|
16
|
+
delegate :fetch_expire_time, :fetch_secret, :issue_certificate,
|
17
|
+
:policy, :policies, :put_policy, :delete_policy,
|
18
|
+
:set_certificate, :certificates, :delete_certificate, to: :client
|
17
19
|
|
18
20
|
def token_valid?
|
19
21
|
vault_error.nil? && expire_time && expire_time > Time.zone.now
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanVault
|
4
|
+
class VaultAuthMethod
|
5
|
+
def initialize(host)
|
6
|
+
@host = host
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
name.present? && options[:certificate].present?
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
return if !host || !vault_policy_name
|
15
|
+
|
16
|
+
[host, vault_policy_name].join('-').parameterize
|
17
|
+
end
|
18
|
+
|
19
|
+
def save
|
20
|
+
return false unless valid?
|
21
|
+
|
22
|
+
set_certificate(name, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete
|
26
|
+
return false unless name
|
27
|
+
|
28
|
+
delete_certificate(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :host
|
34
|
+
delegate :vault_policy, :vault_connection, :fqdn, to: :host
|
35
|
+
delegate :name, to: :vault_policy, prefix: true
|
36
|
+
delegate :set_certificate, :delete_certificate, to: :vault_connection
|
37
|
+
|
38
|
+
def options
|
39
|
+
{
|
40
|
+
certificate: certificate,
|
41
|
+
token_policies: vault_policy_name,
|
42
|
+
allowed_common_names: allowed_common_names
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def allowed_common_names
|
47
|
+
[fqdn].compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def certificate
|
51
|
+
return unless Setting['ssl_ca_file']
|
52
|
+
|
53
|
+
File.read(Setting['ssl_ca_file'])
|
54
|
+
rescue Errno::ENOENT
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'vault'
|
4
|
-
|
5
3
|
module ForemanVault
|
6
4
|
class VaultClient
|
7
5
|
def initialize(base_url, token)
|
@@ -9,6 +7,10 @@ module ForemanVault
|
|
9
7
|
@token = token
|
10
8
|
end
|
11
9
|
|
10
|
+
delegate :sys, :auth_tls, to: :client
|
11
|
+
delegate :policy, :policies, :put_policy, :delete_policy, to: :sys
|
12
|
+
delegate :certificate, :certificates, :set_certificate, :delete_certificate, to: :auth_tls
|
13
|
+
|
12
14
|
def fetch_expire_time
|
13
15
|
response = client.auth_token.lookup_self
|
14
16
|
Time.zone.parse(response.data[:expire_time])
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanVault
|
4
|
+
class VaultPolicy
|
5
|
+
MAGIC_COMMENT_NAME_PREFIX = '# NAME: '
|
6
|
+
|
7
|
+
def initialize(host)
|
8
|
+
@host = host
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
name.present? && rules.present?
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
magic_comment_name&.chomp&.remove(MAGIC_COMMENT_NAME_PREFIX)&.parameterize
|
17
|
+
end
|
18
|
+
|
19
|
+
def new?
|
20
|
+
return unless name
|
21
|
+
|
22
|
+
policies.index(name).nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
return false unless valid?
|
27
|
+
|
28
|
+
put_policy(name, rules)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete
|
32
|
+
return false unless name
|
33
|
+
|
34
|
+
delete_policy(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :host
|
40
|
+
delegate :params, :render_template, :vault_connection, to: :host
|
41
|
+
delegate :policy, :policies, :put_policy, :delete_policy, to: :vault_connection
|
42
|
+
|
43
|
+
def rules
|
44
|
+
rendered&.remove(magic_comment_name)
|
45
|
+
&.lines
|
46
|
+
&.reject { |l| l.strip.empty? }
|
47
|
+
&.join
|
48
|
+
&.presence
|
49
|
+
end
|
50
|
+
|
51
|
+
def magic_comment_name
|
52
|
+
rendered&.lines&.find { |l| l.start_with?(MAGIC_COMMENT_NAME_PREFIX) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def rendered
|
56
|
+
return unless template
|
57
|
+
|
58
|
+
render_template(template: template)
|
59
|
+
end
|
60
|
+
|
61
|
+
def template
|
62
|
+
::ProvisioningTemplate.find_by(name: Setting['vault_policy_template'])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
User.as_anonymous_admin do
|
4
|
+
templates = [
|
5
|
+
{
|
6
|
+
name: 'Default Vault Policy',
|
7
|
+
source: 'VaultPolicy/default.erb',
|
8
|
+
template_kind: TemplateKind.find_or_create_by(name: 'VaultPolicy')
|
9
|
+
}
|
10
|
+
]
|
11
|
+
|
12
|
+
templates.each do |template|
|
13
|
+
template[:contents] = File.read(File.join(ForemanVault::Engine.root, 'app/views/unattended/provisioning_templates', template[:source]))
|
14
|
+
|
15
|
+
ProvisioningTemplate.where(name: template[:name]).first_or_create do |pt|
|
16
|
+
pt.vendor = 'ForemanVault'
|
17
|
+
pt.default = true
|
18
|
+
pt.locked = true
|
19
|
+
pt.name = template[:name]
|
20
|
+
pt.template = template[:contents]
|
21
|
+
pt.template_kind = template[:template_kind] if template[:template_kind]
|
22
|
+
pt.snippet = template[:snippet] if template[:snippet]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/foreman_vault/engine.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'vault'
|
4
|
+
|
3
5
|
module ForemanVault
|
4
6
|
class Engine < ::Rails::Engine
|
5
7
|
engine_name 'foreman_vault'
|
@@ -10,6 +12,14 @@ module ForemanVault
|
|
10
12
|
config.autoload_paths += Dir["#{config.root}/app/lib"]
|
11
13
|
config.autoload_paths += Dir["#{config.root}/app/jobs"]
|
12
14
|
|
15
|
+
initializer 'foreman_vault.load_default_settings', before: :load_config_initializers do
|
16
|
+
require_dependency File.expand_path('../../app/models/setting/vault.rb', __dir__) if begin
|
17
|
+
Setting.table_exists?
|
18
|
+
rescue StandardError
|
19
|
+
(false)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
13
23
|
# Add any db migrations
|
14
24
|
initializer 'foreman_vault.load_app_instance_data' do |app|
|
15
25
|
ForemanVault::Engine.paths['db/migrate'].existent.each do |path|
|
@@ -44,8 +54,9 @@ module ForemanVault
|
|
44
54
|
|
45
55
|
config.to_prepare do
|
46
56
|
begin
|
47
|
-
|
48
|
-
Foreman::Renderer.
|
57
|
+
::Host::Managed.include(ForemanVault::HostExtensions)
|
58
|
+
::Foreman::Renderer::Scope::Base.include(ForemanVault::Macros)
|
59
|
+
::Foreman::Renderer.configure { |c| c.allowed_generic_helpers += [:vault_secret, :vault_issue_certificate] }
|
49
60
|
rescue StandardError => e
|
50
61
|
Rails.logger.warn "ForemanVault: skipping engine hook (#{e})"
|
51
62
|
end
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
FactoryBot.modify do
|
4
|
+
factory :provisioning_template do
|
5
|
+
trait :vault_policy do
|
6
|
+
name { Setting['vault_policy_template'] || 'Default Vault Policy' }
|
7
|
+
template { File.read(File.join(ForemanVault::Engine.root, 'app/views/unattended/provisioning_templates/VaultPolicy/default.erb')) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDgDCCAmgCCQDSQhCJzHnXejANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMC
|
3
|
+
cGwxDjAMBgNVBAgMBXN0YXRlMQ0wCwYDVQQHDARjaXR5MRAwDgYDVQQKDAdjb21w
|
4
|
+
YW55MRAwDgYDVQQLDAdzZWN0aW9uMQ0wCwYDVQQDDARob3N0MSAwHgYJKoZIhvcN
|
5
|
+
AQkBFhFlbWFpbEBleGFtcGxlLmNvbTAeFw0yMDA0MjkxMzQ1MjdaFw0yMzAyMTcx
|
6
|
+
MzQ1MjdaMIGBMQswCQYDVQQGEwJwbDEOMAwGA1UECAwFc3RhdGUxDTALBgNVBAcM
|
7
|
+
BGNpdHkxEDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xDTALBgNV
|
8
|
+
BAMMBGhvc3QxIDAeBgkqhkiG9w0BCQEWEWVtYWlsQGV4YW1wbGUuY29tMIIBIjAN
|
9
|
+
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzQxWM6dJbtdDrIUvG5SBp8XadSst
|
10
|
+
D5Lu/WBwazdiRQI6mF6bOrAviZUJVoN0sFphZHjQpzTkxS9N929fdChBB7fjrP7b
|
11
|
+
vydvcbc8eH+HhK5tYzOjhjw/K2WHriNlSY7tD/Au8ZBkM7PYSM7411GG4Ubxh60+
|
12
|
+
vamBX8rdAp5wEVKWHabdFswqtNbnmFWa00gMRA44ZMoBA5KdDCNnVA+wiA1hSLYl
|
13
|
+
SdSPrKRmYw5gRul7h8lilrvf04Df87NEtRFMjuaHcxrIklVJZMsZ1Mvw5VhlpV3f
|
14
|
+
q1kMGG3wXyeTAOlMDPEFXJ4gs8ZIEqS1T1gfCUF4w/rSDQ0u49WBu2TstQIDAQAB
|
15
|
+
MA0GCSqGSIb3DQEBCwUAA4IBAQBBsovBY2r1+PXJhGTOXvZUMqz+IN/AKi52GIwC
|
16
|
+
dPmVOhFTaztL1LbRTKgtg1cyQGmIdZ8skHGFKVAkESPa1dHu+E5uGs+rFEI1A+KA
|
17
|
+
xKIN2dNXFUEnKDEywcjZhilDHeKqthf1gkcJwkMgv5+DczOtZvtsu7tDF2kcyedw
|
18
|
+
6/A+0GJVG7S72VaL5hcfgglmGXNT5BRjLAWV6ZPViXWSJj43oXLGqqwHWhRBs+d6
|
19
|
+
0+0kNukYjyPLVSLpFpj/DvxvPjQWoDTVzMeT7iTtahd4S9FGPZuHXG2yxnTjCycH
|
20
|
+
jHNvGHXHfYJCJ10RxaeyP1Dz9qG9hH0GiiCCYAuPnf6Eu1qO
|
21
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
module ForemanVault
|
6
|
+
module Orchestration
|
7
|
+
class VaultPolicyTest < ActiveSupport::TestCase
|
8
|
+
describe '#queue_vault_push' do
|
9
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
10
|
+
let(:queue) { mock('queue') }
|
11
|
+
let(:vault_policy) { mock('vault_policy') }
|
12
|
+
let(:vault_auth_method) { mock('vault_auth_method') }
|
13
|
+
|
14
|
+
setup do
|
15
|
+
host.stubs(:queue).returns(queue)
|
16
|
+
host.stubs(:vault_policy).returns(vault_policy)
|
17
|
+
host.stubs(:vault_auth_method).returns(vault_auth_method)
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'should queue Vault orchestration' do
|
21
|
+
vault_policy.stubs(:valid?).returns(true)
|
22
|
+
vault_auth_method.stubs(:valid?).returns(true)
|
23
|
+
|
24
|
+
queue.expects(:create).with(
|
25
|
+
name: "Push #{host} data to Vault",
|
26
|
+
priority: 100,
|
27
|
+
action: [host, :set_vault]
|
28
|
+
).once
|
29
|
+
host.send(:queue_vault_push)
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when vault_policy is not valid' do
|
33
|
+
test 'should not queue Vault orchestration' do
|
34
|
+
vault_auth_method.stubs(:valid?).returns(true)
|
35
|
+
|
36
|
+
vault_policy.expects(:valid?).returns(false)
|
37
|
+
queue.expects(:create).never
|
38
|
+
host.send(:queue_vault_push)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when vault_auth_method is not valid' do
|
43
|
+
test 'should not queue Vault orchestration' do
|
44
|
+
vault_policy.stubs(:valid?).returns(true)
|
45
|
+
|
46
|
+
vault_auth_method.expects(:valid?).returns(false)
|
47
|
+
queue.expects(:create).never
|
48
|
+
host.send(:queue_vault_push)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#set_vault' do
|
54
|
+
let(:environment) { FactoryBot.create(:environment, name: 'MyEnv') }
|
55
|
+
let(:host) { FactoryBot.create(:host, :managed, environment: environment) }
|
56
|
+
let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
|
57
|
+
let(:new_owner) { FactoryBot.create(:usergroup, name: 'MyOwner') }
|
58
|
+
|
59
|
+
let(:vault_policies) { [] }
|
60
|
+
let(:get_policies_request) do
|
61
|
+
stub_request(:get, "#{vault_connection.url}/v1/sys/policy").to_return(
|
62
|
+
status: 200, headers: { 'Content-Type': 'application/json' },
|
63
|
+
body: { policies: vault_policies }.to_json
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:new_policy_name) { "#{new_owner}-#{host.environment}".parameterize }
|
68
|
+
let(:put_policy_request) do
|
69
|
+
url = "#{vault_connection.url}/v1/sys/policy/#{new_policy_name}"
|
70
|
+
# rubocop:disable Metrics/LineLength
|
71
|
+
rules = "# allow access to secrets from puppet hosts from <foreman_owner>-<puppet_environment>\npath \"secrets/data/MyOwner/MyEnv/*\" {\n capabilities = [\"create\", \"read\", \"update\"]\n}\n"
|
72
|
+
# rubocop:enable Metrics/LineLength
|
73
|
+
stub_request(:put, url).with(body: JSON.fast_generate(rules: rules)).to_return(status: 200)
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:new_auth_method_name) { "#{host}-#{new_policy_name}".parameterize }
|
77
|
+
let(:post_auth_method_request) do
|
78
|
+
url = "#{vault_connection.url}/v1/auth/cert/certs/#{new_auth_method_name}"
|
79
|
+
stub_request(:post, url).with(
|
80
|
+
body: JSON.fast_generate(
|
81
|
+
certificate: host.vault_auth_method.send(:certificate),
|
82
|
+
token_policies: new_policy_name,
|
83
|
+
allowed_common_names: [host.fqdn]
|
84
|
+
)
|
85
|
+
).to_return(status: 200)
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:delete_old_auth_method_request) do
|
89
|
+
url = "#{vault_connection.url}/v1/auth/cert/certs/#{host.vault_auth_method.name}"
|
90
|
+
stub_request(:delete, url).to_return(status: 200)
|
91
|
+
end
|
92
|
+
|
93
|
+
setup do
|
94
|
+
Setting.find_by(name: 'ssl_ca_file').update(value: File.join(ForemanVault::Engine.root, 'test/fixtures/ca.crt'))
|
95
|
+
FactoryBot.create(:setting, :vault_policy)
|
96
|
+
FactoryBot.create(:provisioning_template, :vault_policy, name: Setting['vault_policy_template'])
|
97
|
+
FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
|
98
|
+
host.stubs(:skip_orchestration_for_testing?).returns(false)
|
99
|
+
|
100
|
+
get_policies_request
|
101
|
+
put_policy_request
|
102
|
+
post_auth_method_request
|
103
|
+
delete_old_auth_method_request
|
104
|
+
|
105
|
+
host.update(owner: new_owner)
|
106
|
+
end
|
107
|
+
|
108
|
+
it { assert_requested(post_auth_method_request) }
|
109
|
+
it { assert_requested(delete_old_auth_method_request) }
|
110
|
+
|
111
|
+
context 'when policy already exists on Vault' do
|
112
|
+
let(:vault_policies) { [new_policy_name] }
|
113
|
+
|
114
|
+
it { assert_not_requested(put_policy_request) }
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when policy does not exist on Vault' do
|
118
|
+
let(:vault_policies) { [] }
|
119
|
+
|
120
|
+
it { assert_requested(put_policy_request) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/test/test_plugin_helper.rb
CHANGED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
class VaultAuthMethodTest < ActiveSupport::TestCase
|
6
|
+
subject { ForemanVault::VaultAuthMethod.new(host) }
|
7
|
+
|
8
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
9
|
+
|
10
|
+
describe '#name' do
|
11
|
+
context 'with host and vault_policy_name' do
|
12
|
+
setup do
|
13
|
+
subject.stubs(:vault_policy_name).returns('vault_policy_name')
|
14
|
+
end
|
15
|
+
|
16
|
+
it { assert_equal "#{host}-vault_policy_name".parameterize, subject.name }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'without host' do
|
20
|
+
setup do
|
21
|
+
subject.stubs(:host).returns(nil)
|
22
|
+
subject.stubs(:vault_policy_name).returns('vault_policy_name')
|
23
|
+
end
|
24
|
+
|
25
|
+
it { assert_nil subject.name }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'without vault_policy_name' do
|
29
|
+
setup do
|
30
|
+
subject.stubs(:vault_policy_name).returns(nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
it { assert_nil subject.name }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'valid?' do
|
38
|
+
context 'with name and certificate' do
|
39
|
+
setup do
|
40
|
+
subject.stubs(:name).returns('name')
|
41
|
+
subject.stubs(:certificate).returns('cert')
|
42
|
+
end
|
43
|
+
|
44
|
+
it { assert subject.valid? }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'without name' do
|
48
|
+
setup do
|
49
|
+
subject.stubs(:name).returns(nil)
|
50
|
+
subject.stubs(:certificate).returns('cert')
|
51
|
+
end
|
52
|
+
|
53
|
+
it { assert_not subject.valid? }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'without certificate' do
|
57
|
+
setup do
|
58
|
+
subject.stubs(:name).returns('name')
|
59
|
+
subject.stubs(:certificate).returns(nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
it { assert_not subject.valid? }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#save' do
|
67
|
+
context 'when valid' do
|
68
|
+
it 'creates auth method in the Vault' do
|
69
|
+
subject.stubs(:name).returns('name')
|
70
|
+
subject.stubs(:vault_policy_name).returns('vault_policy_name')
|
71
|
+
subject.stubs(:certificate).returns('cert')
|
72
|
+
|
73
|
+
subject.expects(:set_certificate).once.with(
|
74
|
+
'name',
|
75
|
+
certificate: 'cert',
|
76
|
+
token_policies: 'vault_policy_name',
|
77
|
+
allowed_common_names: [host.fqdn]
|
78
|
+
)
|
79
|
+
subject.save
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when not valid' do
|
84
|
+
it 'does not create auth method in the Vault' do
|
85
|
+
subject.stubs(:valid?).returns(false)
|
86
|
+
|
87
|
+
subject.expects(:set_certificate).never
|
88
|
+
subject.save
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#delete' do
|
94
|
+
context 'with name' do
|
95
|
+
it 'deletes Certificate' do
|
96
|
+
subject.stubs(:name).returns('name')
|
97
|
+
|
98
|
+
subject.expects(:delete_certificate).once.with(subject.name)
|
99
|
+
subject.delete
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'without name' do
|
104
|
+
it 'does not delete Certificate' do
|
105
|
+
subject.stubs(:name).returns(nil)
|
106
|
+
|
107
|
+
subject.expects(:delete_certificate).never
|
108
|
+
subject.delete
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#certificate' do
|
114
|
+
setup do
|
115
|
+
Setting.find_by(name: 'ssl_ca_file').update(value: cert_path)
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when certificate file can be read' do
|
119
|
+
let(:cert_path) { File.join(ForemanVault::Engine.root, 'test/fixtures/ca.crt') }
|
120
|
+
|
121
|
+
it { assert_equal File.read(cert_path), subject.send(:certificate) }
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when certificate file cannot be read' do
|
125
|
+
let(:cert_path) { '/tmp/invalid.crt' }
|
126
|
+
|
127
|
+
it { assert_not subject.send(:certificate) }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
class VaultPolicyTest < ActiveSupport::TestCase
|
6
|
+
subject { ForemanVault::VaultPolicy.new(host) }
|
7
|
+
|
8
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
9
|
+
|
10
|
+
setup do
|
11
|
+
FactoryBot.create(:setting, name: 'vault_policy_template', value: 'Default Vault Policy')
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'valid?' do
|
15
|
+
context 'with name and rules' do
|
16
|
+
setup do
|
17
|
+
subject.stubs(:name).returns('name')
|
18
|
+
subject.stubs(:rules).returns('rules')
|
19
|
+
end
|
20
|
+
|
21
|
+
it { assert subject.valid? }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'without name' do
|
25
|
+
setup do
|
26
|
+
subject.stubs(:name).returns(nil)
|
27
|
+
subject.stubs(:rules).returns('rules')
|
28
|
+
end
|
29
|
+
|
30
|
+
it { assert_not subject.valid? }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'without rules' do
|
34
|
+
setup do
|
35
|
+
subject.stubs(:name).returns('name')
|
36
|
+
subject.stubs(:rules).returns(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
it { assert_not subject.valid? }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#name' do
|
44
|
+
context 'without corresponding Vault Policy template' do
|
45
|
+
it { assert_nil subject.name }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with corresponding Vault Policy template' do
|
49
|
+
setup do
|
50
|
+
FactoryBot.create(:provisioning_template, :vault_policy, template: template)
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:template) { '# NAME: <%= @host.name %>' }
|
54
|
+
|
55
|
+
it { assert_equal host.name.parameterize, subject.name }
|
56
|
+
|
57
|
+
context 'when name is empty' do
|
58
|
+
let(:template) { '# NAME:' }
|
59
|
+
|
60
|
+
it { assert_nil subject.name }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when there is no name magic comment' do
|
64
|
+
let(:template) { '# BLAH:' }
|
65
|
+
|
66
|
+
it { assert_nil subject.name }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#new?' do
|
72
|
+
setup do
|
73
|
+
FactoryBot.create(:provisioning_template, :vault_policy)
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'policy already exists in the Vault' do
|
77
|
+
setup do
|
78
|
+
subject.stubs(:policies).returns([subject.name])
|
79
|
+
end
|
80
|
+
|
81
|
+
it { assert_not subject.new? }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'policy does not exist in the Vault' do
|
85
|
+
setup do
|
86
|
+
subject.stubs(:policies).returns([])
|
87
|
+
end
|
88
|
+
|
89
|
+
it { assert subject.new? }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#save' do
|
94
|
+
context 'when valid' do
|
95
|
+
it 'creates Vault Policy' do
|
96
|
+
subject.stubs(:name).returns('name')
|
97
|
+
subject.stubs(:rules).returns('rules')
|
98
|
+
|
99
|
+
subject.expects(:put_policy).once.with(subject.name, subject.send(:rules))
|
100
|
+
subject.save
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when not valid' do
|
105
|
+
it 'does not create Vault Policy' do
|
106
|
+
subject.stubs(:valid?).returns(false)
|
107
|
+
|
108
|
+
subject.expects(:set_certificate).never
|
109
|
+
subject.save
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#delete' do
|
115
|
+
context 'with name' do
|
116
|
+
it 'deletes Vault Policy' do
|
117
|
+
subject.stubs(:name).returns('name')
|
118
|
+
|
119
|
+
subject.expects(:delete_policy).once.with(subject.name)
|
120
|
+
subject.delete
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'without name' do
|
125
|
+
it 'does not delete Vault Policy' do
|
126
|
+
subject.stubs(:name).returns(nil)
|
127
|
+
|
128
|
+
subject.expects(:delete_policy).never
|
129
|
+
subject.delete
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#rules' do
|
135
|
+
context 'without corresponding Vault Policy template' do
|
136
|
+
it { assert_nil subject.send(:rules) }
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'with corresponding Vault Policy template' do
|
140
|
+
let(:rules) { 'path "secrets/data/*" { capabilities = ["create", "read", "update"] }' }
|
141
|
+
|
142
|
+
setup do
|
143
|
+
FactoryBot.create(:provisioning_template, :vault_policy, template: template)
|
144
|
+
end
|
145
|
+
|
146
|
+
let(:template) do
|
147
|
+
<<~TEMPLATE
|
148
|
+
# NAME: <%= @host.name %>
|
149
|
+
|
150
|
+
#{rules}
|
151
|
+
TEMPLATE
|
152
|
+
end
|
153
|
+
|
154
|
+
it { assert_equal "#{rules}\n", subject.send(:rules) }
|
155
|
+
|
156
|
+
context 'when Vault Policy template renders empty' do
|
157
|
+
let(:template) do
|
158
|
+
<<~TEMPLATE
|
159
|
+
# NAME: <%= @host.name %>
|
160
|
+
|
161
|
+
<% if false %>
|
162
|
+
#{rules}
|
163
|
+
<% end %>
|
164
|
+
TEMPLATE
|
165
|
+
end
|
166
|
+
|
167
|
+
it { assert_nil subject.send(:rules) }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_vault
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dmTECH GmbH
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03
|
11
|
+
date: 2020-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: vault
|
@@ -68,14 +68,20 @@ files:
|
|
68
68
|
- app/jobs/refresh_vault_token.rb
|
69
69
|
- app/jobs/refresh_vault_tokens.rb
|
70
70
|
- app/lib/foreman_vault/macros.rb
|
71
|
+
- app/models/concerns/foreman_vault/host_extensions.rb
|
72
|
+
- app/models/concerns/foreman_vault/orchestration/vault_policy.rb
|
73
|
+
- app/models/setting/vault.rb
|
71
74
|
- app/models/vault_connection.rb
|
75
|
+
- app/services/foreman_vault/vault_auth_method.rb
|
72
76
|
- app/services/foreman_vault/vault_client.rb
|
77
|
+
- app/services/foreman_vault/vault_policy.rb
|
73
78
|
- app/views/api/v2/vault_connections/base.json.rabl
|
74
79
|
- app/views/api/v2/vault_connections/create.json.rabl
|
75
80
|
- app/views/api/v2/vault_connections/index.json.rabl
|
76
81
|
- app/views/api/v2/vault_connections/main.json.rabl
|
77
82
|
- app/views/api/v2/vault_connections/show.json.rabl
|
78
83
|
- app/views/api/v2/vault_connections/update.json.rabl
|
84
|
+
- app/views/unattended/provisioning_templates/VaultPolicy/default.erb
|
79
85
|
- app/views/vault_connections/_form.html.erb
|
80
86
|
- app/views/vault_connections/edit.html.erb
|
81
87
|
- app/views/vault_connections/index.html.erb
|
@@ -85,6 +91,7 @@ files:
|
|
85
91
|
- config/routes.rb
|
86
92
|
- db/migrate/20180725072913_create_vault_connection.foreman_vault.rb
|
87
93
|
- db/migrate/20180809172407_rename_vault_status_to_vault_error.foreman_vault.rb
|
94
|
+
- db/seeds.d/103-provisioning_templates.rb
|
88
95
|
- lib/foreman_vault.rb
|
89
96
|
- lib/foreman_vault/engine.rb
|
90
97
|
- lib/foreman_vault/version.rb
|
@@ -93,14 +100,20 @@ files:
|
|
93
100
|
- locale/en/foreman_vault.po
|
94
101
|
- locale/foreman_vault.pot
|
95
102
|
- locale/gemspec.rb
|
96
|
-
- test/factories/
|
103
|
+
- test/factories/vault_connection.rb
|
104
|
+
- test/factories/vault_policy_template.rb
|
105
|
+
- test/factories/vault_setting.rb
|
106
|
+
- test/fixtures/ca.crt
|
97
107
|
- test/functional/api/v2/vault_connections_controller_test.rb
|
98
108
|
- test/jobs/refresh_vault_token_test.rb
|
99
109
|
- test/jobs/refresh_vault_tokens_test.rb
|
110
|
+
- test/models/foreman_vault/orchestration/vault_policy_test.rb
|
100
111
|
- test/models/vault_connection_test.rb
|
101
112
|
- test/test_plugin_helper.rb
|
102
113
|
- test/unit/lib/foreman_vault/macros_test.rb
|
114
|
+
- test/unit/services/foreman_vault/vault_auth_method_test.rb
|
103
115
|
- test/unit/services/foreman_vault/vault_client_test.rb
|
116
|
+
- test/unit/services/foreman_vault/vault_policy_test.rb
|
104
117
|
homepage: https://github.com/dm-drogeriemarkt/foreman_vault
|
105
118
|
licenses:
|
106
119
|
- GPL-3.0
|
@@ -120,16 +133,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
133
|
- !ruby/object:Gem::Version
|
121
134
|
version: '0'
|
122
135
|
requirements: []
|
123
|
-
|
124
|
-
rubygems_version: 2.7.6.2
|
136
|
+
rubygems_version: 3.1.2
|
125
137
|
signing_key:
|
126
138
|
specification_version: 4
|
127
139
|
summary: Adds support for using credentials from Hashicorp Vault
|
128
140
|
test_files:
|
129
141
|
- test/unit/lib/foreman_vault/macros_test.rb
|
130
142
|
- test/unit/services/foreman_vault/vault_client_test.rb
|
143
|
+
- test/unit/services/foreman_vault/vault_policy_test.rb
|
144
|
+
- test/unit/services/foreman_vault/vault_auth_method_test.rb
|
131
145
|
- test/models/vault_connection_test.rb
|
132
|
-
- test/
|
146
|
+
- test/models/foreman_vault/orchestration/vault_policy_test.rb
|
147
|
+
- test/factories/vault_policy_template.rb
|
148
|
+
- test/factories/vault_connection.rb
|
149
|
+
- test/factories/vault_setting.rb
|
150
|
+
- test/fixtures/ca.crt
|
133
151
|
- test/test_plugin_helper.rb
|
134
152
|
- test/jobs/refresh_vault_tokens_test.rb
|
135
153
|
- test/jobs/refresh_vault_token_test.rb
|