chef 18.1.0 → 18.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -3
  3. data/chef-universal-mingw-ucrt.gemspec +2 -2
  4. data/chef.gemspec +1 -1
  5. data/lib/chef/application/base.rb +18 -2
  6. data/lib/chef/client.rb +23 -6
  7. data/lib/chef/http/authenticator.rb +117 -34
  8. data/lib/chef/platform/query_helpers.rb +4 -2
  9. data/lib/chef/resource/apt_repository.rb +20 -2
  10. data/lib/chef/resource/bash.rb +13 -0
  11. data/lib/chef/resource/dsc_script.rb +1 -1
  12. data/lib/chef/resource/launchd.rb +2 -2
  13. data/lib/chef/resource/macos_userdefaults.rb +9 -5
  14. data/lib/chef/resource/rhsm_register.rb +1 -1
  15. data/lib/chef/resource/selinux_fcontext.rb +1 -1
  16. data/lib/chef/resource/selinux_login.rb +129 -0
  17. data/lib/chef/resource/selinux_permissive.rb +1 -1
  18. data/lib/chef/resource/selinux_port.rb +1 -1
  19. data/lib/chef/resource/selinux_state.rb +1 -1
  20. data/lib/chef/resource/selinux_user.rb +137 -0
  21. data/lib/chef/resource/service.rb +1 -1
  22. data/lib/chef/resource/user.rb +2 -2
  23. data/lib/chef/resource/windows_user_privilege.rb +14 -10
  24. data/lib/chef/resources.rb +2 -0
  25. data/lib/chef/version.rb +1 -1
  26. data/spec/data/trusted_certs/intermediate.pem +38 -27
  27. data/spec/data/trusted_certs/opscode.pem +33 -54
  28. data/spec/functional/resource/macos_userdefaults_spec.rb +4 -4
  29. data/spec/integration/client/client_spec.rb +22 -16
  30. data/spec/spec_helper.rb +3 -3
  31. data/spec/unit/client_spec.rb +26 -2
  32. data/spec/unit/compliance/runner_spec.rb +8 -0
  33. data/spec/unit/http/authenticator_spec.rb +64 -11
  34. data/spec/unit/provider/apt_repository_spec.rb +26 -5
  35. data/spec/unit/resource/macos_user_defaults_spec.rb +4 -4
  36. data/spec/unit/resource/selinux_login_spec.rb +73 -0
  37. data/spec/unit/resource/selinux_user_spec.rb +92 -0
  38. metadata +17 -16
  39. data/lib/chef/powershell.rb +0 -81
  40. data/spec/data/chef_guid +0 -1
  41. data/spec/data/nodes/mcwfhpowell.json +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2116a9a16e93030ed9aa85235426085567d3ef63e0c603bffa3a5a1614bb439e
4
- data.tar.gz: cc9dcbc47ddcb72e3fe920f9da13a788a607b66d628331b23a03ea8dbad53c30
3
+ metadata.gz: ae48a568217d00b159123bf621bdaee21412909ef553f400719f251559c8a4d4
4
+ data.tar.gz: 553c80d44e9babb553588b0d01beac0a413aec6b0d21fdbaaf754cc1b3fa0928
5
5
  SHA512:
6
- metadata.gz: d14c879526c8da207c572690f7e5c0a066c8ff89d33412053f6eaf3635ae2231d1f0c2c3478b1ebae30219ac0cbf86ee8c2381239eda50b937f853c2b7a77028
7
- data.tar.gz: 1bfb25d57117e4cc93655ab55573cebcffd9854402a59606f79e5a6e05bc8660d18c2ccc85d2a09c007789509dd23abee90a1a3d26a07bc6d93ba8fff12d0b58
6
+ metadata.gz: 3b2df041625cd266ae86c1a899c69493190f09018714cf281486291958268a669d65103555b63f0104345b5e281248d9bf7a2157a2de3632d92f84b4ed72c99c
7
+ data.tar.gz: 54b4391ec722faa770d54b7d5c6650285ab3dfdb933eed86938d579fb92b1501ad4bf3848e3f17950316eb5c8d9bc2bc99e044368246940321b10f3ddaaa1e97
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
@@ -15,9 +15,9 @@ gemspec.add_dependency "wmi-lite", "~> 1.0"
15
15
  gemspec.add_dependency "win32-taskscheduler", "~> 2.0"
16
16
  gemspec.add_dependency "iso8601", ">= 0.12.1", "< 0.14" # validate 0.14 when it comes out
