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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -11
  3. data/Changelog.md +1 -6
  4. data/Gemfile +4 -5
  5. data/KNIFE_EXAMPLES.md +66 -14
  6. data/LICENSE +201 -177
  7. data/README.md +74 -4
  8. data/Rakefile +1 -1
  9. data/bin/chef-vault +3 -2
  10. data/chef-vault.gemspec +13 -15
  11. data/features/clean.feature +0 -1
  12. data/features/clean_on_refresh.feature +0 -1
  13. data/features/clean_unknown_clients.feature +0 -1
  14. data/features/detect_and_warn_v1_vault.feature +0 -1
  15. data/features/isvault.feature +0 -1
  16. data/features/itemtype.feature +0 -1
  17. data/features/vault_create.feature +1 -2
  18. data/features/vault_list.feature +0 -1
  19. data/features/vault_show.feature +0 -1
  20. data/features/vault_show_vaultname.feature +0 -1
  21. data/features/vault_update.feature +0 -1
  22. data/features/verify_id_matches.feature +0 -1
  23. data/features/wrong_private_key.feature +0 -1
  24. data/hooks/pre-commit +43 -0
  25. data/lib/chef-vault.rb +10 -2
  26. data/lib/chef-vault/actor.rb +149 -0
  27. data/lib/chef-vault/certificate.rb +1 -1
  28. data/lib/chef-vault/chef_api.rb +39 -0
  29. data/lib/chef-vault/item.rb +57 -71
  30. data/lib/chef-vault/item_keys.rb +14 -9
  31. data/lib/chef-vault/user.rb +1 -1
  32. data/lib/chef-vault/version.rb +1 -1
  33. data/lib/chef/knife/vault_base.rb +5 -2
  34. data/lib/chef/knife/{encrypt_delete.rb → vault_clients.rb} +6 -12
  35. data/lib/chef/knife/vault_create.rb +9 -1
  36. data/lib/chef/knife/vault_remove.rb +9 -1
  37. data/lib/chef/knife/vault_rotate_all_keys.rb +1 -1
  38. data/lib/chef/knife/vault_show.rb +4 -4
  39. data/lib/chef/knife/vault_update.rb +13 -5
  40. data/spec/chef-vault/actor_spec.rb +247 -0
  41. data/spec/chef-vault/certificate_spec.rb +2 -9
  42. data/spec/chef-vault/chef_api_spec.rb +39 -0
  43. data/spec/chef-vault/item_keys_spec.rb +52 -0
  44. data/spec/chef-vault/item_spec.rb +139 -85
  45. data/spec/chef-vault/user_spec.rb +2 -9
  46. data/spec/spec_helper.rb +1 -0
  47. metadata +36 -42
  48. data/CONTRIBUTING.md +0 -118
  49. data/lib/chef-vault/chef_patch/api_client.rb +0 -45
  50. data/lib/chef-vault/chef_patch/user.rb +0 -33
  51. data/lib/chef/knife/decrypt.rb +0 -32
  52. data/lib/chef/knife/encrypt_create.rb +0 -51
  53. data/lib/chef/knife/encrypt_remove.rb +0 -42
  54. data/lib/chef/knife/encrypt_rotate_keys.rb +0 -32
  55. data/lib/chef/knife/encrypt_update.rb +0 -51
  56. data/lib/chef/knife/mixin/compat.rb +0 -33
  57. 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
- $stdout.puts "WARNING: This method is deprecated, please switch to item['value'] calls"
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
@@ -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 = nil, action = :add)
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
- client = load_client(node.name)
89
- add_client(client)
94
+ client_key = load_actor(node.name, "clients")
95
+ add_client(client_key)
90
96
  rescue ChefVault::Exceptions::ClientNotFound
91
- $stdout.puts "node '#{node.name}' has no private key; skipping"
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
- "#{action} is not a valid action"
103
+ "#{action} is not a valid action"
98
104
  end
99
105
  end
100
106
 
101
107
  unless results_returned
102
- $stdout.puts "WARNING: No clients were returned from search, you may not have "\
103
- "got what you expected!!"
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(admins = nil, action = :add)
119
- if admins
120
- admins.split(",").each do |admin|
121
- admin.strip!
122
- case action
123
- when :add
124
- keys.add(load_admin(admin), @secret, "admins")
125
- when :delete
126
- keys.delete(admin, "admins")
127
- else
128
- raise ChefVault::Exceptions::KeysActionNotValid,
129
- "#{action} is not a valid action"
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 clients.empty?
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
- clients.each do |client|
175
+ get_clients.each do |client|
167
176
  clients("name:#{client}")
168
177
  end
169
178
  end
170
179
 
171
- unless admins.empty?
180
+ unless get_admins.empty?
172
181
  # re-encrypt the new shared secret for all admins
173
- admins.each do |admin|
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 load_admin(admin)
372
- begin
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
- clients.each do |nodename|
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
- $stdout.puts "Removing unknown client '#{client}'"
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
- ChefVault::ChefPatch::ApiClient.load(clientname)
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(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(client)
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, "clients")
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
- keys.delete(client_or_node.name, "clients")
466
+ client = load_actor(client_or_node.name, "clients")
467
+ keys.delete(client)
482
468
  end
483
469
  end
484
470
  end
@@ -34,22 +34,20 @@ class ChefVault
34
34
  @raw_data.keys.include?(key)
35
35
  end
36
36
 
37
- def add(chef_client, data_bag_shared_secret, type)
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
- public_key = OpenSSL::PKey::RSA.new chef_client.public_key
43
- self[chef_client.name] =
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(chef_client, type)
51
- raw_data.delete(chef_client)
52
- raw_data[type].delete(chef_client)
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
@@ -24,7 +24,7 @@ class ChefVault
24
24
  end
25
25
 
26
26
  def decrypt_password
27
- $stdout.puts "WARNING: This method is deprecated, please switch to item['value'] calls"
27
+ ChefVault::Log.warn "This method is deprecated, please switch to item['value'] calls"
28
28
  @item["password"]
29
29
  end
30
30
  end
@@ -15,6 +15,6 @@
15
15
  # limitations under the License.
16
16
 
17
17
  class ChefVault
18
- VERSION = "2.9.2"
18
+ VERSION = "3.0.0.rc1"
19
19
  MAJOR, MINOR, TINY = VERSION.split(".")
20
20
  end
@@ -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 EncryptDelete class
2
- # Copyright 2013-15, Nordstrom, Inc.
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
- class EncryptDelete < VaultDelete
22
- include Knife::VaultBase
23
-
24
- banner "knife encrypt delete VAULT ITEM (options)"
18
+ module VaultClients
19
+ private
25
20
 
26
- def run
27
- $stdout.puts "DEPRECATION WARNING: knife encrypt is deprecated. Please use knife vault instead."
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