chef 18.0.185-x64-mingw-ucrt → 18.1.29-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -3
- data/chef.gemspec +3 -2
- data/lib/chef/application/base.rb +18 -2
- data/lib/chef/chef_fs/file_system.rb +21 -7
- data/lib/chef/client.rb +23 -6
- data/lib/chef/http/authenticator.rb +117 -34
- data/lib/chef/mixin/proxified_socket.rb +1 -1
- data/lib/chef/property.rb +8 -3
- data/lib/chef/provider/launchd.rb +1 -0
- data/lib/chef/provider/package/yum/yum_helper.py +5 -17
- data/lib/chef/provider/yum_repository.rb +13 -1
- data/lib/chef/resource/apt_repository.rb +20 -2
- data/lib/chef/resource/bash.rb +13 -0
- data/lib/chef/resource/dsc_script.rb +1 -1
- data/lib/chef/resource/launchd.rb +4 -1
- data/lib/chef/resource/macos_userdefaults.rb +3 -3
- data/lib/chef/resource/rhsm_register.rb +2 -1
- data/lib/chef/resource/selinux_fcontext.rb +1 -1
- data/lib/chef/resource/selinux_permissive.rb +1 -1
- data/lib/chef/resource/selinux_port.rb +1 -1
- data/lib/chef/resource/selinux_state.rb +1 -1
- data/lib/chef/resource/service.rb +1 -1
- data/lib/chef/resource/user.rb +2 -2
- data/lib/chef/resource/windows_user_privilege.rb +14 -10
- data/lib/chef/resource/yum_repository.rb +4 -0
- data/lib/chef/version.rb +1 -1
- data/spec/functional/assets/yumrepo-empty/repodata/01a3b489a465bcac22a43492163df43451dc6ce47d27f66de289756b91635523-filelists.sqlite.bz2 +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/401dc19bda88c82c403423fb835844d64345f7e95f5b9835888189c03834cc93-filelists.xml.gz +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/5dc1e6e73c84803f059bb3065e684e56adfc289a7e398946574d79dac6643945-primary.sqlite.bz2 +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/6bf9672d0862e8ef8b8ff05a2fd0208a922b1f5978e6589d87944c88259cb670-other.xml.gz +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/7c36572015e075add2b38b900837bcdbb8a504130ddff49b2351a7fc0affa3d4-other.sqlite.bz2 +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz +0 -0
- data/spec/functional/assets/yumrepo-empty/repodata/repomd.xml +55 -0
- data/spec/functional/resource/yum_package_spec.rb +16 -0
- data/spec/integration/client/client_spec.rb +22 -16
- data/spec/integration/client/fips_spec.rb +20 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/platform_helpers.rb +4 -0
- data/spec/unit/chef_fs/file_system_spec.rb +2 -0
- data/spec/unit/client_spec.rb +26 -2
- data/spec/unit/compliance/runner_spec.rb +8 -0
- data/spec/unit/http/authenticator_spec.rb +64 -11
- data/spec/unit/property/validation_spec.rb +30 -0
- data/spec/unit/provider/apt_repository_spec.rb +26 -5
- data/spec/unit/resource/yum_repository_spec.rb +4 -0
- metadata +21 -14
- data/distro/powershell/chef/chef.psm1 +0 -459
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 937611f440a583a253b01525212c4d6214da8f25aee63891ab928b9a3d53de67
|
4
|
+
data.tar.gz: 7d69aeb9e4df6f13d76b464c08178f9935bc9be01eed136539d5d123315f0fea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bc789e6367a7ba5277f905d460f7b5390854f62c98f0ba07270e6659e7e70bb92b2d4fc64c8d87e524a972da75657f64022d3f01305558e00f90348f8d6811a
|
7
|
+
data.tar.gz: dea177b817a903ef86d7b8bacef0aca894ca778c26615339a041186276da976eb23abede8c47c1a3b9ee459a392961ff75fbc8532475186a1d21d77a1a528fde
|
data/Gemfile
CHANGED
@@ -37,9 +37,6 @@ group(:omnibus_package, :pry) do
|
|
37
37
|
gem "pry-stack_explorer"
|
38
38
|
end
|
39
39
|
|
40
|
-
# proxifier gem is busted on ruby 3.1 and seems abandoned so use git fork of gem
|
41
|
-
gem "proxifier", git: "https://github.com/chef/ruby-proxifier", branch: "lcg/ruby-3"
|
42
|
-
|
43
40
|
# Everything except AIX and Windows
|
44
41
|
group(:ruby_shadow) do
|
45
42
|
# if ruby-shadow does a release that supports ruby-3.0 this can be removed
|
data/chef.gemspec
CHANGED
@@ -49,7 +49,8 @@ Gem::Specification.new do |s|
|
|
49
49
|
s.add_dependency "net-ftp" # remote_file resource
|
50
50
|
s.add_dependency "erubis", "~> 2.7" # template resource / cookbook syntax check
|
51
51
|
s.add_dependency "diff-lcs", ">= 1.2.4", "!= 1.4.0", "< 1.6.0" # 1.4 breaks output. Used in lib/chef/util/diff
|
52
|
-
s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3" # archive_file resource
|
52
|
+
# s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3" # archive_file resource
|
53
|
+
s.add_dependency "ffi-libarchive", "~> 1.1", ">= 1.1.3"
|
53
54
|
s.add_dependency "chef-zero", ">= 14.0.11"
|
54
55
|
s.add_dependency "chef-vault" # chef-vault resources and helpers
|
55
56
|
|
@@ -61,7 +62,7 @@ Gem::Specification.new do |s|
|
|
61
62
|
s.add_dependency "unf_ext", ">= 0.0.8.2" # This is ruby31 compatible ucrt gem version
|
62
63
|
s.add_dependency "corefoundation", "~> 0.3.4" # macos_userdefaults resource
|
63
64
|
|
64
|
-
s.add_dependency "
|
65
|
+
s.add_dependency "proxifier2", "~> 1.1"
|
65
66
|
|
66
67
|
s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
|
67
68
|
s.add_dependency "aws-sdk-secretsmanager", "~> 1.46"
|
@@ -386,8 +386,10 @@ class Chef::Application::Base < Chef::Application
|
|
386
386
|
elsif uri.scheme == "s3"
|
387
387
|
require "aws-sdk-s3" unless defined?(Aws::S3)
|
388
388
|
|
389
|
-
|
390
|
-
|
389
|
+
bucket_name = uri.hostname
|
390
|
+
s3 = Aws::S3::Client.new(region: s3_bucket_location(bucket_name))
|
391
|
+
|
392
|
+
object = s3.get_object(bucket: bucket_name, key: uri.path[1..-1])
|
391
393
|
File.open(path, "wb") do |f|
|
392
394
|
f.write(object.body.read)
|
393
395
|
end
|
@@ -403,6 +405,20 @@ class Chef::Application::Base < Chef::Application
|
|
403
405
|
end
|
404
406
|
end
|
405
407
|
|
408
|
+
def s3_bucket_location(bucket_name)
|
409
|
+
s3 = Aws::S3::Client.new(region: aws_api_region)
|
410
|
+
|
411
|
+
resp = s3.get_bucket_location(bucket: bucket_name)
|
412
|
+
resp.location_constraint
|
413
|
+
rescue Aws::S3::Errors::AccessDenied => _e
|
414
|
+
Chef::Log.warn("Missing s3:GetBucketLocation privilege, trying currently configured region #{aws_api_region}")
|
415
|
+
aws_api_region
|
416
|
+
end
|
417
|
+
|
418
|
+
def aws_api_region
|
419
|
+
ENV["AWS_REGION"] || Aws.shared_config.region || Aws::EC2Metadata.new.get("/latest/meta-data/placement/region")
|
420
|
+
end
|
421
|
+
|
406
422
|
def interval_run_chef_client
|
407
423
|
if Chef::Config[:daemonize]
|
408
424
|
Chef::Daemon.daemonize(ChefUtils::Dist::Infra::PRODUCT)
|
@@ -140,17 +140,18 @@ class Chef
|
|
140
140
|
def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui = nil, format_path = nil)
|
141
141
|
found_result = false
|
142
142
|
error = false
|
143
|
+
result = {}
|
143
144
|
list_pairs(pattern, src_root, dest_root).parallel_each do |src, dest|
|
144
145
|
found_result = true
|
145
146
|
new_dest_parent = get_or_create_parent(dest, options, ui, format_path)
|
146
|
-
child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
|
147
|
+
child_error, result = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
|
147
148
|
error ||= child_error
|
148
149
|
end
|
149
150
|
if !found_result && pattern.exact_path
|
150
151
|
ui.error "#{pattern}: No such file or directory on remote or local" if ui
|
151
152
|
error = true
|
152
153
|
end
|
153
|
-
error
|
154
|
+
[error, result]
|
154
155
|
end
|
155
156
|
|
156
157
|
# Yield entries for children that are in either +a_root+ or +b_root+, with
|
@@ -275,6 +276,7 @@ class Chef
|
|
275
276
|
# case we shouldn't waste time trying PUT if we know the file doesn't
|
276
277
|
# exist.
|
277
278
|
# Will need to decide how that works with checksums, though.
|
279
|
+
result = { "total" => 0, "success_count" => 0, "failed" => [] }
|
278
280
|
error = false
|
279
281
|
begin
|
280
282
|
dest_path = format_path.call(dest_entry) if ui
|
@@ -290,6 +292,8 @@ class Chef
|
|
290
292
|
dest_entry.delete(true)
|
291
293
|
ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
|
292
294
|
rescue Chef::ChefFS::FileSystem::NotFoundError
|
295
|
+
failure = { "src_path" => src_path, "reason" => "Entry #{dest_path} does not exist" }
|
296
|
+
result["failed"].append(failure)
|
293
297
|
ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
|
294
298
|
end
|
295
299
|
end
|
@@ -323,7 +327,7 @@ class Chef
|
|
323
327
|
if recurse_depth != 0
|
324
328
|
src_entry.children.parallel_each do |src_child|
|
325
329
|
new_dest_child = new_dest_dir.child(src_child.name)
|
326
|
-
child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
|
330
|
+
child_error, result = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
|
327
331
|
error ||= child_error
|
328
332
|
end
|
329
333
|
end
|
@@ -339,14 +343,15 @@ class Chef
|
|
339
343
|
|
340
344
|
else
|
341
345
|
# Both exist.
|
342
|
-
|
343
346
|
# If the entry can do a copy directly, do that.
|
344
347
|
if dest_entry.respond_to?(:copy_from)
|
345
348
|
if options[:force] || compare(src_entry, dest_entry)[0] == false
|
346
349
|
if options[:dry_run]
|
347
350
|
ui.output "Would update #{dest_path}" if ui
|
348
351
|
else
|
352
|
+
result["total"] += 1
|
349
353
|
dest_entry.copy_from(src_entry, options)
|
354
|
+
result["success_count"] += 1
|
350
355
|
ui.output "Updated #{dest_path}" if ui
|
351
356
|
end
|
352
357
|
end
|
@@ -359,7 +364,7 @@ class Chef
|
|
359
364
|
# If both are directories, recurse into their children
|
360
365
|
if recurse_depth != 0
|
361
366
|
child_pairs(src_entry, dest_entry).parallel_each do |src_child, dest_child|
|
362
|
-
child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
|
367
|
+
child_error, result = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
|
363
368
|
error ||= child_error
|
364
369
|
end
|
365
370
|
end
|
@@ -373,7 +378,6 @@ class Chef
|
|
373
378
|
ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
|
374
379
|
return
|
375
380
|
else
|
376
|
-
|
377
381
|
# Both are files! Copy them unless we're sure they are the same.'
|
378
382
|
if options[:diff] == false
|
379
383
|
should_copy = false
|
@@ -389,7 +393,9 @@ class Chef
|
|
389
393
|
ui.output "Would update #{dest_path}" if ui
|
390
394
|
else
|
391
395
|
src_value = src_entry.read if src_value.nil?
|
396
|
+
result["total"] += 1
|
392
397
|
dest_entry.write(src_value)
|
398
|
+
result["success_count"] += 1
|
393
399
|
ui.output "Updated #{dest_path}" if ui
|
394
400
|
end
|
395
401
|
end
|
@@ -397,17 +403,25 @@ class Chef
|
|
397
403
|
end
|
398
404
|
end
|
399
405
|
rescue RubyFileError => e
|
406
|
+
failure = { "src_path" => src_path, "reason" => e.reason }
|
407
|
+
result["failed"].append(failure)
|
400
408
|
ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
|
401
409
|
rescue DefaultEnvironmentCannotBeModifiedError => e
|
410
|
+
failure = { "src_path" => src_path, "reason" => e.reason }
|
411
|
+
result["failed"].append(failure)
|
402
412
|
ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
|
403
413
|
rescue OperationFailedError => e
|
414
|
+
failure = { "src_path" => src_path, "reason" => e.reason }
|
415
|
+
result["failed"].append(failure)
|
404
416
|
ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
|
405
417
|
error = true
|
406
418
|
rescue OperationNotAllowedError => e
|
419
|
+
failure = { "src_path" => src_path, "reason" => e.reason }
|
420
|
+
result["failed"].append(failure)
|
407
421
|
ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
|
408
422
|
error = true
|
409
423
|
end
|
410
|
-
error
|
424
|
+
[error, result]
|
411
425
|
end
|
412
426
|
|
413
427
|
def get_or_create_parent(entry, options, ui, format_path)
|
data/lib/chef/client.rb
CHANGED
@@ -66,6 +66,15 @@ class Chef
|
|
66
66
|
class Client
|
67
67
|
CRYPT_EXPORTABLE = 0x00000001
|
68
68
|
|
69
|
+
# adding these
|
70
|
+
# certstore 65536 == 0x00010000 == CurrentUser
|
71
|
+
# certstore 131072 == 0x00020000 == LocalMachine
|
72
|
+
# Reference: https://github.com/chef/win32-certstore/blob/main/lib/win32/certstore/mixin/crypto.rb#L90
|
73
|
+
CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
|
74
|
+
CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
|
75
|
+
CERT_SYSTEM_STORE_SERVICES = 0x00050000
|
76
|
+
CERT_SYSTEM_STORE_USERS = 0x00060000
|
77
|
+
|
69
78
|
attr_reader :local_context
|
70
79
|
|
71
80
|
extend Chef::Mixin::Deprecation
|
@@ -674,9 +683,15 @@ class Chef
|
|
674
683
|
|
675
684
|
# In the brave new world of No Certs On Disk, we want to put the pem file into Keychain or the Certstore
|
676
685
|
# But is it already there?
|
686
|
+
# We're solving the multi-user scenario where both a system/admin user can run on the box but also someone without
|
687
|
+
# admin rights can also run correctly locally.
|
677
688
|
def check_certstore_for_key(cert_name)
|
678
689
|
require "win32-certstore"
|
679
|
-
|
690
|
+
if Chef::Config[:auth_key_registry_type] == "user"
|
691
|
+
win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
|
692
|
+
else
|
693
|
+
win32certstore = ::Win32::Certstore.open("MY")
|
694
|
+
end
|
680
695
|
win32certstore.search("#{cert_name}")
|
681
696
|
end
|
682
697
|
|
@@ -783,8 +798,6 @@ class Chef
|
|
783
798
|
require "time" unless defined?(Time)
|
784
799
|
autoload :URI, "uri"
|
785
800
|
|
786
|
-
# KeyMigration.instance.key_migrated = true
|
787
|
-
|
788
801
|
node = Chef::Config[:node_name]
|
789
802
|
d = Time.now
|
790
803
|
if d.month == 10 || d.month == 11 || d.month == 12
|
@@ -818,9 +831,13 @@ class Chef
|
|
818
831
|
require "win32-certstore"
|
819
832
|
tempfile = Tempfile.new("#{Chef::Config[:node_name]}.pfx")
|
820
833
|
File.open(tempfile, "wb") { |f| f.print new_pfx.to_der }
|
821
|
-
|
822
|
-
|
823
|
-
|
834
|
+
# Need to determine where to store the key
|
835
|
+
if Chef::Config[:auth_key_registry_type] == "user"
|
836
|
+
win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
|
837
|
+
else
|
838
|
+
win32certstore = ::Win32::Certstore.open("MY")
|
839
|
+
end
|
840
|
+
win32certstore.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
|
824
841
|
tempfile.unlink
|
825
842
|
end
|
826
843
|
|
@@ -102,12 +102,12 @@ class Chef
|
|
102
102
|
self.class.get_cert_password
|
103
103
|
end
|
104
104
|
|
105
|
-
def
|
106
|
-
self.class.
|
105
|
+
def encrypt_pfx_pass_with_vector
|
106
|
+
self.class.encrypt_pfx_pass_with_vector
|
107
107
|
end
|
108
108
|
|
109
|
-
def
|
110
|
-
self.class.
|
109
|
+
def decrypt_pfx_pass_with_vector
|
110
|
+
self.class.decrypt_pfx_pass_with_vector
|
111
111
|
end
|
112
112
|
|
113
113
|
# Detects if a private key exists in a certificate repository like Keychain (macOS) or Certificate Store (Windows)
|
@@ -123,9 +123,18 @@ class Chef
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
def self.get_cert_user
|
127
|
+
Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine"
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.get_registry_user
|
131
|
+
Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE"
|
132
|
+
end
|
133
|
+
|
126
134
|
def self.check_certstore_for_key(client_name)
|
135
|
+
store = get_cert_user
|
127
136
|
powershell_code = <<~CODE
|
128
|
-
$cert = Get-ChildItem -path cert
|
137
|
+
$cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
|
129
138
|
if (($cert.HasPrivateKey -eq $true) -and ($cert.PrivateKey.Key.ExportPolicy -ne "NonExportable")) {
|
130
139
|
return $true
|
131
140
|
}
|
@@ -164,49 +173,86 @@ class Chef
|
|
164
173
|
end
|
165
174
|
|
166
175
|
def self.get_cert_password
|
176
|
+
store = get_registry_user
|
167
177
|
@win32registry = Chef::Win32::Registry.new
|
168
|
-
path = "
|
178
|
+
path = "#{store}\\Software\\Progress\\Authentication"
|
169
179
|
# does the registry key even exist?
|
170
|
-
|
171
|
-
|
180
|
+
# password_blob should be an array of hashes
|
181
|
+
password_blob = @win32registry.get_values(path)
|
182
|
+
if password_blob.nil? || password_blob.empty?
|
172
183
|
raise Chef::Exceptions::Win32RegKeyMissing
|
173
184
|
end
|
174
185
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
186
|
+
# Did someone have just the password stored in the registry?
|
187
|
+
raw_data = password_blob.map { |x| x[:data] }
|
188
|
+
vector = raw_data[2]
|
189
|
+
if !!vector
|
190
|
+
decrypted_password = decrypt_pfx_pass_with_vector(password_blob)
|
191
|
+
else
|
192
|
+
decrypted_password = decrypt_pfx_pass_with_password(password_blob)
|
193
|
+
if !!decrypted_password
|
194
|
+
migrate_pass_to_use_vector(decrypted_password)
|
195
|
+
else
|
196
|
+
Chef::Log.error("Failed to retrieve certificate password")
|
179
197
|
end
|
180
198
|
end
|
181
|
-
|
182
|
-
raise Chef::Exceptions::Win32RegKeyMissing
|
183
|
-
|
199
|
+
decrypted_password
|
184
200
|
rescue Chef::Exceptions::Win32RegKeyMissing
|
185
201
|
# if we don't have a password, log that and generate one
|
186
|
-
|
187
|
-
new_path = "
|
202
|
+
store = get_registry_user
|
203
|
+
new_path = "#{store}\\Software\\Progress\\Authentication"
|
188
204
|
unless @win32registry.key_exists?(new_path)
|
189
205
|
@win32registry.create_key(new_path, true)
|
190
206
|
end
|
191
|
-
|
192
|
-
size = 14
|
193
|
-
password = SecureRandom.alphanumeric(size)
|
194
|
-
encrypted_pass = encrypt_pfx_pass(password)
|
195
|
-
values = { name: "PfxPass", type: :string, data: encrypted_pass }
|
196
|
-
@win32registry.set_value(new_path, values)
|
197
|
-
password
|
207
|
+
create_and_store_new_password
|
198
208
|
end
|
199
209
|
|
200
|
-
def self.
|
210
|
+
def self.encrypt_pfx_pass_with_vector(password)
|
201
211
|
powershell_code = <<~CODE
|
202
|
-
$
|
203
|
-
$
|
204
|
-
|
212
|
+
$AES = [System.Security.Cryptography.Aes]::Create()
|
213
|
+
$key_temp = [System.Convert]::ToBase64String($AES.Key)
|
214
|
+
$iv_temp = [System.Convert]::ToBase64String($AES.IV)
|
215
|
+
$encryptor = $AES.CreateEncryptor()
|
216
|
+
[System.Byte[]]$Bytes = [System.Text.Encoding]::Unicode.GetBytes("#{password}")
|
217
|
+
$EncryptedBytes = $encryptor.TransformFinalBlock($Bytes,0,$Bytes.Length)
|
218
|
+
$EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes)
|
219
|
+
# create array of encrypted pass, key, iv
|
220
|
+
$password_blob = @($EncryptedBase64String, $key_temp, $iv_temp)
|
221
|
+
return $password_blob
|
205
222
|
CODE
|
206
223
|
powershell_exec!(powershell_code).result
|
207
224
|
end
|
208
225
|
|
209
|
-
def self.
|
226
|
+
def self.decrypt_pfx_pass_with_vector(password_blob)
|
227
|
+
raw_data = password_blob.map { |x| x[:data] }
|
228
|
+
password = raw_data[0]
|
229
|
+
key = raw_data[1]
|
230
|
+
vector = raw_data[2]
|
231
|
+
|
232
|
+
powershell_code = <<~CODE
|
233
|
+
$KeyBytes = [System.Convert]::FromBase64String("#{key}")
|
234
|
+
$IVBytes = [System.Convert]::FromBase64String("#{vector}")
|
235
|
+
$aes = [System.Security.Cryptography.Aes]::Create()
|
236
|
+
$aes.Key = $KeyBytes
|
237
|
+
$aes.IV = $IVBytes
|
238
|
+
$EncryptedBytes = [System.Convert]::FromBase64String("#{password}")
|
239
|
+
$Decryptor = $aes.CreateDecryptor()
|
240
|
+
$DecryptedBytes = $Decryptor.TransformFinalBlock($EncryptedBytes,0,$EncryptedBytes.Length)
|
241
|
+
$DecryptedString = [System.Text.Encoding]::Unicode.GetString($DecryptedBytes)
|
242
|
+
return $DecryptedString
|
243
|
+
CODE
|
244
|
+
results = powershell_exec!(powershell_code).result
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.decrypt_pfx_pass_with_password(password_blob)
|
248
|
+
password = ""
|
249
|
+
password_blob.each do |secret|
|
250
|
+
if secret[:name] == "PfxPass"
|
251
|
+
password = secret[:data]
|
252
|
+
else
|
253
|
+
Chef::Log.error("Failed to retrieve a password for the private key")
|
254
|
+
end
|
255
|
+
end
|
210
256
|
powershell_code = <<~CODE
|
211
257
|
$secure_string = "#{password}" | ConvertTo-SecureString
|
212
258
|
$string = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((($secure_string))))
|
@@ -215,14 +261,49 @@ class Chef
|
|
215
261
|
powershell_exec!(powershell_code).result
|
216
262
|
end
|
217
263
|
|
218
|
-
def self.
|
219
|
-
|
264
|
+
def self.migrate_pass_to_use_vector(password)
|
265
|
+
store = get_cert_user
|
266
|
+
corrected_store = (store == "CurrentUser" ? "HKCU" : "HKLM")
|
267
|
+
powershell_code = <<~CODE
|
268
|
+
Remove-ItemProperty -Path "#{corrected_store}:\\Software\\Progress\\Authentication" -Name "PfXPass"
|
269
|
+
CODE
|
270
|
+
powershell_exec!(powershell_code)
|
271
|
+
create_and_store_new_password(password)
|
272
|
+
end
|
220
273
|
|
274
|
+
# This method name is a bit of a misnomer. We call it to legit create a new password using the vector format.
|
275
|
+
# But we also call it with a password that needs to be migrated to use the vector format too.
|
276
|
+
def self.create_and_store_new_password(password = nil)
|
277
|
+
@win32registry = Chef::Win32::Registry.new
|
278
|
+
store = get_registry_user
|
279
|
+
path = "#{store}\\Software\\Progress\\Authentication"
|
280
|
+
if password.nil?
|
281
|
+
require "securerandom" unless defined?(SecureRandom)
|
282
|
+
size = 14
|
283
|
+
password = SecureRandom.alphanumeric(size)
|
284
|
+
end
|
285
|
+
encrypted_blob = encrypt_pfx_pass_with_vector(password)
|
286
|
+
encrypted_password = encrypted_blob[0]
|
287
|
+
key = encrypted_blob[1]
|
288
|
+
vector = encrypted_blob[2]
|
289
|
+
values = [
|
290
|
+
{ name: "PfxPass", type: :string, data: encrypted_password },
|
291
|
+
{ name: "PfxKey", type: :string, data: key },
|
292
|
+
{ name: "PfxIV", type: :string, data: vector },
|
293
|
+
]
|
294
|
+
values.each do |i|
|
295
|
+
@win32registry.set_value(path, i)
|
296
|
+
end
|
297
|
+
password
|
298
|
+
end
|
299
|
+
|
300
|
+
def self.retrieve_certificate_key(client_name)
|
221
301
|
if ChefUtils.windows?
|
302
|
+
require "openssl" unless defined?(OpenSSL)
|
222
303
|
password = get_cert_password
|
223
304
|
return false unless password
|
224
305
|
|
225
|
-
if check_certstore_for_key(client_name)
|
306
|
+
if !!check_certstore_for_key(client_name)
|
226
307
|
ps_blob = powershell_exec!(get_the_key_ps(client_name, password)).result
|
227
308
|
file_path = ps_blob["PSPath"].split("::")[1]
|
228
309
|
pkcs = OpenSSL::PKCS12.new(File.binread(file_path), password)
|
@@ -252,10 +333,11 @@ class Chef
|
|
252
333
|
end
|
253
334
|
|
254
335
|
def self.get_the_key_ps(client_name, password)
|
336
|
+
store = get_cert_user
|
255
337
|
powershell_code = <<~CODE
|
256
338
|
Try {
|
257
339
|
$my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
|
258
|
-
$cert = Get-ChildItem -path cert
|
340
|
+
$cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
|
259
341
|
$tempfile = [System.IO.Path]::GetTempPath() + "export_pfx.pfx";
|
260
342
|
Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
|
261
343
|
}
|
@@ -266,8 +348,9 @@ class Chef
|
|
266
348
|
end
|
267
349
|
|
268
350
|
def self.delete_old_key_ps(client_name)
|
351
|
+
store = get_cert_user
|
269
352
|
powershell_code = <<~CODE
|
270
|
-
Get-ChildItem -path cert
|
353
|
+
Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
|
271
354
|
CODE
|
272
355
|
end
|
273
356
|
|
data/lib/chef/property.rb
CHANGED
@@ -307,7 +307,7 @@ class Chef
|
|
307
307
|
#
|
308
308
|
def required?(action = nil)
|
309
309
|
if !action.nil? && options[:required].is_a?(Array)
|
310
|
-
options[:required]
|
310
|
+
(options[:required] & Array(action)).any?
|
311
311
|
else
|
312
312
|
!!options[:required]
|
313
313
|
end
|
@@ -426,7 +426,7 @@ class Chef
|
|
426
426
|
end
|
427
427
|
end
|
428
428
|
|
429
|
-
if value.nil? && required?
|
429
|
+
if value.nil? && required?(resource_action(resource))
|
430
430
|
raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
|
431
431
|
else
|
432
432
|
value
|
@@ -455,7 +455,7 @@ class Chef
|
|
455
455
|
Chef.deprecated(:property, options[:deprecated])
|
456
456
|
end
|
457
457
|
|
458
|
-
if value.nil? && required?
|
458
|
+
if value.nil? && required?(resource_action(resource))
|
459
459
|
raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
|
460
460
|
else
|
461
461
|
value
|
@@ -768,5 +768,10 @@ class Chef
|
|
768
768
|
end
|
769
769
|
visitor.call(value)
|
770
770
|
end
|
771
|
+
|
772
|
+
# action from resource, if available
|
773
|
+
def resource_action(resource)
|
774
|
+
resource.action if resource.respond_to?(:action)
|
775
|
+
end
|
771
776
|
end
|
772
777
|
end
|
@@ -153,6 +153,7 @@ class Chef
|
|
153
153
|
"program" => "Program",
|
154
154
|
"program_arguments" => "ProgramArguments",
|
155
155
|
"abandon_process_group" => "AbandonProcessGroup",
|
156
|
+
"associated_bundle_identifiers" => "AssociatedBundleIdentifiers",
|
156
157
|
"debug" => "Debug",
|
157
158
|
"disabled" => "Disabled",
|
158
159
|
"enable_globbing" => "EnableGlobbing",
|
@@ -53,16 +53,14 @@ def install_only_packages(base, name):
|
|
53
53
|
outpipe.flush()
|
54
54
|
|
55
55
|
def query(base, command):
|
56
|
-
enabled_repos = base.repos.listEnabled()
|
57
|
-
|
58
56
|
# Handle any repocontrols passed in with our options
|
59
57
|
|
60
58
|
if 'repos' in command:
|
61
59
|
for repo in command['repos']:
|
62
60
|
if 'enable' in repo:
|
63
61
|
base.repos.enableRepo(repo['enable'])
|
64
|
-
|
65
|
-
|
62
|
+
if 'disable' in repo:
|
63
|
+
base.repos.disableRepo(repo['disable'])
|
66
64
|
|
67
65
|
args = { 'name': command['provides'] }
|
68
66
|
do_nevra = False
|
@@ -94,8 +92,8 @@ def query(base, command):
|
|
94
92
|
# then the result was searchNevra'd. please be extremely careful if attempting to fix that
|
95
93
|
# since searchNevra does not support prco tuples.
|
96
94
|
if bool(re.search('\\s+', command['provides'])):
|
97
|
-
# handles flags (<, >, =, etc) and versions, but no
|
98
|
-
# raises error for any invalid input like: 'FOO BAR BAZ'
|
95
|
+
# handles flags (<, >, =, etc) and versions, but no wildcards
|
96
|
+
# raises error for any invalid input like: 'FOO BAR BAZ'
|
99
97
|
pkgs = obj.getProvides(*string_to_prco_tuple(command['provides']))
|
100
98
|
elif do_nevra:
|
101
99
|
# now if we're given version or arch properties explicitly, then we do a SearchNevra.
|
@@ -123,16 +121,6 @@ def query(base, command):
|
|
123
121
|
outpipe.write("%(n)s %(e)s:%(v)s-%(r)s %(a)s\n" % { 'n': pkg.name, 'e': pkg.epoch, 'v': pkg.version, 'r': pkg.release, 'a': pkg.arch })
|
124
122
|
outpipe.flush()
|
125
123
|
|
126
|
-
# Reset any repos we were passed in enablerepo/disablerepo to the original state in enabled_repos
|
127
|
-
if 'repos' in command:
|
128
|
-
for repo in command['repos']:
|
129
|
-
if 'enable' in repo:
|
130
|
-
if base.repos.getRepo(repo['enable']) not in enabled_repos:
|
131
|
-
base.repos.disableRepo(repo['enable'])
|
132
|
-
if 'disable' in repo:
|
133
|
-
if base.repos.getRepo(repo['disable']) in enabled_repos:
|
134
|
-
base.repos.enableRepo(repo['disable'])
|
135
|
-
|
136
124
|
# the design of this helper is that it should try to be 'brittle' and fail hard and exit in order
|
137
125
|
# to keep process tables clean. additional error handling should probably be added to the retry loop
|
138
126
|
# on the ruby side.
|
@@ -178,7 +166,7 @@ try:
|
|
178
166
|
|
179
167
|
try:
|
180
168
|
command = json.loads(line)
|
181
|
-
except ValueError
|
169
|
+
except ValueError as e:
|
182
170
|
raise RuntimeError("bad json parse")
|
183
171
|
|
184
172
|
if base is None:
|
@@ -44,7 +44,12 @@ class Chef
|
|
44
44
|
mode new_resource.mode
|
45
45
|
if new_resource.make_cache
|
46
46
|
notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers
|
47
|
-
|
47
|
+
# makecache fast only works on non-dnf systems.
|
48
|
+
if !which "dnf" && new_resource.makecache_fast
|
49
|
+
notifies :run, "execute[yum-makecache-fast-#{new_resource.repositoryid}]", :immediately
|
50
|
+
else
|
51
|
+
notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
|
52
|
+
end
|
48
53
|
notifies :flush_cache, "package[package-cache-reload-#{new_resource.repositoryid}]", :immediately
|
49
54
|
end
|
50
55
|
end
|
@@ -63,6 +68,13 @@ class Chef
|
|
63
68
|
only_if { new_resource.enabled }
|
64
69
|
end
|
65
70
|
|
71
|
+
# download only the minimum required metadata
|
72
|
+
execute "yum-makecache-fast-#{new_resource.repositoryid}" do
|
73
|
+
command "yum -q -y makecache fast --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
|
74
|
+
action :nothing
|
75
|
+
only_if { new_resource.enabled }
|
76
|
+
end
|
77
|
+
|
66
78
|
package "package-cache-reload-#{new_resource.repositoryid}" do
|
67
79
|
action :nothing
|
68
80
|
end
|
@@ -187,6 +187,24 @@ class Chef
|
|
187
187
|
end.compact
|
188
188
|
end
|
189
189
|
|
190
|
+
# run the specified command and extract the public key ids
|
191
|
+
# accepts the command so it can be used to extract both the current keys
|
192
|
+
# and the new keys
|
193
|
+
# @param [Array<String>] cmd the command to run
|
194
|
+
#
|
195
|
+
# @return [Array] an array of key ids
|
196
|
+
def extract_public_keys_from_cmd(*cmd)
|
197
|
+
so = shell_out(*cmd)
|
198
|
+
# Sample output
|
199
|
+
# pub:-:4096:1:D94AA3F0EFE21092:1336774248:::-:::scSC::::::23::0:
|
200
|
+
so.stdout.split(/\n/).map do |t|
|
201
|
+
if t.match(/^pub:/)
|
202
|
+
f = t.split(":")
|
203
|
+
f.slice(0, 6).join(":")
|
204
|
+
end
|
205
|
+
end.compact
|
206
|
+
end
|
207
|
+
|
190
208
|
# validate the key against the apt keystore to see if that version is expired
|
191
209
|
# @param [String] key
|
192
210
|
#
|
@@ -222,8 +240,8 @@ class Chef
|
|
222
240
|
def no_new_keys?(file)
|
223
241
|
# Now we are using the option --with-colons that works across old os versions
|
224
242
|
# as well as the latest (16.10). This for both `apt-key` and `gpg` commands
|
225
|
-
installed_keys =
|
226
|
-
proposed_keys =
|
243
|
+
installed_keys = extract_public_keys_from_cmd(*LIST_APT_KEY_FINGERPRINTS)
|
244
|
+
proposed_keys = extract_public_keys_from_cmd("gpg", "--with-fingerprint", "--with-colons", file)
|
227
245
|
(installed_keys & proposed_keys).sort == proposed_keys.sort
|
228
246
|
end
|
229
247
|
|