chef-vault 2.4.0 → 2.5.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +97 -0
  4. data/Changelog.md +34 -0
  5. data/DEMO.md +13 -6
  6. data/README.md +9 -4
  7. data/Rakefile +47 -10
  8. data/THEORY.md +361 -0
  9. data/bin/chef-vault +5 -5
  10. data/chef-vault.gemspec +11 -2
  11. data/features/clean_unknown_clients.feature +28 -3
  12. data/features/step_definitions/chef-databag.rb +5 -0
  13. data/features/step_definitions/chef-repo.rb +46 -8
  14. data/features/step_definitions/chef-vault.rb +69 -15
  15. data/features/support/env.rb +2 -2
  16. data/features/vault_create.feature +54 -0
  17. data/features/vault_list.feature +26 -0
  18. data/features/vault_show.feature +46 -0
  19. data/features/vault_update.feature +17 -0
  20. data/features/wrong_private_key.feature +14 -0
  21. data/lib/chef-vault.rb +0 -1
  22. data/lib/chef-vault/certificate.rb +1 -1
  23. data/lib/chef-vault/chef_patch/api_client.rb +1 -1
  24. data/lib/chef-vault/chef_patch/user.rb +1 -1
  25. data/lib/chef-vault/exceptions.rb +33 -12
  26. data/lib/chef-vault/item.rb +262 -209
  27. data/lib/chef-vault/item_keys.rb +90 -88
  28. data/lib/chef-vault/user.rb +1 -1
  29. data/lib/chef-vault/version.rb +2 -2
  30. data/lib/chef/knife/decrypt.rb +1 -2
  31. data/lib/chef/knife/encrypt_create.rb +1 -2
  32. data/lib/chef/knife/encrypt_delete.rb +1 -2
  33. data/lib/chef/knife/encrypt_remove.rb +1 -2
  34. data/lib/chef/knife/encrypt_rotate_keys.rb +1 -2
  35. data/lib/chef/knife/encrypt_update.rb +1 -2
  36. data/lib/chef/knife/mixin/compat.rb +3 -3
  37. data/lib/chef/knife/mixin/helper.rb +6 -8
  38. data/lib/chef/knife/vault_admins.rb +1 -2
  39. data/lib/chef/knife/vault_base.rb +2 -2
  40. data/lib/chef/knife/vault_create.rb +3 -4
  41. data/lib/chef/knife/vault_decrypt.rb +3 -4
  42. data/lib/chef/knife/vault_delete.rb +2 -4
  43. data/lib/chef/knife/vault_download.rb +1 -2
  44. data/lib/chef/knife/vault_edit.rb +3 -6
  45. data/lib/chef/knife/vault_list.rb +53 -0
  46. data/lib/chef/knife/vault_refresh.rb +1 -2
  47. data/lib/chef/knife/vault_remove.rb +3 -7
  48. data/lib/chef/knife/vault_rotate_all_keys.rb +2 -4
  49. data/lib/chef/knife/vault_rotate_keys.rb +2 -4
  50. data/lib/chef/knife/vault_show.rb +4 -5
  51. data/lib/chef/knife/vault_update.rb +7 -9
  52. data/spec/chef-vault/certificate_spec.rb +0 -2
  53. data/spec/chef-vault/item_spec.rb +77 -1
  54. data/spec/chef-vault/user_spec.rb +0 -2
  55. data/spec/chef-vault_spec.rb +1 -1
  56. data/spec/spec_helper.rb +1 -3
  57. metadata +38 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 58db597b870524dd9e554ad60f20722c3b95cf9f
4
- data.tar.gz: a49a6a5af9bcb52e84830383957205dd1d4862d2
3
+ metadata.gz: d1d708e9e29a406be66537a1e4f2001bfca5c4fd
4
+ data.tar.gz: e3d77a016b45ee414f9944657f6be4037b409d53
5
5
  SHA512:
