chef-encrypted-attributes 0.3.0 → 0.4.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +40 -4
- data/CONTRIBUTING.md +7 -6
- data/KNIFE.md +151 -0
- data/README.md +70 -192
- data/Rakefile +27 -14
- data/TESTING.md +18 -7
- data/TODO.md +2 -5
- data/lib/chef-encrypted-attributes.rb +7 -1
- data/lib/chef/encrypted_attribute.rb +282 -121
- data/lib/chef/encrypted_attribute/api.rb +521 -0
- data/lib/chef/encrypted_attribute/assertions.rb +16 -6
- data/lib/chef/encrypted_attribute/cache_lru.rb +54 -13
- data/lib/chef/encrypted_attribute/config.rb +198 -89
- data/lib/chef/encrypted_attribute/encrypted_mash.rb +127 -33
- data/lib/chef/encrypted_attribute/encrypted_mash/version0.rb +236 -48
- data/lib/chef/encrypted_attribute/encrypted_mash/version1.rb +249 -36
- data/lib/chef/encrypted_attribute/encrypted_mash/version2.rb +133 -19
- data/lib/chef/encrypted_attribute/exceptions.rb +19 -3
- data/lib/chef/encrypted_attribute/local_node.rb +15 -4
- data/lib/chef/encrypted_attribute/remote_clients.rb +33 -17
- data/lib/chef/encrypted_attribute/remote_node.rb +84 -29
- data/lib/chef/encrypted_attribute/remote_nodes.rb +62 -11
- data/lib/chef/encrypted_attribute/remote_users.rb +58 -19
- data/lib/chef/encrypted_attribute/search_helper.rb +214 -74
- data/lib/chef/encrypted_attribute/version.rb +3 -1
- data/lib/chef/encrypted_attributes.rb +20 -0
- data/lib/chef/knife/core/config.rb +4 -1
- data/lib/chef/knife/core/encrypted_attribute_base.rb +179 -0
- data/lib/chef/knife/core/encrypted_attribute_depends.rb +43 -0
- data/lib/chef/knife/core/encrypted_attribute_editor_options.rb +125 -61
- data/lib/chef/knife/encrypted_attribute_create.rb +51 -31
- data/lib/chef/knife/encrypted_attribute_delete.rb +32 -40
- data/lib/chef/knife/encrypted_attribute_edit.rb +51 -32
- data/lib/chef/knife/encrypted_attribute_show.rb +30 -55
- data/lib/chef/knife/encrypted_attribute_update.rb +43 -28
- data/spec/benchmark_helper.rb +2 -1
- data/spec/integration_helper.rb +1 -0
- data/spec/spec_helper.rb +21 -7
- metadata +75 -36
- metadata.gz.sig +1 -1
- data/API.md +0 -174
- data/INTERNAL.md +0 -166
data/Rakefile
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
#
|
1
|
+
# encoding: UTF-8
|
2
|
+
# -*- mode: ruby -*-
|
3
|
+
# vi: set ft=ruby :
|
4
|
+
|
5
|
+
# More info at https://github.com/jimweirich/rake/blob/master/doc/rakefile.rdoc
|
3
6
|
|
4
7
|
#
|
5
8
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
@@ -24,23 +27,33 @@ Bundler::GemHelper.install_tasks
|
|
24
27
|
|
25
28
|
require 'rake/testtask'
|
26
29
|
|
30
|
+
desc 'Run RuboCop style checks'
|
31
|
+
task :rubocop do
|
32
|
+
require 'rubocop/rake_task'
|
33
|
+
RuboCop::RakeTask.new
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Run all style checks'
|
37
|
+
task style: %w(rubocop)
|
38
|
+
|
27
39
|
{
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
40
|
+
test: '{unit,integration}',
|
41
|
+
unit: 'unit',
|
42
|
+
integration: 'integration',
|
43
|
+
benchmark: 'benchmark'
|
32
44
|
}.each do |test, dir|
|
33
|
-
Rake::TestTask.new(test) do |
|
34
|
-
|
35
|
-
|
36
|
-
|
45
|
+
Rake::TestTask.new(test) do |t|
|
46
|
+
t.libs << 'lib' << 'spec'
|
47
|
+
t.pattern = "spec/#{dir}/**/*.rb"
|
48
|
+
t.verbose = true
|
37
49
|
end
|
38
50
|
end
|
39
51
|
|
40
52
|
if RUBY_VERSION < '1.9.3'
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
53
|
+
# Integration tests are broken in 1.9.2 due to a chef-zero bug:
|
54
|
+
# https://github.com/opscode/chef-zero/issues/65
|
55
|
+
# RuboCop require Ruby 1.9.3.
|
56
|
+
task default: %w(unit)
|
44
57
|
else
|
45
|
-
task :
|
58
|
+
task default: %w(style test)
|
46
59
|
end
|
data/TESTING.md
CHANGED
@@ -1,22 +1,33 @@
|
|
1
1
|
# Testing
|
2
2
|
|
3
|
+
## Installing the Requirements
|
4
|
+
|
5
|
+
You can install gem dependencies with bundler:
|
6
|
+
|
7
|
+
$ gem install bundler
|
8
|
+
$ bundler install
|
9
|
+
|
3
10
|
## All the Tests
|
4
11
|
|
5
|
-
$ rake test
|
12
|
+
$ bundle exec rake test
|
13
|
+
|
14
|
+
## Running the Syntax Style Tests
|
15
|
+
|
16
|
+
$ bundle exec rake style
|
6
17
|
|
7
|
-
## Unit Tests
|
18
|
+
## Running the Unit Tests
|
8
19
|
|
9
|
-
$ rake unit
|
20
|
+
$ bundle exec rake unit
|
10
21
|
|
11
|
-
## Integration Tests
|
22
|
+
## Running the Integration Tests
|
12
23
|
|
13
|
-
$ rake integration
|
24
|
+
$ bundle exec rake integration
|
14
25
|
|
15
|
-
## Benchmarks
|
26
|
+
## Running the Benchmarks
|
16
27
|
|
17
28
|
You can run some simple benchmarks, not at all realistic:
|
18
29
|
|
19
|
-
$ rspec spec/benchmark/*
|
30
|
+
$ bundle exec rspec spec/benchmark/*
|
20
31
|
user system total real
|
21
32
|
Local EncryptedAttribute read (v=0) 0.410000 0.000000 0.410000 ( 0.417956)
|
22
33
|
Local EncryptedAttribute read (v=1) 0.390000 0.010000 0.400000 ( 0.398934)
|
data/TODO.md
CHANGED
@@ -1,20 +1,17 @@
|
|
1
1
|
TODO
|
2
2
|
====
|
3
3
|
|
4
|
+
* Fix all RuboCop offenses.
|
4
5
|
* knife encrypted attribute create/edit from file.
|
5
6
|
* Save config inside encrypted data: `:client_search`, `:node_search` and `:keys` (including user keys).
|
6
7
|
* Chef internal node attribute integration monkey-patch. It may require some `EncryptedMash` class rewrite or adding some methods.
|
7
8
|
* Support for Chef `< 11.4` (add `JSONCompat#map_to_rb_obj`, disable `Chef::User` for `< 11.2`, ...).
|
8
|
-
* Test with Chef `10`.
|
9
|
-
* Add Ruby `1.8` support?
|
10
|
-
* Document the Ruby code.
|
11
9
|
* Add more info/debug prints.
|
12
10
|
* Space-optimized `EncryptedMash::Version3` class.
|
13
11
|
* Tests: Add test helper functions (key generation, ApiClients including priv keys, Node creation...).
|
14
12
|
* Tests: Add more tests for `EncryptedMash::Version1` and `EncryptedMash::Version2`.
|
15
13
|
* Tests: Add unit tests for `EncryptedAttribute`.
|
16
14
|
* Tests: Add unit tests for all knife commands.
|
17
|
-
* Tests:
|
18
|
-
* Tests: Review and clean some tests.
|
15
|
+
* Tests: `raise_error` tests always include regex.
|
19
16
|
* Add `chef-vault` to benchmarks.
|
20
17
|
* Signed attributes?
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -16,4 +17,9 @@
|
|
16
17
|
# limitations under the License.
|
17
18
|
#
|
18
19
|
|
19
|
-
|
20
|
+
warn(
|
21
|
+
'[DEPRECATION] The required "chef-encrypted-attributes" file has been moved '\
|
22
|
+
'to "chef/encrypted_attributes" and will be removed in a future release. '\
|
23
|
+
'Please switch to "chef/encrypted_attributes" as soon as possible.'
|
24
|
+
)
|
25
|
+
require 'chef/encrypted_attributes'
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
#
|
2
3
|
# Author:: Xabier de Zuazo (<xabier@onddo.com>)
|
3
4
|
# Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
|
@@ -21,6 +22,7 @@ require 'chef/encrypted_attribute/encrypted_mash'
|
|
21
22
|
require 'chef/config'
|
22
23
|
require 'chef/mash'
|
23
24
|
|
25
|
+
require 'chef/encrypted_attribute/api'
|
24
26
|
require 'chef/encrypted_attribute/local_node'
|
25
27
|
require 'chef/encrypted_attribute/remote_node'
|
26
28
|
require 'chef/encrypted_attribute/remote_nodes'
|
@@ -30,60 +32,227 @@ require 'chef/encrypted_attribute/encrypted_mash/version0'
|
|
30
32
|
require 'chef/encrypted_attribute/encrypted_mash/version1'
|
31
33
|
require 'chef/encrypted_attribute/encrypted_mash/version2'
|
32
34
|
|
33
|
-
|
35
|
+
unless Chef::Config[:encrypted_attributes].is_a?(Hash)
|
36
|
+
Chef::Config[:encrypted_attributes] = Mash.new
|
37
|
+
end
|
34
38
|
|
35
39
|
class Chef
|
40
|
+
# Main EncryptedAttribute class.
|
41
|
+
#
|
42
|
+
# This class contains both static and instance level public methods.
|
43
|
+
# Internally, all work with {EncryptedMash} object instances.
|
44
|
+
#
|
45
|
+
# # Class Methods
|
46
|
+
#
|
47
|
+
# The *class methods* (or static methods) are normally used **from Chef
|
48
|
+
# cookbooks**.
|
49
|
+
#
|
50
|
+
# The attributes create with the class methods are encrypted **only for the
|
51
|
+
# local node** by default.
|
52
|
+
#
|
53
|
+
# The static `*_on_node` methods can be used, although they have not been
|
54
|
+
# designed for this purpose (have not been tested).
|
55
|
+
#
|
56
|
+
# They are # documented in the {Chef::EncryptedAttribute::API} class.
|
57
|
+
#
|
58
|
+
# # Instance Methods
|
59
|
+
#
|
60
|
+
# The *instance methods* are normally used **by other libraries or gems**. For
|
61
|
+
# example, the knife extensions included in this gem uses these methods.
|
62
|
+
#
|
63
|
+
# The instance methods will grant encrypted attribute access **only to the
|
64
|
+
# remote node** by default.
|
65
|
+
#
|
66
|
+
# Usually only the `*_from_node/*_on_node` instance methods will be used.
|
67
|
+
#
|
68
|
+
# @see EncryptedAttribute::API
|
36
69
|
class EncryptedAttribute
|
70
|
+
# Include the *class methods* for the recipe API.
|
71
|
+
extend Chef::EncryptedAttribute::API
|
37
72
|
|
38
|
-
|
73
|
+
# Chef::EncryptedAttribute constructor.
|
74
|
+
#
|
75
|
+
# @param c [Config, Hash] configuration to use.
|
76
|
+
def initialize(c = nil)
|
39
77
|
config(c)
|
40
78
|
end
|
41
79
|
|
42
|
-
|
43
|
-
|
80
|
+
# Sets or gets the encrypted attribute configuration.
|
81
|
+
#
|
82
|
+
# Reads the default configuration from
|
83
|
+
# `Chef::Config[:encrypted_attributes]`.
|
84
|
+
#
|
85
|
+
# When setting using a {Chef::EncryptedAttribute::Config} class, all the
|
86
|
+
# configuration options will be replaced.
|
87
|
+
#
|
88
|
+
# When setting using a _Hash_, only the provided keys will be replaced.
|
89
|
+
#
|
90
|
+
# @param arg [Config, Hash] the configuration to set.
|
91
|
+
# @return [Config] the read or set configuration object.
|
92
|
+
def config(arg = nil)
|
93
|
+
@config ||= EncryptedAttribute::Config.new(
|
94
|
+
Chef::Config[:encrypted_attributes]
|
95
|
+
)
|
44
96
|
@config.update!(arg) unless arg.nil?
|
45
97
|
@config
|
46
98
|
end
|
47
99
|
|
48
|
-
# Decrypts an encrypted attribute from a
|
49
|
-
|
100
|
+
# Decrypts an encrypted attribute from a local node attribute.
|
101
|
+
#
|
102
|
+
# @param enc_hs [Mash] the encrypted hash as read from the node attributes.
|
103
|
+
# @param key [String, OpenSSL::PKey::RSA] private key to use in the
|
104
|
+
# decryption process, uses the local node key by default.
|
105
|
+
# @return [Hash, Array, String, ...] decrypted attribute value.
|
106
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
107
|
+
# format is wrong.
|
108
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
109
|
+
# format is not supported or unknown.
|
110
|
+
def load(enc_hs, key = nil)
|
50
111
|
enc_attr = EncryptedMash.json_create(enc_hs)
|
51
112
|
decrypted = enc_attr.decrypt(key || local_key)
|
52
|
-
decrypted['content'] # TODO check this Hash
|
113
|
+
decrypted['content'] # TODO: check this Hash
|
53
114
|
end
|
54
115
|
|
55
|
-
# Decrypts a encrypted attribute from a remote node
|
56
|
-
|
116
|
+
# Decrypts a encrypted attribute from a remote node.
|
117
|
+
#
|
118
|
+
# @param name [String] node name.
|
119
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
120
|
+
# @param key [String, OpenSSL::PKey::RSA] private key to use in the
|
121
|
+
# decryption process, uses the local key by default.
|
122
|
+
# @return [Hash, Array, String, ...] decrypted attribute value.
|
123
|
+
# @raise [ArgumentError] if the attribute path format is wrong.
|
124
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
125
|
+
# format is wrong.
|
126
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
127
|
+
# format is not supported or unknown.
|
128
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
129
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
130
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
131
|
+
def load_from_node(name, attr_ary, key = nil)
|
57
132
|
remote_node = RemoteNode.new(name)
|
58
|
-
|
133
|
+
load(remote_node.load_attribute(attr_ary, config.partial_search), key)
|
59
134
|
end
|
60
135
|
|
61
|
-
# Creates an encrypted attribute from a Hash
|
62
|
-
|
136
|
+
# Creates an encrypted attribute from a Hash.
|
137
|
+
#
|
138
|
+
# Only the **keys passed as parameter and the configured keys** will be able
|
139
|
+
# to decrypt the attribute, so beware of including your local key if you
|
140
|
+
# need to decrypt it in the future.
|
141
|
+
#
|
142
|
+
# @param value [Hash, Array, String, Fixnum, ...] the value to encrypt in
|
143
|
+
# clear.
|
144
|
+
# @param keys [String, OpenSSL::PKey::RSA] public keys that will be able to
|
145
|
+
# decrypt the attribute.
|
146
|
+
# @raise [ArgumentError] if user list is wrong.
|
147
|
+
# @return [EncryptedMash] encrypted attribute value. This is usually what is
|
148
|
+
# saved in the node attributes.
|
149
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
150
|
+
# format is wrong or does not exist.
|
151
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
152
|
+
# format is not supported or unknown.
|
153
|
+
# @raise [EncryptionFailure] if there are encryption errors.
|
154
|
+
# @raise [MessageAuthenticationFailure] if HMAC calculation error.
|
155
|
+
# @raise [InvalidPublicKey] if it is not a valid RSA public key.
|
156
|
+
# @raise [InvalidKey] if the RSA key format is wrong.
|
157
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
158
|
+
# the keys from the Chef Server.
|
159
|
+
# @raise [ClientNotFound] if client does not exist.
|
160
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
161
|
+
# @raise [RequirementsFailure] if the specified encrypted attribute
|
162
|
+
# version cannot be used.
|
163
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
164
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
165
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
166
|
+
def create(value, keys = nil)
|
63
167
|
decrypted = { 'content' => value }
|
64
168
|
|
65
169
|
enc_attr = EncryptedMash.create(config.version)
|
66
170
|
enc_attr.encrypt(decrypted, target_keys(keys))
|
67
171
|
end
|
68
172
|
|
173
|
+
# Creates an encrypted attribute on a remote node.
|
174
|
+
#
|
175
|
+
# The remote node will always be able to decrypt it. The local node will
|
176
|
+
# not be able to decrypt it by default, you must remember to include the key
|
177
|
+
# in the configuration.
|
178
|
+
#
|
179
|
+
# @param name [String] node name.
|
180
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
181
|
+
# @param value [Hash, Array, String, Fixnum, ...] the value to encrypt.
|
182
|
+
# @return [EncryptedMash] encrypted attribute value.
|
183
|
+
# @raise [ArgumentError] if the attribute path format or the user list is
|
184
|
+
# wrong.
|
185
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
186
|
+
# format is wrong or does not exist.
|
187
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
188
|
+
# format is not supported or unknown.
|
189
|
+
# @raise [EncryptionFailure] if there are encryption errors.
|
190
|
+
# @raise [MessageAuthenticationFailure] if HMAC calculation error.
|
191
|
+
# @raise [InvalidPublicKey] if it is not a valid RSA public key.
|
192
|
+
# @raise [InvalidKey] if the RSA key format is wrong.
|
193
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
194
|
+
# the keys from the Chef Server.
|
195
|
+
# @raise [ClientNotFound] if client does not exist.
|
196
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
197
|
+
# @raise [RequirementsFailure] if the specified encrypted attribute
|
198
|
+
# version cannot be used.
|
199
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
200
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
201
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
69
202
|
def create_on_node(name, attr_ary, value)
|
70
203
|
# read the client public key
|
71
204
|
node_public_key = RemoteClients.get_public_key(name)
|
72
205
|
|
73
206
|
# create the encrypted attribute
|
74
|
-
enc_attr =
|
207
|
+
enc_attr = create(value, [node_public_key])
|
75
208
|
|
76
209
|
# save encrypted attribute
|
77
210
|
remote_node = RemoteNode.new(name)
|
78
211
|
remote_node.save_attribute(attr_ary, enc_attr)
|
79
212
|
end
|
80
213
|
|
81
|
-
# Updates the keys for which
|
82
|
-
|
214
|
+
# Updates the keys for which a local attribute is encrypted.
|
215
|
+
#
|
216
|
+
# In case new keys are added or some keys are removed, the attribute will
|
217
|
+
# be re-created again.
|
218
|
+
#
|
219
|
+
# Only the **keys passed as parameter and the configured keys** will be able
|
220
|
+
# to decrypt the attribute, so beware of including your local key if you
|
221
|
+
# need to decrypt it in the future.
|
222
|
+
#
|
223
|
+
# Uses the local key to decrypt the attribute, so the local key should be
|
224
|
+
# able to read the attribute. At least before updating.
|
225
|
+
#
|
226
|
+
# @param enc_hs [Mash] encrypted attribute. This parameter value will be
|
227
|
+
# modified on updates.
|
228
|
+
# @param keys [Array<String, OpenSSL::PKey::RSA> public keys that should be
|
229
|
+
# able to read the attribute.
|
230
|
+
# @return [Boolean] Returns `true` if the encrypted attribute (the *Mash*
|
231
|
+
# parameter) has been updated.
|
232
|
+
# @raise [ArgumentError] if user list is wrong.
|
233
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
234
|
+
# format is wrong or does not exist.
|
235
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
236
|
+
# format is not supported or unknown.
|
237
|
+
# @raise [EncryptionFailure] if there are encryption errors.
|
238
|
+
# @raise [MessageAuthenticationFailure] if HMAC calculation error.
|
239
|
+
# @raise [InvalidPublicKey] if it is not a valid RSA public key.
|
240
|
+
# @raise [InvalidKey] if the RSA key format is wrong.
|
241
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
242
|
+
# the keys from the Chef Server.
|
243
|
+
# @raise [ClientNotFound] if client does not exist.
|
244
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
245
|
+
# @raise [RequirementsFailure] if the specified encrypted attribute
|
246
|
+
# version cannot be used.
|
247
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
248
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
249
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
250
|
+
# @see #config
|
251
|
+
def update(enc_hs, keys = nil)
|
83
252
|
old_enc_attr = EncryptedMash.json_create(enc_hs)
|
84
253
|
if old_enc_attr.needs_update?(target_keys(keys))
|
85
254
|
hs = old_enc_attr.decrypt(local_key)
|
86
|
-
new_enc_attr = create(hs['content'], keys) # TODO check this Hash
|
255
|
+
new_enc_attr = create(hs['content'], keys) # TODO: check this Hash
|
87
256
|
enc_hs.replace(new_enc_attr)
|
88
257
|
true
|
89
258
|
else
|
@@ -91,6 +260,42 @@ class Chef
|
|
91
260
|
end
|
92
261
|
end
|
93
262
|
|
263
|
+
# Updates the keys for which a remote attribute is encrypted.
|
264
|
+
#
|
265
|
+
# In case new keys are added or some keys are removed, the attribute will
|
266
|
+
# be re-created again.
|
267
|
+
#
|
268
|
+
# Only the **remote node and the configured keys** will be able to decrypt
|
269
|
+
# the attribute, so beware of including your local key if you need to
|
270
|
+
# decrypt it in the future.
|
271
|
+
#
|
272
|
+
# Uses the local key to decrypt the attribute, so the local key should be
|
273
|
+
# able to read the attribute. At least before updating.
|
274
|
+
#
|
275
|
+
# @param name [String] node name.
|
276
|
+
# @param attr_ary [Array<String>] node attribute path as Array.
|
277
|
+
# @return [Boolean] Returns `true` if the remote encrypted attribute has
|
278
|
+
# been updated.
|
279
|
+
# @raise [ArgumentError] if the attribute path format or the user list is
|
280
|
+
# wrong.
|
281
|
+
# @raise [UnacceptableEncryptedAttributeFormat] if encrypted attribute
|
282
|
+
# format is wrong or does not exist.
|
283
|
+
# @raise [UnsupportedEncryptedAttributeFormat] if encrypted attribute
|
284
|
+
# format is not supported or unknown.
|
285
|
+
# @raise [EncryptionFailure] if there are encryption errors.
|
286
|
+
# @raise [MessageAuthenticationFailure] if HMAC calculation error.
|
287
|
+
# @raise [InvalidPublicKey] if it is not a valid RSA public key.
|
288
|
+
# @raise [InvalidKey] if the RSA key format is wrong.
|
289
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
290
|
+
# the keys from the Chef Server.
|
291
|
+
# @raise [ClientNotFound] if client does not exist.
|
292
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
293
|
+
# @raise [RequirementsFailure] if the specified encrypted attribute
|
294
|
+
# version cannot be used.
|
295
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
296
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
297
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
298
|
+
# @see #config
|
94
299
|
def update_on_node(name, attr_ary)
|
95
300
|
# read the client public key
|
96
301
|
node_public_key = RemoteClients.get_public_key(name)
|
@@ -98,11 +303,11 @@ class Chef
|
|
98
303
|
# update the encrypted attribute
|
99
304
|
remote_node = RemoteNode.new(name)
|
100
305
|
enc_hs = remote_node.load_attribute(attr_ary, config.partial_search)
|
101
|
-
updated = update(enc_hs, [
|
306
|
+
updated = update(enc_hs, [node_public_key])
|
102
307
|
|
103
308
|
# save encrypted attribute
|
104
309
|
if updated
|
105
|
-
# TODO Node is accessed twice (
|
310
|
+
# TODO: Node is accessed twice (RemoteNode#load_attribute above)
|
106
311
|
remote_node.save_attribute(attr_ary, enc_hs)
|
107
312
|
end
|
108
313
|
updated
|
@@ -110,124 +315,80 @@ class Chef
|
|
110
315
|
|
111
316
|
protected
|
112
317
|
|
318
|
+
# Gets remote client public keys using the *client search* query included in
|
319
|
+
# the configuration.
|
320
|
+
#
|
321
|
+
# @return [Array<String>] list of client public keys.
|
322
|
+
# @raise [ClientNotFound] if client does not exist.
|
323
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
324
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
325
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
326
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
327
|
+
# @see #config
|
113
328
|
def remote_client_keys
|
114
|
-
RemoteClients.search_public_keys(
|
329
|
+
RemoteClients.search_public_keys(
|
330
|
+
config.client_search, config.partial_search
|
331
|
+
)
|
115
332
|
end
|
116
333
|
|
334
|
+
# Gets remote node public keys using the *node search* query included in the
|
335
|
+
# configuration.
|
336
|
+
#
|
337
|
+
# @return [Array<String>] list of node public keys.
|
338
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
339
|
+
# the keys from the Chef Server.
|
340
|
+
# @raise [ClientNotFound] if client does not exist.
|
341
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
342
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
343
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
344
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
345
|
+
# @see #config
|
117
346
|
def remote_node_keys
|
118
347
|
RemoteNodes.search_public_keys(config.node_search, config.partial_search)
|
119
348
|
end
|
120
349
|
|
350
|
+
# Gets remote user keys using the configured user list.
|
351
|
+
#
|
352
|
+
# @return [Array<String>] list of user public keys.
|
353
|
+
# @raise [ArgumentError] if user list is wrong.
|
354
|
+
# @see #config
|
121
355
|
def remote_user_keys
|
122
356
|
RemoteUsers.get_public_keys(config.users)
|
123
357
|
end
|
124
358
|
|
125
|
-
|
126
|
-
|
127
|
-
|
359
|
+
# Gets the public keys that should be able to read the attribute based on
|
360
|
+
# the configuration.
|
361
|
+
#
|
362
|
+
# This includes keys passed as parameter, configured keys,
|
363
|
+
# #remote_client_keys, #remote_node_keys and remote_user_keys.
|
364
|
+
#
|
365
|
+
# @param keys [Array<String>] list of public keys to include in addition to
|
366
|
+
# the configured.
|
367
|
+
# @return [Array<String>] list of user public keys.
|
368
|
+
# @raise [ArgumentError] if user list is wrong.
|
369
|
+
# @raise [InsufficientPrivileges] if you lack enough privileges to read
|
370
|
+
# the keys from the Chef Server.
|
371
|
+
# @raise [ClientNotFound] if client does not exist.
|
372
|
+
# @raise [Net::HTTPServerException] for Chef Server HTTP errors.
|
373
|
+
# @raise [SearchFailure] if there is a Chef search error.
|
374
|
+
# @raise [SearchFatalError] if the Chef search response is wrong.
|
375
|
+
# @raise [InvalidSearchKeys] if search keys structure is wrong.
|
376
|
+
# @see #config
|
377
|
+
# @see #remote_client_keys
|
378
|
+
# @see #remote_node_keys
|
379
|
+
# @see #remote_user_keys
|
380
|
+
def target_keys(keys = nil)
|
381
|
+
target_keys =
|
382
|
+
config.keys + remote_client_keys + remote_node_keys + remote_user_keys
|
383
|
+
target_keys += keys if keys.is_a?(Array)
|
128
384
|
target_keys
|
129
385
|
end
|
130
386
|
|
387
|
+
# Gets the local private key.
|
388
|
+
#
|
389
|
+
# @return [OpenSSL::PKey::RSA.new] local private (and public) key object.
|
131
390
|
def local_key
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.local_node
|
136
|
-
LocalNode.new
|
137
|
-
end
|
138
|
-
|
139
|
-
def self.config(arg)
|
140
|
-
config = EncryptedAttribute::Config.new(Chef::Config[:encrypted_attributes])
|
141
|
-
config.update!(arg)
|
142
|
-
config.keys(config.keys + [ self.local_node.public_key ])
|
143
|
-
config
|
144
|
-
end
|
145
|
-
|
146
|
-
public
|
147
|
-
|
148
|
-
def self.load(hs, c={})
|
149
|
-
Chef::Log.debug("#{self.class.name}: Loading Local Encrypted Attribute from: #{hs.to_s}")
|
150
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
151
|
-
result = enc_attr.load(hs)
|
152
|
-
Chef::Log.debug("#{self.class.name}: Local Encrypted Attribute loaded.")
|
153
|
-
result
|
154
|
-
end
|
155
|
-
|
156
|
-
def self.load_from_node(name, attr_ary, c={})
|
157
|
-
Chef::Log.debug("#{self.class.name}: Loading Remote Encrypted Attribute from #{name}: #{attr_ary.to_s}")
|
158
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
159
|
-
result = enc_attr.load_from_node(name, attr_ary)
|
160
|
-
Chef::Log.debug("#{self.class.name}: Remote Encrypted Attribute loaded.")
|
161
|
-
result
|
162
|
-
end
|
163
|
-
|
164
|
-
def self.create(value, c={})
|
165
|
-
Chef::Log.debug("#{self.class.name}: Creating Encrypted Attribute.")
|
166
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
167
|
-
result = enc_attr.create(value)
|
168
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Attribute created.")
|
169
|
-
result
|
170
|
-
end
|
171
|
-
|
172
|
-
def self.create_on_node(name, attr_ary, value, c={})
|
173
|
-
Chef::Log.debug("#{self.class.name}: Creating Remote Encrypted Attribute on #{name}: #{attr_ary.to_s}")
|
174
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
175
|
-
result = enc_attr.create_on_node(name, attr_ary, value)
|
176
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Remote Attribute created.")
|
177
|
-
result
|
178
|
-
end
|
179
|
-
|
180
|
-
def self.update(hs, c={})
|
181
|
-
Chef::Log.debug("#{self.class.name}: Updating Encrypted Attribute: #{hs.to_s}")
|
182
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
183
|
-
result = enc_attr.update(hs)
|
184
|
-
if result
|
185
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Attribute updated.")
|
186
|
-
else
|
187
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Attribute not updated.")
|
188
|
-
end
|
189
|
-
result
|
190
|
-
end
|
191
|
-
|
192
|
-
def self.update_on_node(name, attr_ary, c={})
|
193
|
-
Chef::Log.debug("#{self.class.name}: Updating Remote Encrypted Attribute on #{name}: #{attr_ary.to_s}")
|
194
|
-
enc_attr = EncryptedAttribute.new(self.config(c))
|
195
|
-
result = enc_attr.update_on_node(name, attr_ary)
|
196
|
-
if result
|
197
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Remote Attribute updated.")
|
198
|
-
else
|
199
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Remote Attribute not updated.")
|
200
|
-
end
|
201
|
-
result
|
202
|
-
end
|
203
|
-
|
204
|
-
def self.exist?(hs)
|
205
|
-
Chef::Log.debug("#{self.class.name}: Checking if Encrypted Attribute exists here: #{hs.to_s}")
|
206
|
-
result = EncryptedMash.exist?(hs)
|
207
|
-
if result
|
208
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Attribute found.")
|
209
|
-
else
|
210
|
-
Chef::Log.debug("#{self.class.name}: Encrypted Attribute not found.")
|
211
|
-
end
|
212
|
-
result
|
391
|
+
LocalNode.new.key
|
213
392
|
end
|
214
|
-
|
215
|
-
def self.exists?(*args)
|
216
|
-
Chef::Log.warn("#{self.name}.exists? is deprecated in favor of #{self.name}.exist?.")
|
217
|
-
exist?(*args)
|
218
|
-
end
|
219
|
-
|
220
|
-
def self.exist_on_node?(name, attr_ary, c={})
|
221
|
-
Chef::Log.debug("#{self.class.name}: Checking if Remote Encrypted Attribute exists on #{name}")
|
222
|
-
remote_node = RemoteNode.new(name)
|
223
|
-
node_attr = remote_node.load_attribute(attr_ary, self.config(c).partial_search)
|
224
|
-
Chef::EncryptedAttribute.exist?(node_attr)
|
225
|
-
end
|
226
|
-
|
227
|
-
def self.exists_on_node?(*args)
|
228
|
-
Chef::Log.warn("#{self.name}.exists_on_node? is deprecated in favor of #{self.name}.exist_on_node?.")
|
229
|
-
exist_on_node?(*args)
|
230
|
-
end
|
231
|
-
|
232
393
|
end
|
233
394
|
end
|