chef-vault 2.5.0 → 2.6.0
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 +4 -4
- data/.rubocop_todo.yml +5 -1
- data/.travis.yml +5 -1
- data/Changelog.md +36 -3
- data/KNIFE_EXAMPLES.md +49 -74
- data/README.md +166 -104
- data/THEORY.md +4 -2
- data/bin/chef-vault +2 -2
- data/chef-vault.gemspec +2 -2
- data/features/clean_on_refresh.feature +28 -0
- data/features/isvault.feature +24 -0
- data/features/itemtype.feature +25 -0
- data/features/step_definitions/chef-databag.rb +4 -0
- data/features/step_definitions/chef-vault.rb +21 -0
- data/features/step_definitions/chef_databagitem.rb +9 -0
- data/features/vault_show_vaultname.feature +22 -0
- data/features/vault_update.feature +1 -1
- data/features/verify_id_matches.feature +11 -0
- data/lib/chef-vault/certificate.rb +1 -1
- data/lib/chef-vault/exceptions.rb +3 -0
- data/lib/chef-vault/item.rb +161 -18
- data/lib/chef-vault/user.rb +1 -1
- data/lib/chef-vault/version.rb +1 -1
- data/lib/chef/knife/decrypt.rb +1 -1
- data/lib/chef/knife/encrypt_create.rb +1 -1
- data/lib/chef/knife/encrypt_delete.rb +1 -1
- data/lib/chef/knife/encrypt_remove.rb +1 -1
- data/lib/chef/knife/encrypt_rotate_keys.rb +1 -1
- data/lib/chef/knife/encrypt_update.rb +1 -1
- data/lib/chef/knife/vault_base.rb +22 -0
- data/lib/chef/knife/vault_create.rb +0 -1
- data/lib/chef/knife/vault_decrypt.rb +1 -1
- data/lib/chef/knife/vault_isvault.rb +43 -0
- data/lib/chef/knife/vault_itemtype.rb +43 -0
- data/lib/chef/knife/vault_list.rb +7 -18
- data/lib/chef/knife/vault_refresh.rb +6 -12
- data/lib/chef/knife/vault_rotate_all_keys.rb +1 -1
- data/lib/chef/knife/vault_show.rb +15 -1
- data/lib/chef/knife/vault_update.rb +1 -1
- data/spec/chef-vault/certificate_spec.rb +9 -2
- data/spec/chef-vault/item_spec.rb +164 -3
- data/spec/chef-vault/user_spec.rb +9 -2
- metadata +14 -6
data/THEORY.md
CHANGED
@@ -195,7 +195,7 @@ the vault create throws an error.
|
|
195
195
|
The data bag 'foo' is then fetched (or created if it does
|
196
196
|
not already exist). An encrypted data bag item named 'bar'
|
197
197
|
is created, encrypted with the shared secret. The data bag
|
198
|
-
item is
|
198
|
+
item is saved to the Chef server.
|
199
199
|
|
200
200
|
### Decrypting a Vault using knife
|
201
201
|
|
@@ -231,7 +231,9 @@ the server 'one'.
|
|
231
231
|
|
232
232
|
Assuming that the recipe contains code such as this:
|
233
233
|
|
234
|
-
chef_gem 'chef-vault'
|
234
|
+
chef_gem 'chef-vault' do
|
235
|
+
compile_time true if respond_to?(:compile_time)
|
236
|
+
end
|
235
237
|
require 'chef-vault'
|
236
238
|
vaultitem = ChefVault::Item.load('foo', 'bar')
|
237
239
|
|
data/bin/chef-vault
CHANGED
@@ -89,9 +89,9 @@ require 'chef-vault'
|
|
89
89
|
ChefVault.load_config(options[:chef])
|
90
90
|
item = ChefVault::Item.load(options[:vault], options[:item])
|
91
91
|
|
92
|
-
puts "#{options[:vault]}/#{options[:item]}"
|
92
|
+
$stdout.puts "#{options[:vault]}/#{options[:item]}"
|
93
93
|
|
94
94
|
options[:values].split(",").each do |value|
|
95
95
|
value.strip! # remove white space
|
96
|
-
puts("\t#{value}: #{item[value]}")
|
96
|
+
$stdout.puts("\t#{value}: #{item[value]}")
|
97
97
|
end
|
data/chef-vault.gemspec
CHANGED
@@ -35,12 +35,12 @@ Gem::Specification.new do |s|
|
|
35
35
|
s.executables = %w( chef-vault )
|
36
36
|
|
37
37
|
s.add_development_dependency 'rake', '~> 10.4'
|
38
|
-
s.add_development_dependency 'rspec', '~> 3.
|
38
|
+
s.add_development_dependency 'rspec', '~> 3.2'
|
39
39
|
s.add_development_dependency 'rspec-its', '~> 1.1'
|
40
40
|
s.add_development_dependency 'aruba', '~> 0.6'
|
41
41
|
s.add_development_dependency 'simplecov', '~> 0.9'
|
42
42
|
s.add_development_dependency 'simplecov-console', '~> 0.2'
|
43
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
43
|
+
s.add_development_dependency 'rubocop', '~> 0.30'
|
44
44
|
# Chef 12 and higher pull in Ohai 8, which needs Ruby v2
|
45
45
|
# so only in the case of a CI build on ruby v1, we constrain
|
46
46
|
# chef to 11 or lower so that we can maintain CI test coverage
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Feature: clean unknown clients on vault refresh
|
2
|
+
|
3
|
+
When refreshing a vault, new clients may be added if they match
|
4
|
+
the search query or client list, but old clients that no longer
|
5
|
+
exist will never be removed. The --clean-unknown-clients switch
|
6
|
+
will cause cause any unknown clients to be removed from the vault
|
7
|
+
item's access list as well
|
8
|
+
|
9
|
+
Scenario: Refresh without clean option
|
10
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
11
|
+
And I create a vault item 'test/item' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
12
|
+
Then the vault item 'test/item' should be encrypted for 'one,two,three'
|
13
|
+
And I delete client 'one' from the Chef server
|
14
|
+
And I refresh the vault item 'test/item'
|
15
|
+
And the vault item 'test/item' should be encrypted for 'one,two,three'
|
16
|
+
And 'one,two,three' should be a client for the vault item 'test/item'
|
17
|
+
|
18
|
+
Scenario: Refresh with clean option
|
19
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
20
|
+
And I create a vault item 'test/item' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
21
|
+
Then the vault item 'test/item' should be encrypted for 'one,two,three'
|
22
|
+
And I delete client 'one' from the Chef server
|
23
|
+
And I refresh the vault item 'test/item' with the 'clean-unknown-clients' option
|
24
|
+
Then the output should contain "Removing unknown client 'one'"
|
25
|
+
And the vault item 'test/item' should be encrypted for 'two,three'
|
26
|
+
And the vault item 'test/item' should not be encrypted for 'one'
|
27
|
+
And 'two,three' should be a client for the vault item 'test/item'
|
28
|
+
And 'one' should not be a client for the vault item 'test/item'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: determine if a data bag item is a vault
|
2
|
+
|
3
|
+
If a data bag item is a vault, 'knife vault isvault VAULTNAME ITEMNAME'
|
4
|
+
should exit 0. Otherwise it should exit 1.
|
5
|
+
|
6
|
+
Scenario: detect vault item
|
7
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
8
|
+
And I create a vault item 'test/item' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
9
|
+
And I check if the data bag item 'test/item' is a vault
|
10
|
+
Then the exit status should be 0
|
11
|
+
|
12
|
+
Scenario: detect non-vault item (encrypted data bag)
|
13
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
14
|
+
And I create an empty data bag 'test'
|
15
|
+
And I create an encrypted data bag item 'test/item' containing the JSON '{"id": "item", "foo": "bar"}' with the secret 'sekrit'
|
16
|
+
And I check if the data bag item 'test/item' is a vault
|
17
|
+
Then the exit status should not be 0
|
18
|
+
|
19
|
+
Scenario: detect non-vault item (normal data bag)
|
20
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
21
|
+
And I create an empty data bag 'test'
|
22
|
+
And I create a data bag item 'test/item' containing the JSON '{"id": "item", "foo": "bar"}'
|
23
|
+
And I check if the data bag item 'test/item' is a vault
|
24
|
+
Then the exit status should not be 0
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Feature: determine the type of a data bag item
|
2
|
+
|
3
|
+
'knife vault itemtype VAULTNAME ITEMNAME' should output one of
|
4
|
+
'normal', 'encrypted', or 'vault' depending on what type of item
|
5
|
+
it detects
|
6
|
+
|
7
|
+
Scenario: detect vault item
|
8
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
9
|
+
And I create a vault item 'test/item' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
10
|
+
And I check the type of the data bag item 'test/item'
|
11
|
+
Then the output should match /^vault$/
|
12
|
+
|
13
|
+
Scenario: detect non-vault item (encrypted data bag)
|
14
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
15
|
+
And I create an empty data bag 'test'
|
16
|
+
And I create an encrypted data bag item 'test/item' containing the JSON '{"id": "item", "foo": "bar"}' with the secret 'sekrit'
|
17
|
+
And I check the type of the data bag item 'test/item'
|
18
|
+
Then the output should match /^encrypted$/
|
19
|
+
|
20
|
+
Scenario: detect non-vault item (normal data bag)
|
21
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
22
|
+
And I create an empty data bag 'test'
|
23
|
+
And I create a data bag item 'test/item' containing the JSON '{"id": "item", "foo": "bar"}'
|
24
|
+
And I check the type of the data bag item 'test/item'
|
25
|
+
Then the output should match /^normal$/
|
@@ -3,3 +3,7 @@ When /^I create a data bag '(.+)' containing the JSON '(.+)'$/ do |bag, json|
|
|
3
3
|
run_simple "knife data bag create #{bag} -z -c knife.rb -d"
|
4
4
|
run_simple "knife data bag from_file #{bag} -z -c knife.rb item.json"
|
5
5
|
end
|
6
|
+
|
7
|
+
Given(/^I create an empty data bag '(.+)'$/) do |databag|
|
8
|
+
run_simple "knife data bag create #{databag} -z -c knife.rb", false
|
9
|
+
end
|
@@ -28,6 +28,15 @@ Given(/^I rotate all keys with the '(.+)' options?$/) do |optionlist|
|
|
28
28
|
run_simple "knife vault rotate all keys -z -c knife.rb #{options}"
|
29
29
|
end
|
30
30
|
|
31
|
+
Given(/^I refresh the vault item '(.+)\/(.+)'$/) do |vault, item|
|
32
|
+
run_simple "knife vault refresh #{vault} #{item} -c knife.rb -z"
|
33
|
+
end
|
34
|
+
|
35
|
+
Given(/^I refresh the vault item '(.+)\/(.+)' with the '(.+)' options?$/) do |vault, item, optionlist|
|
36
|
+
options = optionlist.split(/,/).map{|o| "--#{o}"}.join(' ')
|
37
|
+
run_simple "knife vault refresh #{vault} #{item} -c knife.rb -z #{options}"
|
38
|
+
end
|
39
|
+
|
31
40
|
Given(/^I try to decrypt the vault item '(.+)\/(.+)' as '(.+)'$/) do |vault, item, node|
|
32
41
|
run_simple "knife vault show #{vault} #{item} -z -c knife.rb -u #{node} -k #{node}.pem", false
|
33
42
|
end
|
@@ -93,3 +102,15 @@ end
|
|
93
102
|
Given(/^I add '(.+)' as an admin for the vault item '(.+)\/(.+)'$/) do |newadmin, vault, item|
|
94
103
|
run_simple "knife vault update #{vault} #{item} -c knife.rb -z -A #{newadmin}"
|
95
104
|
end
|
105
|
+
|
106
|
+
Given(/^I show the keys of the vault '(.+)'$/) do |vault|
|
107
|
+
run_simple "knife vault show #{vault} -c knife.rb -z"
|
108
|
+
end
|
109
|
+
|
110
|
+
Given(/^I check if the data bag item '(.+)\/(.+)' is a vault$/) do |vault, item|
|
111
|
+
run_simple "knife vault isvault #{vault} #{item} -c knife.rb -z", false
|
112
|
+
end
|
113
|
+
|
114
|
+
Given(/^I check the type of the data bag item '(.+)\/(.+)'$/) do |vault, item|
|
115
|
+
run_simple "knife vault itemtype #{vault} #{item} -c knife.rb -z"
|
116
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Given(/^I create a data bag item '(.+)\/(.+)' containing the JSON '(.+)'$/) do |databag, _, json|
|
2
|
+
write_file 'item.json', json
|
3
|
+
run_simple "knife data bag from file #{databag} item.json -z -c knife.rb", false
|
4
|
+
end
|
5
|
+
|
6
|
+
Given(/^I create an encrypted data bag item '(.+)\/(.+)' containing the JSON '(.+)' with the secret '(.+)'$/) do |databag, _, json, secret|
|
7
|
+
write_file 'item.json', json
|
8
|
+
run_simple "knife data bag from file #{databag} item.json -s #{secret} -z -c knife.rb", false
|
9
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: knife vault show [VAULTNAME]
|
2
|
+
|
3
|
+
'knife vault show [VAULTNAME]' displays the keys of a vault
|
4
|
+
(i.e. the items that are not suffixed with _keys)
|
5
|
+
|
6
|
+
Scenario: show keys of a vault
|
7
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
8
|
+
And I create a vault item 'test/item' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
9
|
+
And I create a vault item 'test/item2' containing the JSON '{"foo": "bar"}' encrypted for 'one,two,three'
|
10
|
+
Then the vault item 'test/item' should be encrypted for 'one,two,three'
|
11
|
+
And 'one,two,three' should be a client for the vault item 'test/item'
|
12
|
+
And I show the keys of the vault 'test'
|
13
|
+
Then the output should match /(?m:^item$)/
|
14
|
+
And the output should match /(?m:^item2$)/
|
15
|
+
And the output should not match /(?m:^item_keys$)/
|
16
|
+
And the output should not match /(?m:^item2_keys$)/
|
17
|
+
|
18
|
+
Scenario: show keys of a data bag that is not a vault
|
19
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
20
|
+
And I create a data bag 'notavault' containing the JSON '{"id": "item", "foo": "bar"}'
|
21
|
+
And I show the keys of the vault 'notavault'
|
22
|
+
Then the output should match /data bag notavault is not a chef-vault/
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Feature: knife vault create with mismatched ID
|
2
|
+
|
3
|
+
'knife vault create' creates a vault. A JSON file can be passed
|
4
|
+
on the command line. If the vault ID specified on the command line
|
5
|
+
does not match the value of the 'id' key in the JSON file, knife
|
6
|
+
should throw an error
|
7
|
+
|
8
|
+
Scenario: create vault from JSON file with mismatched ID
|
9
|
+
Given a local mode chef repo with nodes 'one,two,three'
|
10
|
+
And I create a vault item 'test/item' containing the JSON '{"id": "eyetem"}' encrypted for 'one,two,three'
|
11
|
+
Then the output should match /id mismatch - input JSON has id 'eyetem' but vault item has id 'item'/
|
data/lib/chef-vault/item.rb
CHANGED
@@ -18,16 +18,52 @@ require 'securerandom'
|
|
18
18
|
|
19
19
|
class ChefVault
|
20
20
|
class Item < Chef::DataBagItem
|
21
|
+
# @!attribute [rw] keys
|
22
|
+
# @return [ChefVault::ItemKeys] the keys associated with this vault
|
21
23
|
attr_accessor :keys
|
24
|
+
|
25
|
+
# @!attribute [rw] encrypted_data_bag_item
|
26
|
+
# @return [nil] this attribute is not currently used
|
22
27
|
attr_accessor :encrypted_data_bag_item
|
23
28
|
|
24
|
-
|
29
|
+
# @!attribute [rw] node_name
|
30
|
+
# @return [String] the node name that is used to decrypt secrets.
|
31
|
+
# Defaults to the value of Chef::Config[:node_name]
|
32
|
+
attr_accessor :node_name
|
33
|
+
|
34
|
+
# @!attribute [rw] client_key_path
|
35
|
+
# @return [String] the path to the private key that is used to
|
36
|
+
# decrypt secrets. Defaults to the value of Chef::Config[:client_key]
|
37
|
+
attr_accessor :client_key_path
|
38
|
+
|
39
|
+
# returns the raw keys of the underlying Chef::DataBagItem. chef-vault v2
|
40
|
+
# defined #keys as a public accessor that returns the ChefVault::ItemKeys
|
41
|
+
# object for the vault. Ideally, #keys would provide Hash-like behaviour
|
42
|
+
# as it does for Chef::DataBagItem, but this would break the API. We will
|
43
|
+
# revisit this as part of the 3.x rewrite
|
44
|
+
def_delegator :@raw_data, :keys, :raw_keys
|
45
|
+
|
46
|
+
# constructs a new ChefVault::Item
|
47
|
+
# @param vault [String] the name of the data bag that contains the vault
|
48
|
+
# @param name [String] the name of the item in the vault
|
49
|
+
# @param opts [Hash]
|
50
|
+
# @option opts [String] :node_name the name of the node to decrypt secrets
|
51
|
+
# as. Defaults to the :node_name value of Chef::Config
|
52
|
+
# @option opts [String] :client_key_path the name of the node to decrypt
|
53
|
+
# secrets as. Defaults to the :client_key value of Chef::Config
|
54
|
+
def initialize(vault, name, opts = {})
|
25
55
|
super() # Don't pass parameters
|
26
56
|
@data_bag = vault
|
27
57
|
@raw_data["id"] = name
|
28
58
|
@keys = ChefVault::ItemKeys.new(vault, "#{name}_keys")
|
29
59
|
@secret = generate_secret
|
30
60
|
@encrypted = false
|
61
|
+
opts = {
|
62
|
+
:node_name => Chef::Config[:node_name],
|
63
|
+
:client_key_path => Chef::Config[:client_key]
|
64
|
+
}.merge(opts)
|
65
|
+
@node_name = opts[:node_name]
|
66
|
+
@client_key_path = opts[:client_key_path]
|
31
67
|
end
|
32
68
|
|
33
69
|
def load_keys(vault, keys)
|
@@ -35,23 +71,24 @@ class ChefVault
|
|
35
71
|
@secret = secret
|
36
72
|
end
|
37
73
|
|
38
|
-
def clients(
|
39
|
-
if
|
74
|
+
def clients(search_or_client=nil, action=:add)
|
75
|
+
if search_or_client.is_a?(Chef::ApiClient)
|
76
|
+
handle_client_action(search_or_client, action)
|
77
|
+
elsif search_or_client
|
40
78
|
results_returned = false
|
41
|
-
|
42
79
|
query = Chef::Search::Query.new
|
43
|
-
query.search(:node,
|
80
|
+
query.search(:node, search_or_client)[0].each do |node|
|
44
81
|
results_returned = true
|
45
|
-
|
46
82
|
case action
|
47
83
|
when :add
|
48
84
|
begin
|
49
|
-
|
85
|
+
client = load_client(node.name)
|
86
|
+
add_client(client)
|
50
87
|
rescue ChefVault::Exceptions::ClientNotFound
|
51
|
-
$
|
88
|
+
$stdout.puts "node '#{node.name}' has no private key; skipping"
|
52
89
|
end
|
53
90
|
when :delete
|
54
|
-
|
91
|
+
delete_client_or_node(node)
|
55
92
|
else
|
56
93
|
raise ChefVault::Exceptions::KeysActionNotValid,
|
57
94
|
"#{action} is not a valid action"
|
@@ -59,7 +96,7 @@ class ChefVault
|
|
59
96
|
end
|
60
97
|
|
61
98
|
unless results_returned
|
62
|
-
puts "WARNING: No clients were returned from search, you may not have "\
|
99
|
+
$stdout.puts "WARNING: No clients were returned from search, you may not have "\
|
63
100
|
"got what you expected!!"
|
64
101
|
end
|
65
102
|
else
|
@@ -99,10 +136,10 @@ class ChefVault
|
|
99
136
|
end
|
100
137
|
|
101
138
|
def secret
|
102
|
-
if @keys.include?(
|
103
|
-
private_key = OpenSSL::PKey::RSA.new(open(
|
139
|
+
if @keys.include?(@node_name)
|
140
|
+
private_key = OpenSSL::PKey::RSA.new(File.open(@client_key_path).read())
|
104
141
|
begin
|
105
|
-
private_key.private_decrypt(Base64.decode64(@keys[
|
142
|
+
private_key.private_decrypt(Base64.decode64(@keys[@node_name]))
|
106
143
|
rescue OpenSSL::PKey::RSAError
|
107
144
|
raise ChefVault::Exceptions::SecretDecryption,
|
108
145
|
"#{data_bag}/#{id} is encrypted for you, but your private key failed to decrypt the contents. "\
|
@@ -159,6 +196,14 @@ class ChefVault
|
|
159
196
|
# validate the format of the id before attempting to save
|
160
197
|
validate_id!(item_id)
|
161
198
|
|
199
|
+
# ensure that the ID of the vault hasn't changed since the keys
|
200
|
+
# data bag item was created
|
201
|
+
keys_id = keys['id'].match(/^(.+)_keys/)[1]
|
202
|
+
if keys_id != item_id
|
203
|
+
raise ChefVault::Exceptions::IdMismatch,
|
204
|
+
"id mismatch - input JSON has id '#{item_id}' but vault item has id '#{keys_id}'"
|
205
|
+
end
|
206
|
+
|
162
207
|
# save the keys first, raising an error if no keys were defined
|
163
208
|
if keys.admins.empty? && keys.clients.empty?
|
164
209
|
raise ChefVault::Exceptions::NoKeysDefined,
|
@@ -218,8 +263,11 @@ class ChefVault
|
|
218
263
|
end
|
219
264
|
end
|
220
265
|
|
221
|
-
|
222
|
-
|
266
|
+
# loads an existing vault item
|
267
|
+
# @param (see #initialize)
|
268
|
+
# @option (see #initialize)
|
269
|
+
def self.load(vault, name, opts = {})
|
270
|
+
item = new(vault, name, opts)
|
223
271
|
item.load_keys(vault, "#{name}_keys")
|
224
272
|
|
225
273
|
begin
|
@@ -240,6 +288,74 @@ class ChefVault
|
|
240
288
|
item
|
241
289
|
end
|
242
290
|
|
291
|
+
# determines if a data bag item looks like a vault
|
292
|
+
# @param vault [String] the name of the data bag
|
293
|
+
# @param name [String] the name of the item in the data bag
|
294
|
+
# @return [Boolean] true if the data bag item looks like a vault
|
295
|
+
def self.vault?(vault, name)
|
296
|
+
:vault == data_bag_item_type(vault, name)
|
297
|
+
end
|
298
|
+
|
299
|
+
# determines whether a data bag item is a vault, an encrypted
|
300
|
+
# data bag item, or a normal data bag item. An item is a vault
|
301
|
+
# if:
|
302
|
+
#
|
303
|
+
# a) the data bag item contains at least one key whose value is
|
304
|
+
# an hash with the key 'encrypted data'
|
305
|
+
# b) the data bag that contains the item contains a second item
|
306
|
+
# suffixed with _keys
|
307
|
+
#
|
308
|
+
# if a) is false, the item is a normal data bag
|
309
|
+
# if a) and b) are true, the item is a vault
|
310
|
+
# if a) is true but b) is false, the item is an encrypted data
|
311
|
+
# bag item
|
312
|
+
# @param vault [String] the name of the data bag
|
313
|
+
# @param name [String] the name of the item in the data bag
|
314
|
+
# @return [Symbol] one of :vault, :encrypted or :normal
|
315
|
+
def self.data_bag_item_type(vault, name)
|
316
|
+
# adapted from https://github.com/opscode-cookbooks/chef-vault/blob/v1.3.0/libraries/chef_vault_item.rb
|
317
|
+
# and https://github.com/sensu/sensu-chef/blob/2.9.0/libraries/sensu_helpers.rb
|
318
|
+
dbi = Chef::DataBagItem.load(vault, name)
|
319
|
+
encrypted = dbi.detect do |_, v|
|
320
|
+
v.is_a?(Hash) && v.key?('encrypted_data')
|
321
|
+
end
|
322
|
+
|
323
|
+
# return a symbol describing the type of item we detected
|
324
|
+
case
|
325
|
+
when encrypted && Chef::DataBag.load(vault).key?("#{name}_keys")
|
326
|
+
:vault
|
327
|
+
when encrypted
|
328
|
+
:encrypted
|
329
|
+
else
|
330
|
+
:normal
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# refreshes a vault by re-processing the search query and
|
335
|
+
# adding a secret for any nodes found (including new ones)
|
336
|
+
# @param clean_unknown_clients [Boolean] remove clients that can
|
337
|
+
# no longer be found
|
338
|
+
# @return [void]
|
339
|
+
def refresh(clean_unknown_clients = false)
|
340
|
+
unless search
|
341
|
+
raise ChefVault::Exceptions::SearchNotFound,
|
342
|
+
"#{vault}/#{item} does not have a stored search_query, "\
|
343
|
+
'probably because it was created with an older version '\
|
344
|
+
"of chef-vault. Use 'knife vault update' to update the "\
|
345
|
+
'databag with the search query.'
|
346
|
+
end
|
347
|
+
|
348
|
+
# a bit of a misnomer; this doesn't remove unknown
|
349
|
+
# admins, just clients which are nodes
|
350
|
+
remove_unknown_nodes if clean_unknown_clients
|
351
|
+
|
352
|
+
# re-process the search query to add new clients
|
353
|
+
clients(search)
|
354
|
+
|
355
|
+
# save the updated vault
|
356
|
+
save
|
357
|
+
end
|
358
|
+
|
243
359
|
private
|
244
360
|
|
245
361
|
def encrypt!
|
@@ -261,7 +377,7 @@ class ChefVault
|
|
261
377
|
rescue Net::HTTPServerException => http_error
|
262
378
|
if http_error.response.code == "404"
|
263
379
|
begin
|
264
|
-
puts "WARNING: #{admin} not found in users, trying clients."
|
380
|
+
$stdout.puts "WARNING: #{admin} not found in users, trying clients."
|
265
381
|
admin = load_client(admin)
|
266
382
|
rescue ChefVault::Exceptions::ClientNotFound
|
267
383
|
raise ChefVault::Exceptions::AdminNotFound,
|
@@ -291,7 +407,7 @@ class ChefVault
|
|
291
407
|
end
|
292
408
|
|
293
409
|
# removes unknown nodes by performing a node search
|
294
|
-
# for each of the existing
|
410
|
+
# for each of the existing clients. If the search
|
295
411
|
# returns nothing or the client cannot be loaded, then
|
296
412
|
# we remove that client from the vault
|
297
413
|
# @return [void]
|
@@ -304,7 +420,7 @@ class ChefVault
|
|
304
420
|
end
|
305
421
|
# now delete any flagged clients from the keys data bag
|
306
422
|
clients_to_remove.each do |client|
|
307
|
-
puts "Removing unknown client '#{client}'"
|
423
|
+
$stdout.puts "Removing unknown client '#{client}'"
|
308
424
|
keys.delete(client, "clients")
|
309
425
|
end
|
310
426
|
end
|
@@ -340,5 +456,32 @@ class ChefVault
|
|
340
456
|
end
|
341
457
|
true
|
342
458
|
end
|
459
|
+
|
460
|
+
# adds or deletes an API client from the vault item keys
|
461
|
+
# @param client [Chef::ApiClient] the API client to operate on
|
462
|
+
# @param action [Symbol] :add or :delete
|
463
|
+
# @return [void]
|
464
|
+
def handle_client_action(client, action)
|
465
|
+
case action
|
466
|
+
when :add
|
467
|
+
add_client(client)
|
468
|
+
when :delete
|
469
|
+
delete_client_or_node(client)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# adds a client to the vault item keys
|
474
|
+
# @param client [Chef::ApiClient] the API client to add
|
475
|
+
# @return [void]
|
476
|
+
def add_client(client)
|
477
|
+
keys.add(client, @secret, 'clients')
|
478
|
+
end
|
479
|
+
|
480
|
+
# removes a client to the vault item keys
|
481
|
+
# @param client_or_node [Chef::ApiClient,Chef::Node] the API client or node to remove
|
482
|
+
# @return [void]
|
483
|
+
def delete_client_or_node(client_or_node)
|
484
|
+
keys.delete(client_or_node.name, 'clients')
|
485
|
+
end
|
343
486
|
end
|
344
487
|
end
|