chef-vault 2.9.2 → 3.0.0.rc1
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/.travis.yml +2 -11
- data/Changelog.md +1 -6
- data/Gemfile +4 -5
- data/KNIFE_EXAMPLES.md +66 -14
- data/LICENSE +201 -177
- data/README.md +74 -4
- data/Rakefile +1 -1
- data/bin/chef-vault +3 -2
- data/chef-vault.gemspec +13 -15
- data/features/clean.feature +0 -1
- data/features/clean_on_refresh.feature +0 -1
- data/features/clean_unknown_clients.feature +0 -1
- data/features/detect_and_warn_v1_vault.feature +0 -1
- data/features/isvault.feature +0 -1
- data/features/itemtype.feature +0 -1
- data/features/vault_create.feature +1 -2
- data/features/vault_list.feature +0 -1
- data/features/vault_show.feature +0 -1
- data/features/vault_show_vaultname.feature +0 -1
- data/features/vault_update.feature +0 -1
- data/features/verify_id_matches.feature +0 -1
- data/features/wrong_private_key.feature +0 -1
- data/hooks/pre-commit +43 -0
- data/lib/chef-vault.rb +10 -2
- data/lib/chef-vault/actor.rb +149 -0
- data/lib/chef-vault/certificate.rb +1 -1
- data/lib/chef-vault/chef_api.rb +39 -0
- data/lib/chef-vault/item.rb +57 -71
- data/lib/chef-vault/item_keys.rb +14 -9
- data/lib/chef-vault/user.rb +1 -1
- data/lib/chef-vault/version.rb +1 -1
- data/lib/chef/knife/vault_base.rb +5 -2
- data/lib/chef/knife/{encrypt_delete.rb → vault_clients.rb} +6 -12
- data/lib/chef/knife/vault_create.rb +9 -1
- data/lib/chef/knife/vault_remove.rb +9 -1
- data/lib/chef/knife/vault_rotate_all_keys.rb +1 -1
- data/lib/chef/knife/vault_show.rb +4 -4
- data/lib/chef/knife/vault_update.rb +13 -5
- data/spec/chef-vault/actor_spec.rb +247 -0
- data/spec/chef-vault/certificate_spec.rb +2 -9
- data/spec/chef-vault/chef_api_spec.rb +39 -0
- data/spec/chef-vault/item_keys_spec.rb +52 -0
- data/spec/chef-vault/item_spec.rb +139 -85
- data/spec/chef-vault/user_spec.rb +2 -9
- data/spec/spec_helper.rb +1 -0
- metadata +36 -42
- data/CONTRIBUTING.md +0 -118
- data/lib/chef-vault/chef_patch/api_client.rb +0 -45
- data/lib/chef-vault/chef_patch/user.rb +0 -33
- data/lib/chef/knife/decrypt.rb +0 -32
- data/lib/chef/knife/encrypt_create.rb +0 -51
- data/lib/chef/knife/encrypt_remove.rb +0 -42
- data/lib/chef/knife/encrypt_rotate_keys.rb +0 -32
- data/lib/chef/knife/encrypt_update.rb +0 -51
- data/lib/chef/knife/mixin/compat.rb +0 -33
- data/lib/chef/knife/vault_decrypt.rb +0 -58
@@ -24,7 +24,7 @@ class ChefVault
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def decrypt_contents
|
27
|
-
|
27
|
+
ChefVault::Log.warn "This method is deprecated, please switch to item['value'] calls"
|
28
28
|
@item["contents"]
|
29
29
|
end
|
30
30
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Author:: Tyler Cloke <tyler@chef.io>
|
2
|
+
# Copyright:: Copyright 2016, Chef Software, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require "chef/server_api"
|
18
|
+
|
19
|
+
class ChefVault
|
20
|
+
class ChefApi
|
21
|
+
|
22
|
+
def rest_v0
|
23
|
+
@rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { :api_version => "0" })
|
24
|
+
end
|
25
|
+
|
26
|
+
def rest_v1
|
27
|
+
@rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { :api_version => "1" })
|
28
|
+
end
|
29
|
+
|
30
|
+
def org_scoped_rest_v0
|
31
|
+
@org_scoped_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" })
|
32
|
+
end
|
33
|
+
|
34
|
+
def org_scoped_rest_v1
|
35
|
+
@org_scoped_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "1" })
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/chef-vault/item.rb
CHANGED
@@ -69,15 +69,21 @@ class ChefVault
|
|
69
69
|
@client_key_path = opts[:client_key_path]
|
70
70
|
end
|
71
71
|
|
72
|
+
# private
|
72
73
|
def load_keys(vault, keys)
|
73
74
|
@keys = ChefVault::ItemKeys.load(vault, keys)
|
74
75
|
@secret = secret
|
75
76
|
end
|
76
77
|
|
77
|
-
def clients(search_or_client
|
78
|
+
def clients(search_or_client, action = :add)
|
78
79
|
if search_or_client.is_a?(Chef::ApiClient)
|
79
80
|
handle_client_action(search_or_client, action)
|
80
|
-
elsif search_or_client
|
81
|
+
elsif search_or_client.is_a?(Array)
|
82
|
+
search_or_client.each do |name|
|
83
|
+
client = load_actor(name, "clients")
|
84
|
+
handle_client_action(client, action)
|
85
|
+
end
|
86
|
+
else
|
81
87
|
results_returned = false
|
82
88
|
query = Chef::Search::Query.new
|
83
89
|
query.search(:node, search_or_client) do |node|
|
@@ -85,28 +91,30 @@ class ChefVault
|
|
85
91
|
case action
|
86
92
|
when :add
|
87
93
|
begin
|
88
|
-
|
89
|
-
add_client(
|
94
|
+
client_key = load_actor(node.name, "clients")
|
95
|
+
add_client(client_key)
|
90
96
|
rescue ChefVault::Exceptions::ClientNotFound
|
91
|
-
|
97
|
+
ChefVault::Log.warn "node '#{node.name}' has no private key; skipping"
|
92
98
|
end
|
93
99
|
when :delete
|
94
100
|
delete_client_or_node(node)
|
95
101
|
else
|
96
102
|
raise ChefVault::Exceptions::KeysActionNotValid,
|
97
|
-
|
103
|
+
"#{action} is not a valid action"
|
98
104
|
end
|
99
105
|
end
|
100
106
|
|
101
107
|
unless results_returned
|
102
|
-
|
103
|
-
|
108
|
+
ChefVault::Log.warn "No clients were returned from search, you may not have "\
|
109
|
+
"got what you expected!!"
|
104
110
|
end
|
105
|
-
else
|
106
|
-
keys.clients
|
107
111
|
end
|
108
112
|
end
|
109
113
|
|
114
|
+
def get_clients
|
115
|
+
keys.clients
|
116
|
+
end
|
117
|
+
|
110
118
|
def search(search_query = nil)
|
111
119
|
if search_query
|
112
120
|
keys.search_query(search_query)
|
@@ -115,25 +123,26 @@ class ChefVault
|
|
115
123
|
end
|
116
124
|
end
|
117
125
|
|
118
|
-
def admins(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
126
|
+
def admins(admin_string, action = :add)
|
127
|
+
admin_string.split(",").each do |admin|
|
128
|
+
admin.strip!
|
129
|
+
admin_key = load_actor(admin, "admins")
|
130
|
+
case action
|
131
|
+
when :add
|
132
|
+
keys.add(admin_key, @secret)
|
133
|
+
when :delete
|
134
|
+
keys.delete(admin_key)
|
135
|
+
else
|
136
|
+
raise ChefVault::Exceptions::KeysActionNotValid,
|
137
|
+
"#{action} is not a valid action"
|
131
138
|
end
|
132
|
-
else
|
133
|
-
keys.admins
|
134
139
|
end
|
135
140
|
end
|
136
141
|
|
142
|
+
def get_admins
|
143
|
+
keys.admins
|
144
|
+
end
|
145
|
+
|
137
146
|
def remove(key)
|
138
147
|
@raw_data.delete(key)
|
139
148
|
end
|
@@ -158,19 +167,19 @@ class ChefVault
|
|
158
167
|
def rotate_keys!(clean_unknown_clients = false)
|
159
168
|
@secret = generate_secret
|
160
169
|
|
161
|
-
unless
|
170
|
+
unless get_clients.empty?
|
162
171
|
# a bit of a misnomer; this doesn't remove unknown
|
163
172
|
# admins, just clients which are nodes
|
164
173
|
remove_unknown_nodes if clean_unknown_clients
|
165
174
|
# re-encrypt the new shared secret for all remaining clients
|
166
|
-
|
175
|
+
get_clients.each do |client|
|
167
176
|
clients("name:#{client}")
|
168
177
|
end
|
169
178
|
end
|
170
179
|
|
171
|
-
unless
|
180
|
+
unless get_admins.empty?
|
172
181
|
# re-encrypt the new shared secret for all admins
|
173
|
-
|
182
|
+
get_admins.each do |admin|
|
174
183
|
admins(admin)
|
175
184
|
end
|
176
185
|
end
|
@@ -179,6 +188,7 @@ class ChefVault
|
|
179
188
|
reload_raw_data
|
180
189
|
end
|
181
190
|
|
191
|
+
# private
|
182
192
|
def generate_secret(key_size = 32)
|
183
193
|
# Defaults to 32 bytes, as this is the size that a Chef
|
184
194
|
# Encrypted Data Bag Item will digest all secrets down to anyway
|
@@ -285,6 +295,11 @@ class ChefVault
|
|
285
295
|
item
|
286
296
|
end
|
287
297
|
|
298
|
+
def delete_client(client_name)
|
299
|
+
client_key = load_actor(client_name, "clients")
|
300
|
+
keys.delete(client_key)
|
301
|
+
end
|
302
|
+
|
288
303
|
# determines if a data bag item looks like a vault
|
289
304
|
# @param vault [String] the name of the data bag
|
290
305
|
# @param name [String] the name of the item in the data bag
|
@@ -368,39 +383,8 @@ class ChefVault
|
|
368
383
|
@raw_data
|
369
384
|
end
|
370
385
|
|
371
|
-
def
|
372
|
-
|
373
|
-
admin = ChefVault::ChefPatch::User.load(admin)
|
374
|
-
rescue Net::HTTPServerException => http_error
|
375
|
-
if http_error.response.code == "404"
|
376
|
-
begin
|
377
|
-
$stdout.puts "WARNING: #{admin} not found in users, trying clients."
|
378
|
-
admin = load_client(admin)
|
379
|
-
rescue ChefVault::Exceptions::ClientNotFound
|
380
|
-
raise ChefVault::Exceptions::AdminNotFound,
|
381
|
-
"FATAL: Could not find #{admin} in users or clients!"
|
382
|
-
end
|
383
|
-
else
|
384
|
-
raise http_error
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
admin
|
389
|
-
end
|
390
|
-
|
391
|
-
def load_client(client)
|
392
|
-
begin
|
393
|
-
client = ChefVault::ChefPatch::ApiClient.load(client)
|
394
|
-
rescue Net::HTTPServerException => http_error
|
395
|
-
if http_error.response.code == "404"
|
396
|
-
raise ChefVault::Exceptions::ClientNotFound,
|
397
|
-
"#{client} is not a valid chef client and/or node"
|
398
|
-
else
|
399
|
-
raise http_error
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
client
|
386
|
+
def load_actor(actor_name, type)
|
387
|
+
ChefVault::Actor.new(type, actor_name)
|
404
388
|
end
|
405
389
|
|
406
390
|
# removes unknown nodes by performing a node search
|
@@ -412,13 +396,13 @@ class ChefVault
|
|
412
396
|
# build a list of clients to remove so we don't
|
413
397
|
# mutate the clients while iterating over search results
|
414
398
|
clients_to_remove = []
|
415
|
-
|
399
|
+
get_clients.each do |nodename|
|
416
400
|
clients_to_remove.push(nodename) unless node_exists?(nodename)
|
417
401
|
end
|
418
402
|
# now delete any flagged clients from the keys data bag
|
419
403
|
clients_to_remove.each do |client|
|
420
|
-
|
421
|
-
keys.delete(client, "clients")
|
404
|
+
ChefVault::Log.warn "Removing unknown client '#{client}'"
|
405
|
+
keys.delete(load_actor(client, "clients"))
|
422
406
|
end
|
423
407
|
end
|
424
408
|
|
@@ -446,7 +430,7 @@ class ChefVault
|
|
446
430
|
# @return [Boolean] whether the client exists or not
|
447
431
|
def client_exists?(clientname)
|
448
432
|
begin
|
449
|
-
|
433
|
+
Chef::ApiClient.load(clientname)
|
450
434
|
rescue Net::HTTPServerException => http_error
|
451
435
|
return false if http_error.response.code == "404"
|
452
436
|
raise http_error
|
@@ -458,12 +442,13 @@ class ChefVault
|
|
458
442
|
# @param client [Chef::ApiClient] the API client to operate on
|
459
443
|
# @param action [Symbol] :add or :delete
|
460
444
|
# @return [void]
|
461
|
-
def handle_client_action(
|
445
|
+
def handle_client_action(api_client, action)
|
462
446
|
case action
|
463
447
|
when :add
|
448
|
+
client = load_actor(api_client.name, "clients")
|
464
449
|
add_client(client)
|
465
450
|
when :delete
|
466
|
-
delete_client_or_node(
|
451
|
+
delete_client_or_node(api_client)
|
467
452
|
end
|
468
453
|
end
|
469
454
|
|
@@ -471,14 +456,15 @@ class ChefVault
|
|
471
456
|
# @param client [Chef::ApiClient] the API client to add
|
472
457
|
# @return [void]
|
473
458
|
def add_client(client)
|
474
|
-
keys.add(client, @secret
|
459
|
+
keys.add(client, @secret)
|
475
460
|
end
|
476
461
|
|
477
462
|
# removes a client to the vault item keys
|
478
|
-
# @param client_or_node [Chef::ApiClient,Chef::Node] the API client or node to remove
|
463
|
+
# @param client_or_node [Chef::ApiClient, Chef::Node] the API client or node to remove
|
479
464
|
# @return [void]
|
480
465
|
def delete_client_or_node(client_or_node)
|
481
|
-
|
466
|
+
client = load_actor(client_or_node.name, "clients")
|
467
|
+
keys.delete(client)
|
482
468
|
end
|
483
469
|
end
|
484
470
|
end
|
data/lib/chef-vault/item_keys.rb
CHANGED
@@ -34,22 +34,20 @@ class ChefVault
|
|
34
34
|
@raw_data.keys.include?(key)
|
35
35
|
end
|
36
36
|
|
37
|
-
def add(
|
37
|
+
def add(chef_key, data_bag_shared_secret)
|
38
|
+
type = chef_key.type
|
38
39
|
unless @raw_data.key?(type)
|
39
40
|
raise ChefVault::Exceptions::V1Format,
|
40
41
|
"cannot manage a v1 vault. See UPGRADE.md for help"
|
41
42
|
end
|
42
|
-
|
43
|
-
|
44
|
-
Base64.encode64(public_key.public_encrypt(data_bag_shared_secret))
|
45
|
-
|
46
|
-
@raw_data[type] << chef_client.name unless @raw_data[type].include?(chef_client.name)
|
43
|
+
self[chef_key.name] = ChefVault::ItemKeys.encode_key(chef_key.key, data_bag_shared_secret)
|
44
|
+
@raw_data[type] << chef_key.name unless @raw_data[type].include?(chef_key.name)
|
47
45
|
@raw_data[type]
|
48
46
|
end
|
49
47
|
|
50
|
-
def delete(
|
51
|
-
raw_data.delete(
|
52
|
-
raw_data[type].delete(
|
48
|
+
def delete(chef_key)
|
49
|
+
raw_data.delete(chef_key.name)
|
50
|
+
raw_data[chef_key.type].delete(chef_key.name)
|
53
51
|
end
|
54
52
|
|
55
53
|
def search_query(search_query = nil)
|
@@ -128,5 +126,12 @@ class ChefVault
|
|
128
126
|
|
129
127
|
from_data_bag_item(data_bag_item)
|
130
128
|
end
|
129
|
+
|
130
|
+
# @private
|
131
|
+
|
132
|
+
def self.encode_key(key_string, data_bag_shared_secret)
|
133
|
+
public_key = OpenSSL::PKey::RSA.new(key_string)
|
134
|
+
Base64.encode64(public_key.public_encrypt(data_bag_shared_secret))
|
135
|
+
end
|
131
136
|
end
|
132
137
|
end
|
data/lib/chef-vault/user.rb
CHANGED
@@ -24,7 +24,7 @@ class ChefVault
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def decrypt_password
|
27
|
-
|
27
|
+
ChefVault::Log.warn "This method is deprecated, please switch to item['value'] calls"
|
28
28
|
@item["password"]
|
29
29
|
end
|
30
30
|
end
|
data/lib/chef-vault/version.rb
CHANGED
@@ -23,9 +23,7 @@ class Chef
|
|
23
23
|
includer.class_eval do
|
24
24
|
deps do
|
25
25
|
require "chef/search/query"
|
26
|
-
require File.expand_path("../mixin/compat", __FILE__)
|
27
26
|
require File.expand_path("../mixin/helper", __FILE__)
|
28
|
-
include ChefVault::Mixin::KnifeCompat
|
29
27
|
include ChefVault::Mixin::Helper
|
30
28
|
end
|
31
29
|
|
@@ -42,6 +40,11 @@ class Chef
|
|
42
40
|
exit 1
|
43
41
|
end
|
44
42
|
|
43
|
+
def configure_chef
|
44
|
+
super
|
45
|
+
ChefVault::Log.logger = Chef::Log.logger
|
46
|
+
end
|
47
|
+
|
45
48
|
private
|
46
49
|
|
47
50
|
def bag_is_vault?(bagname)
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# Description: Chef-Vault
|
2
|
-
# Copyright
|
1
|
+
# Description: Chef-Vault VaultClients module
|
2
|
+
# Copyright 2014-15, Nordstrom, Inc.
|
3
3
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -13,19 +13,13 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
require "chef/knife/vault_base"
|
17
|
-
require "chef/knife/vault_delete"
|
18
|
-
|
19
16
|
class Chef
|
20
17
|
class Knife
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
banner "knife encrypt delete VAULT ITEM (options)"
|
18
|
+
module VaultClients
|
19
|
+
private
|
25
20
|
|
26
|
-
def
|
27
|
-
|
28
|
-
super
|
21
|
+
def clients
|
22
|
+
config[:clients].split(",") if config[:clients]
|
29
23
|
end
|
30
24
|
end
|
31
25
|
end
|
@@ -15,12 +15,14 @@
|
|
15
15
|
|
16
16
|
require "chef/knife/vault_base"
|
17
17
|
require "chef/knife/vault_admins"
|
18
|
+
require "chef/knife/vault_clients"
|
18
19
|
|
19
20
|
class Chef
|
20
21
|
class Knife
|
21
22
|
class VaultCreate < Knife
|
22
23
|
include Chef::Knife::VaultBase
|
23
24
|
include Chef::Knife::VaultAdmins
|
25
|
+
include Chef::Knife::VaultClients
|
24
26
|
|
25
27
|
banner "knife vault create VAULT ITEM VALUES (options)"
|
26
28
|
|
@@ -29,6 +31,11 @@ class Chef
|
|
29
31
|
:long => "--search SEARCH",
|
30
32
|
:description => "Chef SOLR search for clients"
|
31
33
|
|
34
|
+
option :clients,
|
35
|
+
:short => "-C CLIENTS",
|
36
|
+
:long => "--clients CLIENTS",
|
37
|
+
:description => "Chef clients to be added as clients"
|
38
|
+
|
32
39
|
option :admins,
|
33
40
|
:short => "-A ADMINS",
|
34
41
|
:long => "--admins ADMINS",
|
@@ -53,7 +60,7 @@ class Chef
|
|
53
60
|
|
54
61
|
set_mode(config[:vault_mode])
|
55
62
|
|
56
|
-
if vault && item && (search || admins)
|
63
|
+
if vault && item && (search || clients || admins)
|
57
64
|
begin
|
58
65
|
vault_item = ChefVault::Item.load(vault, item)
|
59
66
|
raise ChefVault::Exceptions::ItemAlreadyExists,
|
@@ -82,6 +89,7 @@ class Chef
|
|
82
89
|
|
83
90
|
vault_item.search(search) if search
|
84
91
|
vault_item.clients(search) if search
|
92
|
+
vault_item.clients(clients) if clients
|
85
93
|
vault_item.admins(admins) if admins
|
86
94
|
|
87
95
|
vault_item.save
|