17
17
  gemspec.add_dependency "win32-certstore", "~> 0.6.15" # 0.5+ required for specifying user vs. system store
18
- gemspec.add_dependency "chef-powershell", "~> 1.0.12" # The guts of the powershell_exec code have been moved to its own gem, chef-powershell. It's part of the chef-powershell-shim repo.
18
+ gemspec.add_dependency "chef-powershell", "~> 18.0.0" # The guts of the powershell_exec code have been moved to its own gem, chef-powershell. It's part of the chef-powershell-shim repo.
19
19
 
20
20
  gemspec.extensions << "ext/win32-eventlog/Rakefile"
21
21
  gemspec.files += Dir.glob("{distro,ext}/**/*")
22
22
 
23
- gemspec
23
+ gemspec
data/chef.gemspec CHANGED
@@ -61,7 +61,7 @@ Gem::Specification.new do |s|
61
61
  s.add_dependency "unf_ext", ">= 0.0.8.2" # This is ruby31 compatible ucrt gem version
62
62
  s.add_dependency "corefoundation", "~> 0.3.4" # macos_userdefaults resource
63
63
 
64
- s.add_dependency "proxifier", "~> 1.0"
64
+ s.add_dependency "proxifier2", "~> 1.1"
65
65
 
66
66
  s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
67
67
  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
- s3 = Aws::S3::Client.new
390
- object = s3.get_object(bucket: uri.hostname, key: uri.path[1..-1])
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)
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
- win32certstore = ::Win32::Certstore.open("MY")
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
- store = ::Win32::Certstore.open("MY")
823
- store.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
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 encrypt_pfx_pass
106
- self.class.encrypt_pfx_pass
105
+ def encrypt_pfx_pass_with_vector
106
+ self.class.encrypt_pfx_pass_with_vector
107
107
  end
108
108
 
109
- def decrypt_pfx_pass
110
- self.class.decrypt_pfx_pass
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" ? "CurrentUser" : "LocalMachine"
128
+ end
129
+
130
+ def self.get_registry_user
131
+ Chef::Config[:auth_key_registry_type] == "user" ? "HKEY_CURRENT_USER" : "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:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
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 = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
178
+ path = "#{store}\\Software\\Progress\\Authentication"
169
179
  # does the registry key even exist?
170
- present = @win32registry.get_values(path)
171
- if present.nil? || present.empty?
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
- present.each do |secret|
176
- if secret[:name] == "PfxPass"
177
- password = decrypt_pfx_pass(secret[:data])
178
- return password
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
- Chef::Log.warn "Authentication Hive and values not present in registry, creating them now"
187
- new_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
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
- require "securerandom" unless defined?(SecureRandom)
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.encrypt_pfx_pass(password)
210
+ def self.encrypt_pfx_pass_with_vector(password)
201
211
  powershell_code = <<~CODE
202
- $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force
203
- $secure_string = ConvertFrom-SecureString $encrypted_string
204
- return $secure_string
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.decrypt_pfx_pass(password)
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.retrieve_certificate_key(client_name)
219
- require "openssl" unless defined?(OpenSSL)
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:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
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:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
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
 
@@ -17,11 +17,14 @@
17
17
  #
18
18
 
19
19
  require "chef-utils" unless defined?(ChefUtils::CANARY)
20
+ require_relative "../mixin/powershell_exec"
20
21
 
21
22
  class Chef
22
23
  class Platform
23
24
 
24
25
  class << self
26
+ include Chef::Mixin::PowershellExec
27
+
25
28
  def windows?
26
29
  ChefUtils.windows?
27
30
  end
@@ -58,8 +61,7 @@ class Chef
58
61
  end
59
62
 
60
63
  def dsc_refresh_mode_disabled?(node)
61
- require_relative "../powershell"
62
- exec = Chef::PowerShell.new("Get-DscLocalConfigurationManager")
64
+ exec = powershell_exec!("Get-DscLocalConfigurationManager")
63
65
  exec.error!
64
66
  exec.result["RefreshMode"] == "Disabled"
65
67
  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 = extract_fingerprints_from_cmd(*LIST_APT_KEY_FINGERPRINTS)
226
- proposed_keys = extract_fingerprints_from_cmd("gpg", "--with-fingerprint", "--with-colons", file)
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
 
@@ -43,6 +43,19 @@ class Chef
43
43
  end