6
- metadata.gz: 7a420cc9e5b246ffcf9c44f3c00ebac5d339f8fe3c6e7c2bbe4849a8fd126cecd4386831549ec0666836ac9304da7641ff1d9f18d1395058aaca5339798f38ef
7
- data.tar.gz: f29af664121f81f45070bef3548894729fd0c6e88afba0f2669edd449876c4a1341738ec8e9d375c30044424a4b22b907cfa0ba74ccb8128a14d3a6ac6f479e5
6
+ metadata.gz: 728d1dc1cad74668f52576130e0171a1bcea7ccdf21a6f1a32861e231e194e52016d94bce5e9028f91ef87ec6a877daa22b63db05f1afe6a441910551c34bdae
7
+ data.tar.gz: e811b693b9363d55fea010ef9cdd20e2bfbe8955686eaf087d494cab30f1cdc6b64a9737d3a04b6e730622a24aee03dfc163f3c95c3ea5c24468d6360ab132d2
@@ -0,0 +1 @@
1
+ inherit_from: .rubocop_todo.yml
@@ -0,0 +1,97 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-02-09 09:22:33 -0800 using RuboCop version 0.29.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 12
9
+ Metrics/AbcSize:
10
+ Max: 43
11
+
12
+ # Offense count: 1
13
+ Metrics/BlockNesting:
14
+ Max: 4
15
+
16
+ # Offense count: 1
17
+ # Configuration parameters: CountComments.
18
+ Metrics/ClassLength:
19
+ Max: 247
20
+
21
+ # Offense count: 5
22
+ Metrics/CyclomaticComplexity:
23
+ Max: 14
24
+
25
+ # Offense count: 45
26
+ # Configuration parameters: AllowURI, URISchemes.
27
+ Metrics/LineLength:
28
+ Max: 136
29
+
30
+ # Offense count: 22
31
+ # Configuration parameters: CountComments.
32
+ Metrics/MethodLength:
33
+ Max: 40
34
+
35
+ # Offense count: 4
36
+ Metrics/PerceivedComplexity:
37
+ Max: 15
38
+
39
+ # Offense count: 1
40
+ Style/AccessorMethodName:
41
+ Enabled: false
42
+
43
+ # Offense count: 43
44
+ # Cop supports --auto-correct.
45
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
46
+ Style/AlignParameters:
47
+ Enabled: false
48
+
49
+ # Offense count: 30
50
+ Style/Documentation:
51
+ Enabled: false
52
+
53
+ # Offense count: 6
54
+ # Configuration parameters: Exclude.
55
+ Style/FileName:
56
+ Enabled: false
57
+
58
+ # Offense count: 77
59
+ # Cop supports --auto-correct.
60
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
61
+ Style/HashSyntax:
62
+ Enabled: false
63
+
64
+ # Offense count: 7
65
+ # Cop supports --auto-correct.
66
+ Style/MethodCallParentheses:
67
+ Enabled: false
68
+
69
+ # Offense count: 7
70
+ # Cop supports --auto-correct.
71
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
72
+ Style/SignalException:
73
+ Enabled: false
74
+
75
+ # Offense count: 11
76
+ # Cop supports --auto-correct.
77
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
78
+ Style/SpaceAroundEqualsInParameterDefault:
79
+ Enabled: false
80
+
81
+ # Offense count: 16
82
+ # Cop supports --auto-correct.
83
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
84
+ Style/SpaceBeforeBlockBraces:
85
+ Enabled: false
86
+
87
+ # Offense count: 18
88
+ # Cop supports --auto-correct.
89
+ # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
90
+ Style/SpaceInsideBlockBraces:
91
+ Enabled: false
92
+
93
+ # Offense count: 135
94
+ # Cop supports --auto-correct.
95
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
96
+ Style/StringLiterals:
97
+ Enabled: false
@@ -1,6 +1,40 @@
1
1
  ## Planned (Unreleased)
2
+ ## v2.6.0
3
+
4
+ This release will focus on adding any new features covered by open issues
5
+
6
+ ## v2.7.0
7
+
8
+ This release will focus on reducing tech debt:
9
+
10
+ * improve the coverage of the RSpec suite
11
+ * ensure there are Aruba tests for all the subcommands and scenarios that match DEMO.md
12
+ * clean up any leftover Rubocop issues
13
+
14
+ ## v2.99.0
15
+
16
+ This will be the last release in the 2.x branch, and is for anyone who
17
+ is constraining to '~> 2.4' (for example). Anything that we decide to
18
+ deprecate for v3.0.0 will produce warnings in this release.
19
+
20
+ ## v3.0.0
21
+
22
+ This will be a major refactor. The primary goal is to solve the bootstrap
23
+ problem where a vault can't be encrypted for a node until the node has been
24
+ created. Exactly how we will do that is open to discussion (watch the
25
+ chef-vault issues on github for news).
26
+
27
+ This release will also remove the chef-vault 1.x commands (encrypt/decrypt)
2
28
 
