chef-vault 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|