44
44
  ```
45
45
 
46
+ **Using escape characters in a string of code**
47
+
48
+ In the following example, the `find` command uses an escape character (`\`). Use a second escape character (`\\`) to preserve the escape character in the code string:
49
+
50
+ ```ruby
51
+ bash 'delete some archives ' do
52
+ code <<-EOH
53
+ find ./ -name "*.tar.Z" -mtime +180 -exec rm -f {} \\;
54
+ EOH
55
+ ignore_failure true
56
+ end
57
+ ```
58
+
46
59
  **Install a file from a remote location**
47
60
 
48
61
  The following is an example of how to install the foo123 module for Nginx. This module adds shell-style functionality to an Nginx configuration file and does the following:
@@ -34,7 +34,7 @@ class Chef
34
34
  resource in #{ChefUtils::Dist::Infra::PRODUCT}, such as the Archive resource, a custom DSC resource, an existing DSC script that performs an important
35
35
  task, and so on. Use the dsc_script resource to embed the code that defines a DSC configuration directly within a #{ChefUtils::Dist::Infra::PRODUCT} recipe.
36
36
 
37
- Warning: The **dsc_script** resource may not be used with the 32 bit Chef Infra client. It must be executed from a 64 bit Chef Infra client.
37
+ Warning: The **dsc_script** resource is only available on 64-bit Chef Infra Client.
38
38
  DESC
39
39
 
40
40
  default_action :run
@@ -21,7 +21,7 @@ require_relative "../resource"
21
21
  class Chef
22
22
  class Resource
23
23
  class Launchd < Chef::Resource
24
- provides :launchd
24
+ provides :launchd, os: "darwin"
25
25
 
26
26
  description "Use the **launchd** resource to manage system-wide services (daemons) and per-user services (agents) on the macOS platform."
27
27
  introduced "12.8"
@@ -129,7 +129,7 @@ class Chef
129
129
  property :abandon_process_group, [ TrueClass, FalseClass ],
130
130
  description: "If a job dies, all remaining processes with the same process ID may be kept running. Set to true to kill all remaining processes."
131
131
 
132
- property :associated_bundle_identifiers, Hash,
132
+ property :associated_bundle_identifiers, Array,
133
133
  description: "This optional key indicates which bundles the Login Items Added by Apps panel associates with the helper executable."
134
134
 
135
135
  property :debug, [ TrueClass, FalseClass ],
@@ -50,15 +50,17 @@ class Chef
50
50
  end
51
51
  ```
52
52
 
53
- **Specifying the type of a key to skip automatic type detection**
53
+ **Setting a value for specific user and hosts**
54
54
 
55
55
  ```ruby
56
- macos_userdefaults 'Finder expanded save dialogs' do
57
- key 'NSNavPanelExpandedStateForSaveMode'
58
- value 'TRUE'
59
- type 'bool'
56
+ macos_userdefaults 'Enable macOS firewall' do
57
+ key 'globalstate'
58
+ value 1
59
+ user 'jane'
60
+ host :current
60
61
  end
61
62
  ```
63
+
62
64
  DOC
63
65
 
64
66
  property :domain, String,
@@ -79,6 +81,7 @@ class Chef
79
81
 
80
82
  property :host, [String, Symbol],
81
83
  description: "Set either :current, :all or a hostname to set the user default at the host level.",
84
+ default: :all,
82
85
  desired_state: false,
83
86
  introduced: "16.3"
84
87
 
@@ -94,6 +97,7 @@ class Chef
94
97
 
95
98
  property :user, [String, Symbol],
96
99
  description: "The system user that the default will be applied to. Set :current for current user, :all for all users or pass a valid username",
100
+ default: :current,
97
101
  desired_state: false
98
102
 
99
103
  property :sudo, [TrueClass, FalseClass],
@@ -92,7 +92,7 @@ class Chef
92
92
 
93
93
  property :release,
94
94
  [Float, String],
95
- description: "Sets the operating system minor release to use for subscriptions for the system. Products and updates are limited to the specified minor release version. This is used with the `auto_attach` option, it may also be used with activation keys. For example, `release '6.4'` will append `--release=6.4` to the register command.",
95
+ description: "Sets the operating system minor release to use for subscriptions for the system. Products and updates are limited to the specified minor release version. This is used with the `auto_attach` or `activation_key` options. For example, `release '6.4'` will append `--release=6.4` to the register command.",
96
96
  introduced: "17.8"
97
97
 
98
98
  action :register, description: "Register the node with RHSM." do
@@ -22,7 +22,7 @@ class Chef
22
22
 
23
23
  provides :selinux_fcontext
24
24
 