3
29
  ## Released
30
+ ## v2.5.0 / 2015-02-09
31
+ * when decrypting, if the vault is encrypted for the node but decryption fails, emit a more friendly error message than 'OpenSSL::PKey::RSAError: padding check failed'
32
+ * when attempting to add a client key to a vault item, warn and skip if the node doesn't have a public key (reported by Nik Ormseth)
33
+ * add a new 'knife vault list' command that lists the data bags that are vaults
34
+ * Add more detailed explanation of how chef-vault works in THEORY.md (Issue #109)
35
+ * fix a problem with the --clean-unknown-clients switch to `rotate keys` that made it impossible to delete a client that could not be searched for (i.e. the node object is deleted)
36
+ * add rubocop tasks to Rakefile and take a first pass at the low-hanging fruit
37
+
4
38
  ## v2.4.0 / 2014-12-03
5
39
  * add simplecov test coverage configuration (Doug Ireton)
6
40
  * add --clean-unknown-clients switch to knife remove/rotate (Thomas Gschwind and Reto Hermann)
data/DEMO.md CHANGED
@@ -8,11 +8,11 @@
8
8
 
9
9
  ###Put the hat in the magic show
10
10
 
11
- export assistant=aug24 #Change this to your chef id
11
+ export assistant=aug24 #Change this to your chef id
12
12
  export role=magician #Change this to the role you need to pass the secret to
13
-
13
+
14
14
  knife vault create magicshow hat \ #Create a hat object in a data bag called magicshow
15
- --mode client \ #Talk to the chef server rather than local
15
+ --mode client \ #Talk to the chef server rather than local
16
16
  --file tophat \ #Use the hat (file) we put the bunny in
17
17
  --search "role:${role}" \ #Encrypted for all *current* nodes with the magician role
18
18
  --admins "${assistant}" #Encrypted for the assistant
@@ -20,34 +20,41 @@
20
20
  ###Check the magic show is on the chef server
21
21
 
22
22
  knife data bag list
23
+ knife vault list
23
24
 
24
25
  ###Check the hat is there (and that nobody can see what's in it)
26
+
25
27
  knife data bag show magicshow hat
26
28
 
27
29
  ###Check you can see what's in it
30
+
28
31
  knife vault show magicshow hat file-content --mode client
29
32
 
30
33
  ##'Hop' on to a node with a role of 'magician'
31
34
 
32
35
  ###Install required software
36
+
33
37
  sudo apt-get install ruby-dev --yes
34
38
  sudo gem install chef-vault --no-ri --no-rdoc
35
39
 
36
40
  ###Get the bunny back out of the hat!
41
+
37
42
  sudo chef-shell --client <<EOF
38
43
  require 'chef-vault'
39
44
  puts ChefVault::Item.load('magicshow', 'hat')['file-content']
40
45
  EOF
41
46
 
42
- If you are on a node which is not a magician, an exception will be thrown,
47
+ If you are on a node which is not a magician, an exception will be thrown,
43
48
  and the node cannot see what is in the hat.
44
49
 
45
50
  #Finally, do a disappearing act.
46
51
 
47
52
  ###Make the hat disappear...
53
+
48
54
  knife vault delete magicshow hat --mode client
49
-
55
+
50
56
  ###Make the entire magic show disappear...
57
+
51
58
  knife data bag delete magicshow
52
59
 
53
- ###Thank you!
60
+ ###Thank you!
data/README.md CHANGED
@@ -3,10 +3,14 @@
3
3
 
4
4
  [![Build Status](https://travis-ci.org/Nordstrom/chef-vault.png?branch=master)](https://travis-ci.org/Nordstrom/chef-vault)
5
5
 
6
+ [![Code Climate](https://codeclimate.com/github/Nordstrom/chef-vault/badges/gpa.svg)](https://codeclimate.com/github/Nordstrom/chef-vault)
7
+
6
8
  ## DESCRIPTION:
7
9
 
8
10
  Gem that allows you to encrypt a Chef Data Bag Item using the public keys of a list of chef nodes. This allows only those chef nodes to decrypt the encrypted values.
9
11
 
12
+ For a more detailed explanation of how chef-vault works, please refer to the file THEORY.md
13
+
10
14
  ## INSTALLATION:
11
15
 
12
16
  Be sure you are running the latest version Chef. Versions earlier than 0.10.0 don't support plugins:
@@ -167,16 +171,17 @@ Author:: Kevin Moser - @moserke<br>
167
171
  Author:: Eli Klein - @eliklein<br>
168
172
  Author:: Joey Geiger - @jgeiger<br>
169
173
  Author:: Joshua Timberman - @jtimberman<br>
174
+ Author:: James FitzGibbon - @jf647<br>
170
175
 
171
176
  ## Contributors
172
177
 
173
- Contributor:: Matt Brimstone (https://github.com/brimstone)
174
- Contributor:: Thomas Gschwind (https://github.com/thg65)
175
- Contributor:: Reto Hermann
178
+ Contributor:: Matt Brimstone - @brimstone<br>
179
+ Contributor:: Thomas Gschwind - @thg65<br>
180
+ Contributor:: Reto Hermann<br>
176
181
 
177
182
  ## License
178
183
 
179
- Copyright:: Copyright (c) 2013-14 Nordstrom, Inc.<br>
184
+ Copyright:: Copyright (c) 2013-15 Nordstrom, Inc.<br>
180
185
  License:: Apache License, Version 2.0
181
186
 
182
187
  Licensed under the Apache License, Version 2.0 (the "License");
data/Rakefile CHANGED
@@ -1,16 +1,53 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
- require 'cucumber'
4
- require 'cucumber/rake/task'
5
2
 
6
- RSpec::Core::RakeTask.new(:spec)
3
+ # Style Tests
4
+ begin
5
+ require 'rubocop/rake_task'
6
+ RuboCop::RakeTask.new do |t|
7
+ t.formatters = ['progress']
8
+ t.options = ['-D']
9
+ t.patterns = %w(
10
+ lib/**/*.rb
11
+ spec/**/*.rb
12
+ ./Rakefile
13
+ )
14
+ end
7
15
 
8
- Cucumber::Rake::Task.new(:features)
16
+ # style is an alias for rubocop
17
+ task style: :rubocop
18
+ rescue LoadError
19
+ puts 'Rubocop not available; disabling rubocop tasks'
20
+ end
9
21
 
10
- task default: [:spec, :features]
22
+ # Unit Tests
23
+ begin
24
+ require 'rspec/core/rake_task'
25
+ RSpec::Core::RakeTask.new
26
+
27
+ # Coverage
28
+ desc 'Generate unit test coverage report'
29
+ task :coverage do
30
+ ENV['COVERAGE'] = 'true'
31
+ Rake::Task[:spec].invoke
32
+ end
33
+ rescue LoadError
34
+ puts 'RSpec not available; disabling rspec tasks'
35
+ # create a no-op spec task for :default
36
+ task :spec
37
+ end
11
38
 
12
- task :coverage do
13
- ENV['COVERAGE'] = '1'
14
- Rake::Task[:spec].invoke
15
- Rake::Task[:features].invoke
39
+ # Feature Tests
40
+ begin
41
+ require 'cucumber'
42
+ require 'cucumber/rake/task'
43
+ Cucumber::Rake::Task.new(:features)
44
+ rescue LoadError
45
+ puts 'Cucumber/Aruba not available; disabling feature tasks'
46
+ # create a no-op spec task for :default
47
+ task :features
16
48
  end
49
+
50
+ # test or the default task runs spec and features
51
+ desc 'run all tests'
52
+ task default: [:spec, :features]
53
+ task test: :default
@@ -0,0 +1,361 @@
1
+ # How chef-vault works
2
+
3
+ ## The Problem
4
+
5
+ Chef provides [encrypted data bags](https://docs.chef.io/chef/essentials_data_bags.html)
6
+ as part of the core software, but leaves the problem of key
7
+ distribution up to end users. Encrypted data bags are
8
+ symmetrically encrypted, and so you have to distribute the
9
+ decryption key out-of-band to your nodes (e.g. baking it into
10
+ your image or pushing it to the system before running
11
+ chef-client).
12
+
13
+ ## The Solution
14
+
15
+ Every node managed by chef has an associated client object
16
+ on the Chef Server. The client object has the public half of
17
+ an RSA keypair. The private half only lives on the node,
18
+ typically in `/etc/chef/client.pem`.
19
+
20
+ Every API user on the chef server also has an RSA keypair.
21
+ This is typically kept in your `~/.chef` directory in PEM
22
+ format. Like nodes, the public half is stored on the Chef
23
+ server.
24
+
25
+ chef-vault creates an encrypted data bag which is symmetrically
26
+ encrypted using a random secret (a 32-byte string generated
27
+ using [SecureRandom.random_bytes](http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-random_bytes)). We'll refer to this secret as the 'shared secret' through
28
+ the rest of this document.
29
+
30
+ A vault has a list of administrative users and a list of
31
+ clients. The shared secret is asymmetrically encrypted
32
+ for each of the administrators and clients using their public
33
+ key (a separate copy for each).
34
+
35
+ The administrators of a vault are API users named explicitly
36
+ when the vault is created. The clients can be provided
37
+ explicitly, but are more often determined by running a SOLR
38
+ query against the Chef server. The query is stored as part
39
+ of the \_keys data bag so that the clients can be updated by
40
+ re-executing the search.
41
+
42
+ The asymmmetrically encrypted keys are stored in a second
43
+ data bag item whose name is appended with \_keys
44
+
45
+ ## Data Bag Structure
46
+
47
+ These examples assume that I have two nodes in my Chef
48
+ server/organization, named 'one' and 'two'. I also have
49
+ two administrators named 'alice' and 'bob'.
50
+
51
+ Given a file named `item.json` containin the following:
52
+
53
+ ```json
54
+ { "foo": "bar" }
55
+ ```
56
+
57
+ Running
58
+
59
+ > knife vault create foo bar -A alice,bob -S '*:*' -J item.json
60
+
61
+ will create a data bag named 'foo' and two data bag items
62
+ within named 'bar' and 'bar_keys':
63
+
64
+ > knife data bag show foo
65
+ bar
66
+ bar_keys
67
+
68
+ The 'bar' item contains the symmetrically encrypted version
69
+ of the JSON data:
70
+
71
+ > knife data bag show -F json foo bar
72
+ {
73
+ "id": "bar",
74
+ "foo": {
75
+ "encrypted_data": "k9On2aJxnLDRwOeCr60l0C41XjIJ2+5Xu0AYFbmSvFw=\n",
76
+ "iv": "oKeiEkIlaspvhKghee30GA==\n",
77
+ "version": 1,
78
+ "cipher": "aes-256-cbc"
79
+ }
80
+ }
81
+
82
+ (this is a standard Chef encrypted data bag)
83
+
84
+ The 'bar_keys' item contains four copies of the asymetrically
85
+ encrypted shared secret:
86
+
87
+ > knife data bag show -F json foo bar_keys
88
+ {
89
+ "id": "bar_keys",
90
+ "admins": [
91
+ "alice",
92
+ "bob"
93
+ ],
94
+ "clients": [
95
+ "one",
96
+ "two"
97
+ ],
98
+ "search_query": "*:*",
99
+ "alice": "MW7hOvoc7XvYHJbbm0gWAaLNbVcHnxv5YDMnYsjiK/F1qxnFrY4X8pTwzgUI\nRsZuREpEpCSWY9C23ESolTHnBtgEHkR6Xe74NFUr9OURiAGljZL9zEzVUFJu\npds8pWjgGnqpwULxiPZT96xKEw+BnMy0ipYChdF2iaJtQzVAlIzXZoPaOXeH\nJPd1dwmD/G0X2nK0+cNEnJGUP6gideMun3S+dTN9rpP0/7bInjNRPAAXV5yY\nKNRBFgtFyG828B9uXXJ8wXaYYOzp7VLK4ehJw25g5VNkMttqNQVWyIbxGirf\nuvys/PTlCXzLkJ3+e0X5q4ZSotQ+1zJ5UVs16VOChQ==\n",
100
+ "bob": "h9qvmFyR3ygYUQgoW42ABIeCov16cSyYFlj9wKrscLFDzhs0jRrFRdvpcBBl\nqU3Glk79Y898L3C4+/EYomi2I7/EuxZozP+wgeTJDIcXQdeZwEWxGzY1JZq2\nxZYezdoWKATysAtPJEtNIPRzOGiloq+QanHDrxq3JVvZrJ/L5fE0eyV0Nh3T\nbX4X0KzZ4LzeGeUCXXOVa9C2rEHpf61PsMF79iAnULDpD++YdxDGHv6KgHJS\nVENHvyIWi4erRGrcwZRq709iB1BRm/14Zb9ZzZT/HcHIw5P47Ht0wgZ8x71V\nbhAjK410AWG9QtefDf6ybD/ERkKgVjeqcZ57TysdvQ==\n",
101
+ "one": "Ugwpeq2du2RqEzAcn1GA+Uj+dW9fHq7+coCT4LWD2CLo9og9Qu7MSkwuZXaj\nZngl31skSCyvE15ZVhhXilkwmJmrOoEU0B5FlbZTzjHxlq/ga2MhemXmASAH\nyu39if2fb++sE/g5RLy1A9EQs7oeVY53BLZCtENA5XHGjMA1WoBi1PfQTpUs\nZZKW604vs4i/zw88j1Np5o7xb77Wt7zZQsRS+uLxp7qWOTPaT85usChk5ygS\nFrPNZF/F95ODe74o6qwxAtQhKroEeUV6GSWCB2M9FTIoGH+Fhj7oDSiLT1AA\n4VqF4mCMuVMeAM2GTx5IvdIYja2GV7DbomTBNYsGiA==\n",
102
+ "two": "yO6eaCnDmNnQP5d1h1LxZyQdHhYh0BvhBhauVBv8RXWuyuY/8qC7iREPlN52\nRFCr38BStHO9/D4m+uv6SnJhKREe99eOtpddSXD92K/I4bMSCszC+/TmaZWj\nNibZonivam1SuutMbh6WPlHT6/yjIXb1w0cXxple5R+WmPttuMj13V0at0wY\nWMg4JC5/PpYoX8qfmUKvcrVxqFdbQ0YlgAzzdJwzWJOpN+ZEfiFSJopREt6L\n2wSkWezHylGmIWuGLmANSCdluk0oaEVyA1Panf8HL87tWlEc+BajY53JZxY1\n3YIZNWpelU6W/Nl8zu8R206ksKNNMk0yuhd++7F+yw==\n"
103
+ }
104
+
105
+ It also contains the list of administrators and clients for
106
+ which the key is encrypted and the query used to choose which
107
+ clients can decrypt the vault.
108
+
109
+ In practice, the search query would not be `*:*`. More common
110
+ practice is to select which nodes can decrypt the vault based
111
+ on characteristics like:
112
+
113
+ * which recipes are in the node's runlist
114
+ * which chef environment the node belongs to
115
+ * what OS or platform the node runs
116
+ * attributes collected by Ohai Plugins (such as AWS tags).
117
+
118
+ While there are four copies of the shared secret, any single
119
+ user or node will only be able to decrypt one of the copies,
120
+ because no user or node (should) have more than one private key.
121
+
122
+ Likewise, a node or user for whom the vault was not encrypted
123
+ is able to see the contents of the 'bar_keys' data bag,
124
+ but since they lack a private key that can decrypt any of the
125
+ copies of the shared secret, they cannot access the 'bar' encrypted
126
+ data bag item.
127
+
128
+ As alice or bob, I can show the contents of the vault:
129
+
130
+ > knife vault show -F json foo bar
131
+ {
132
+ "id": "bar",
133
+ "foo": "bar"
134
+ }
135
+
136
+ Or in a recipe that runs on node one or two:
137
+
138
+ vaultitem = ChefVault::Item.load('foo', 'bar')
139
+ Chef::Log.debug "the vault value foo is #{vaultitem['foo']}"
140
+
141
+ ## Use Cases
142
+
143
+ In these use cases, assume there are three API clients:
144
+
145
+ * alice (Administrator)
146
+ * bob (Administrator)
147
+ * charlie (Normal User)
148
+
149
+ And three nodes, of the given platform:
150
+
151
+ * one (Linux, Ubuntu)
152
+ * two (Linux, CentOS)
153
+ * three (Windows)
154
+
155
+ The use cases are written from the perspective of 'client'
156
+ mode using a Chef server, but work similarly in 'solo' mode
157
+ assuming that the commands are run from the directory containing
158
+ the Chef repository.
159
+
160
+ We also assume that the commands are being run from a Linux
161
+ terminal session (paths to key files are slightly different
162
+ on Windows)
163
+
164
+ ### Creating a Vault
165
+
166
+ The actor in this case is alice. When she runs
167
+
168
+ knife vault create foo bar -S os:linux -A alice,bob
169
+
170
+ After editing the empty JSON document and saving it, chef-vault
171
+ generates a random 32 byte string for the shared secret.
172
+
173
+ A new data bag named 'foo/bar_keys' is created. chef-vault then
174
+ executes the search 'os:linux' against the node index.
175
+
176
+ For each of the nodes returned from the search (one and two), the
177
+ public keys are fetched for the like-named client and the shared
178
+ secret is encrypted using it. The client name is added to the
179
+ 'clients' array in the bar_keys data bag item. The node-specific
180
+ encrypted copy of the shared secret is stored in the bar_keys
181
+ data bag item, keyed by node name.
182
+
183
+ The search query is also stored in the keys data bag, for use
184
+ during future update or refresh operations.
185
+
186
+ In a similar fashion, the public keys for alice and bob are
187
+ retrieved from the Chef server and the shared secret is
188
+ asymmetrically encrypted for each. The names of the admins are
189
+ stored in the 'admins' array in the bar_keys data bag item.
190
+
191
+ At this point, the bar_keys data bag item is only present in
192
+ memory. It is saved to the server, and if that operation fails
193
+ the vault create throws an error.
194
+
195
+ The data bag 'foo' is then fetched (or created if it does
196
+ not already exist). An encrypted data bag item named 'bar'
197
+ is created, encrypted with the shared secret. The data bag
198
+ item is save to the Chef server.
199
+
200
+ ### Decrypting a Vault using knife
201
+
202
+ The actor in this case is alice. When she runs
203
+
204
+ knife vault show foo bar
205
+
206
+ chef-vault fetches the data bag item foo/bar_keys. It
207
+ then looks for a key in the data bag named 'alice'. The value
208
+ of this key is the asymmetrically encrypted copy of the shared
209
+ secret specific to alice.
210
+
211
+ chef-vault then uses alice's private key (typically `~/.chef/alice.pem`) to decrypt the shared secret.
212
+
213
+ If the \_keys data bag did not have a key 'alice', chef-vault
214
+ would have emitted an error to the effect that the vault was not
215
+ encrypted for her and that someone else who does have access to
216
+ the bag needs to add her as an administrator before she can view
217
+ it.
218
+
219
+ Using the decrypted shared secret, chef-vault loads the Chef
220
+ encrypted data bag foo/bar. The plaintext contents of this bag
221
+ are then displayed to alice. If the appropriate options are
222
+ provided on the command line, additional information such as
223
+ the list of administrators, the list of clients and the search
224
+ query (all of which are stored in the bar_keys data bag item)
225
+ are also displayed.
226
+
227
+ ### Decrypting a Vault in a Chef Recipe
228
+
229
+ The actor in this case is chef-client (running as root) on
230
+ the server 'one'.
231
+
232
+ Assuming that the recipe contains code such as this:
233
+
234
+ chef_gem 'chef-vault'
235
+ require 'chef-vault'
236
+ vaultitem = ChefVault::Item.load('foo', 'bar')
237
+
238
+ chef-vault fetches the data bag item foo/bar_keys. It
239
+ then looks for a key in the data bag named 'one'. The value
240
+ of this key is the asymetrically encrypted copy of the shared
241
+ secret specific to the node 'one'.
242
+
243
+ chef-vault then uses the private key of node one (typically
244
+ `/etc/chef/client.pem`) to decrypt the shared secret.
245
+
246
+ If the \_keys data bag did not have a key 'one', chef-vault
247
+ would have thrown an exception indicating that the vault was
248
+ not encrypted for the node and that an administrator needs to
249
+ refresh the vault (possibly after updating the search query)
250
+ before the recipe can use the vault.
251
+
252
+ Uncaught, this would cause chef-client to abort in the compile
253
+ phase. Robust recipes can be written that catch the exception
254
+ and do other things, such as only converging resources that do
255
+ not need the secrets, or sending an alert so that a human can
256
+ update the vault, or even sending a request to a work queue
257
+ to automatically update the vault.
258
+
259
+ Using the decrypted shared secret, chef-vault loads the Chef
260
+ encrypted data bag foo/bar. The plaintext contents of this bag
261
+ are now available to the recipe in the local variable named
262
+ 'vaultitem'. At this point the data looks and feels like a
263
+ normal data bag (i.e. it behaves in a hash-like way)
264
+
265
+ ### Adding a new Administrator
266
+
267
+ The actor in this case is Alice, who wants to make charlie
268
+ an administrator of the vault. We assume she is working with
269
+ the vault created in the section 'Creating a Vault'. When she
270
+ runs
271
+
272
+ knife vault update -A charlie foo bar
273
+
274
+ This vault is decrypted in the same fashion as described
275
+ in 'Decrypting a Vault using knife'. This results in the
276
+ plain text of the shared secret being available in memory.
277
+
278
+ The search query is run again, returning the same two nodes.
279
+ The shared secret is then asymmetrically encrypted for each
280
+ as described in 'Creating a Vault'.
281
+
282
+ The shared secret is then asymmetrically encrypted for all
283
+ of the admins (alice, bob and the newly-added charlie) as
284
+ described in 'Creating a Vault'.
285
+
286
+ The data bag item 'foo/bar_keys' is then saved, followed by the
287
+ data bag item 'foo/bar'.
288
+
289
+ charlie can now view the contents of the vault using `knife
290
+ vault show` because there is a now a copy of the shared
291
+ secret that he can access.
292
+
293
+ ### Adding a new Node
294
+
295
+ The actor in this case is Alice, who wants to encrypt the
296
+ value for all nodes instead of just those running linux.
297
+ When she runs
298
+
299
+ knife vault update -S '*:*' foo bar
300
+
301
+ This vault is decrypted in the same fashion as described
302
+ in 'Decrypting a Vault using knife'. This results in the
303
+ plain text of the shared secret being available in memory.
304
+
305
+ The search query is run again, returning three nodes this
306
+ time: one, two and three. The shared secret is then
307
+ asymmetrically encrypted for each as described in 'Creating
308
+ a Vault'.
309
+
310
+ The data bag item 'foo/bar_keys' is then saved, followed by the
311
+ data bag item 'foo/bar'.
312
+
313
+ Node three can now use the vault in a recipe because
314
+ there is a now a copy of the shared secret that he it access.
315
+
316
+ ### Rotating Keys
317
+
318
+ Rotating keys chooses a new shared secret for the bag and
319
+ encrypts it for all of the administrators and clients
320
+ who currently have access. Unlike the `knife vault update`
321
+ command, the search query is not re-run to pick up new clients.
322
+
323
+ The actor in this case is Alice, who wants to rotate the keys
324
+ (perhaps to conform to an internal security policy). When
325
+ she runs
326
+
327
+ knife vault rotate_keys foo bar
328
+
329
+ The vault is decrypted in the same fashion as described
330
+ in 'Decrypting a Vault using knife'.
331
+
332
+ chef-vault generates a new 32-byte random string. It then
333
+ creates an asymmetrically encrypted version of the new
334
+ shared secret for each of the clients and administrators
335
+ listed in the data bag item 'foo/bar_keys'.
336
+
337
+ The data bag item 'foo/bar_keys' is then saved, followed by the
338
+ data bag item 'foo/bar'.
339
+
340
+ ## Failure Scenarios
341
+
342
+ Because the secret data is just a normal Chef encrypted
343
+ data bag item, the keys are stored separately in a data bag
344
+ suffixed with \_keys. When the vault is saved, the data bag
345
+ item containing the keys is saved before the encrypted data
346
+ bag is.
347
+
348
+ If the key data bag item save succeeds, but the vault data bag
349
+ item fails, the vault will be in an unsable state, because
350
+ it will be encrypted with the old shared secret, but the keys
351
+ data bag item contains asymmetrically encrypted copies of the
352
+ new shared secret.
353
+
354
+ This can be mitigated by using solo mode, which writes the
355
+ encrypted data bags to JSON files on disk rather than making
356
+ changes using the Chef Server API. Another option is to use
357
+ `knife download` to temporarily store a on-disk version of the
358
+ vault data bag item and the keys data bag item before making
359
+ changes. If any probems are encountered, the old copy of
360
+ the data bags can be loaded back into the Chef server using
361
+ `knife data bag from file`.