25
- description "Use **selinux_fcontext** resource to set the SELinux context of files with semanage fcontext."
25
+ description "Use the **selinux_fcontext** resource to set the SELinux context of files using the `semanage fcontext` command."
26
26
  introduced "18.0"
27
27
  examples <<~DOC
28
28
  **Allow http servers (e.g. nginx/apache) to modify moodle files**:
@@ -0,0 +1,129 @@
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require_relative "../resource"
15
+ require_relative "selinux/common_helpers"
16
+
17
+ class Chef
18
+ class Resource
19
+ class SelinuxLogin < Chef::Resource
20
+ unified_mode true
21
+
22
+ provides :selinux_login
23
+
24
+ description "Use the **selinux_login** resource to add, update, or remove SELinux user to OS login mappings."
25
+ introduced "18.1"
26
+ examples <<~DOC
27
+ **Manage test OS user mapping with a range of s0 and associated SELinux user test_u**:
28
+
29
+ ```ruby
30
+ selinux_login 'test' do
31
+ user 'test_u'
32
+ range 's0'
33
+ end
34
+ ```
35
+ DOC
36
+
37
+ property :login, String,
38
+ name_property: true,
39
+ description: "An optional property to set the OS user login value if it differs from the resource block's name."
40
+
41
+ property :user, String,
42
+ description: "SELinux user to be mapped."
43
+
44
+ property :range, String,
45
+ description: "MLS/MCS security range for the SELinux user."
46
+
47
+ load_current_value do |new_resource|
48
+ logins = shell_out!("semanage login -l").stdout.split("\n")
49
+
50
+ current_login = logins.grep(/^#{Regexp.escape(new_resource.login)}\s+/) do |l|
51
+ l.match(/^(?<login>[^\s]+)\s+(?<user>[^\s]+)\s+(?<range>[^\s]+)/)
52
+ # match returns [<Match 'data'>] or [], shift converts that to <Match 'data'> or nil
53
+ end.shift
54
+
55
+ current_value_does_not_exist! unless current_login
56
+
57
+ # Existing resources should maintain their current configuration unless otherwise specified
58
+ new_resource.user ||= current_login[:user]
59
+ new_resource.range ||= current_login[:range]
60
+
61
+ user current_login[:user]
62
+ range current_login[:range]
63
+ end
64
+
65
+ action_class do
66
+ include Chef::SELinux::CommonHelpers
67
+
68
+ def semanage_login_args
69
+ # Generate arguments for semanage login -a or -m
70
+ args = ""
71
+
72
+ args += " -s #{new_resource.user}" if new_resource.user
73
+ args += " -r #{new_resource.range}" if new_resource.range
74
+
75
+ args
76
+ end
77
+ end
78
+
79
+ action :manage, description: "Sets the SELinux login mapping to the desired settings regardless of previous state." do
80
+ run_action(:add)
81
+ run_action(:modify)
82
+ end
83
+
84
+ # Create if doesn't exist, do not touch if user already exists
85
+ action :add, description: "Creates the SELinux login mapping if not previously created." do
86
+ raise "The user property must be populated to create a new SELinux login" if new_resource.user.to_s.empty?
87
+
88
+ if selinux_disabled?
89
+ Chef::Log.warn("Unable to add SELinux login #{new_resource.login} as SELinux is disabled")
90
+ return
91
+ end
92
+
93
+ unless current_resource
94
+ converge_if_changed do
95
+ shell_out!("semanage login -a#{semanage_login_args} #{new_resource.login}")
96
+ end
97
+ end
98
+ end
99
+
100
+ # Only modify port if it exists & doesn't have the correct context already
101
+ action :modify, description: "Updates the SELinux login mapping if previously created." do
102
+ if selinux_disabled?
103
+ Chef::Log.warn("Unable to modify SELinux login #{new_resource.login} as SELinux is disabled")
104
+ return
105
+ end
106
+
107
+ if current_resource
108
+ converge_if_changed do
109
+ shell_out!("semanage login -m#{semanage_login_args} #{new_resource.login}")
110
+ end
111
+ end
112
+ end
113
+
114
+ # Delete if exists
115
+ action :delete, description: "Removes the SELinux login mapping if previously created." do
116
+ if selinux_disabled?
117
+ Chef::Log.warn("Unable to delete SELinux login #{new_resource.login} as SELinux is disabled")
118
+ return
119
+ end
120
+
121
+ if current_resource
122
+ converge_by "deleting SELinux login #{new_resource.login}" do
123
+ shell_out!("semanage login -d #{new_resource.login}")
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end