puppet 7.15.0-x86-mingw32 → 7.18.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +75 -14
  3. data/ext/systemd/puppet.service +1 -1
  4. data/lib/puppet/agent.rb +47 -11
  5. data/lib/puppet/application/agent.rb +3 -13
  6. data/lib/puppet/application/apply.rb +2 -2
  7. data/lib/puppet/configurer.rb +1 -1
  8. data/lib/puppet/defaults.rb +11 -1
  9. data/lib/puppet/http/client.rb +22 -2
  10. data/lib/puppet/info_service/task_information_service.rb +1 -1
  11. data/lib/puppet/module/task.rb +5 -1
  12. data/lib/puppet/parameter.rb +19 -4
  13. data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
  14. data/lib/puppet/pops/functions/dispatcher.rb +10 -6
  15. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
  16. data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
  17. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  18. data/lib/puppet/provider/package/yum.rb +8 -3
  19. data/lib/puppet/provider/user/directoryservice.rb +15 -8
  20. data/lib/puppet/ssl/ssl_provider.rb +75 -19
  21. data/lib/puppet/ssl/state_machine.rb +13 -17
  22. data/lib/puppet/transaction.rb +22 -0
  23. data/lib/puppet/type/user.rb +3 -0
  24. data/lib/puppet/type.rb +20 -3
  25. data/lib/puppet/version.rb +1 -1
  26. data/lib/puppet.rb +1 -14
  27. data/man/man5/puppet.conf.5 +11 -3
  28. data/man/man8/puppet-agent.8 +2 -2
  29. data/man/man8/puppet-apply.8 +1 -1
  30. data/man/man8/puppet-catalog.8 +1 -1
  31. data/man/man8/puppet-config.8 +1 -1
  32. data/man/man8/puppet-describe.8 +1 -1
  33. data/man/man8/puppet-device.8 +1 -1
  34. data/man/man8/puppet-doc.8 +1 -1
  35. data/man/man8/puppet-epp.8 +1 -1
  36. data/man/man8/puppet-facts.8 +1 -1
  37. data/man/man8/puppet-filebucket.8 +1 -1
  38. data/man/man8/puppet-generate.8 +1 -1
  39. data/man/man8/puppet-help.8 +1 -1
  40. data/man/man8/puppet-lookup.8 +1 -1
  41. data/man/man8/puppet-module.8 +1 -1
  42. data/man/man8/puppet-node.8 +1 -1
  43. data/man/man8/puppet-parser.8 +1 -1
  44. data/man/man8/puppet-plugin.8 +1 -1
  45. data/man/man8/puppet-report.8 +1 -1
  46. data/man/man8/puppet-resource.8 +1 -1
  47. data/man/man8/puppet-script.8 +1 -1
  48. data/man/man8/puppet-ssl.8 +1 -1
  49. data/man/man8/puppet.8 +2 -2
  50. data/spec/integration/application/agent_spec.rb +157 -0
  51. data/spec/integration/application/apply_spec.rb +74 -0
  52. data/spec/integration/http/client_spec.rb +51 -4
  53. data/spec/lib/puppet_spec/https.rb +1 -1
  54. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  55. data/spec/unit/agent_spec.rb +28 -2
  56. data/spec/unit/application/agent_spec.rb +26 -16
  57. data/spec/unit/daemon_spec.rb +2 -11
  58. data/spec/unit/http/client_spec.rb +18 -0
  59. data/spec/unit/info_service_spec.rb +11 -3
  60. data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
  61. data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
  62. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
  63. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  64. data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
  65. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  66. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  67. data/spec/unit/task_spec.rb +56 -13
  68. data/tasks/generate_cert_fixtures.rake +5 -4
  69. metadata +2 -2
@@ -18,7 +18,7 @@ class Puppet::Pops::Loader::RubyLegacyFunctionInstantiator
18
18
  def self.create(loader, typed_name, source_ref, ruby_code_string)
19
19
  # Assert content of 3x function by parsing
20
20
  assertion_result = []
21
- if assert_code(ruby_code_string, assertion_result)
21
+ if assert_code(ruby_code_string, source_ref, assertion_result)
22
22
  unless ruby_code_string.is_a?(String) && assertion_result.include?(:found_newfunction)
23
23
  raise ArgumentError, _("The code loaded from %{source_ref} does not seem to be a Puppet 3x API function - no 'newfunction' call.") % { source_ref: source_ref }
24
24
  end
@@ -69,15 +69,15 @@ class Puppet::Pops::Loader::RubyLegacyFunctionInstantiator
69
69
  end
70
70
  private_class_method :get_binding
71
71
 
72
- def self.assert_code(code_string, result)
72
+ def self.assert_code(code_string, source_ref, result)
73
73
  ripped = Ripper.sexp(code_string)
74
74
  return false if ripped.nil? # Let the next real parse crash and tell where and what is wrong
75
- ripped.each {|x| walk(x, result) }
75
+ ripped.each {|x| walk(x, source_ref, result) }
76
76
  true
77
77
  end
78
78
  private_class_method :assert_code
79
79
 
80
- def self.walk(x, result)
80
+ def self.walk(x, source_ref, result)
81
81
  return unless x.is_a?(Array)
82
82
  first = x[0]
83
83
  case first
@@ -89,13 +89,14 @@ class Puppet::Pops::Loader::RubyLegacyFunctionInstantiator
89
89
  when :def, :defs
90
90
  # There should not be any calls to def in a 3x function
91
91
  mname, mline = extract_name_line(find_identity(x))
92
- raise SecurityError, _("Illegal method definition of method '%{method_name}' on line %{line} in legacy function. See %{url} for more information") % {
92
+ raise SecurityError, _("Illegal method definition of method '%{method_name}' in source %{source_ref} on line %{line} in legacy function. See %{url} for more information") % {
93
93
  method_name: mname,
94
+ source_ref: source_ref,
94
95
  line: mline,
95
96
  url: "https://puppet.com/docs/puppet/latest/functions_refactor_legacy.html"
96
97
  }
97
98
  end
98
- x.each {|v| walk(v, result) }
99
+ x.each {|v| walk(v, source_ref, result) }
99
100
  end
100
101
  private_class_method :walk
101
102
 
@@ -581,6 +581,15 @@ module Types
581
581
  end
582
582
  end
583
583
 
584
+ def get_deferred_function_return_type(value)
585
+ func = Puppet.lookup(:loaders).private_environment_loader.
586
+ load(:function, value.name)
587
+ dispatcher = func.class.dispatcher.find_matching_dispatcher(value.arguments)
588
+ raise ArgumentError, "No matching arity found for #{func.class.name} with arguments #{value.arguments}" unless dispatcher
589
+ dispatcher.type.return_type
590
+ end
591
+ private :get_deferred_function_return_type
592
+
584
593
  # Validates that all entries in the _param_hash_ exists in the given param_struct, that their type conforms
585
594
  # with the corresponding param_struct element and that all required values are provided.
586
595
  # An error message is created for each problem found.
@@ -598,7 +607,19 @@ module Types
598
607
  value = param_hash[name]
599
608
  value_type = elem.value_type
600
609
  if param_hash.include?(name)
601
- result << describe(value_type, TypeCalculator.singleton.infer_set(value), [ParameterPathElement.new(name)]) unless value_type.instance?(value)
610
+ if Puppet::Pops::Types::TypeFactory.deferred.implementation_class == value.class
611
+ if (df_return_type = get_deferred_function_return_type(value))
612
+ result << describe(value_type, df_return_type, [ParameterPathElement.new(name)]) unless value_type.generalize.assignable?(df_return_type.generalize)
613
+ else
614
+ warning_text = _("Deferred function %{function_name} has no return_type, unable to guarantee value type during compilation.") %
615
+ {function_name: value.name }
616
+ Puppet.warn_once('deprecations',
617
+ "#{value.name}_deferred_warning",
618
+ warning_text)
619
+ end
620
+ else
621
+ result << describe(value_type, TypeCalculator.singleton.infer_set(value), [ParameterPathElement.new(name)]) unless value_type.instance?(value)
622
+ end
602
623
  else
603
624
  result << MissingParameter.new(nil, name) unless missing_ok || elem.key_type.is_a?(POptionalType)
604
625
  end
@@ -53,7 +53,7 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
53
53
  end
54
54
 
55
55
  if options[:local]
56
- list = execute_rubygems_list_command(gem_regex)
56
+ list = execute_rubygems_list_command(command_options)
57
57
  else
58
58
  begin
59
59
  list = puppetservercmd(command_options)
@@ -137,7 +137,7 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
137
137
  # for example: json (1.8.3 java)
138
138
  # but java platform gems should not be managed by this (or any) provider.
139
139
 
140
- def self.execute_rubygems_list_command(gem_regex)
140
+ def self.execute_rubygems_list_command(command_options)
141
141
  puppetserver_default_gem_home = '/opt/puppetlabs/server/data/puppetserver/jruby-gems'
142
142
  puppetserver_default_vendored_jruby_gems = '/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems'
143
143
  puppet_default_vendor_gems = '/opt/puppetlabs/puppet/lib/ruby/vendor_gems'
@@ -157,24 +157,15 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
157
157
  gem_env['GEM_PATH'] = puppetserver_conf['jruby-puppet'].key?('gem-path') ? puppetserver_conf['jruby-puppet']['gem-path'].join(':') : puppetserver_default_gem_path
158
158
  end
159
159
  gem_env['GEM_SPEC_CACHE'] = "/tmp/#{$$}"
160
- Gem.paths = gem_env
161
-
162
- sio_inn = StringIO.new
163
- sio_out = StringIO.new
164
- sio_err = StringIO.new
165
- stream_ui = Gem::StreamUI.new(sio_inn, sio_out, sio_err, false)
166
- gem_list_cmd = Gem::Commands::ListCommand.new
167
- gem_list_cmd.options[:domain] = :local
168
- gem_list_cmd.options[:args] = [gem_regex] if gem_regex
169
- gem_list_cmd.ui = stream_ui
170
- gem_list_cmd.execute
160
+
161
+ # Remove the 'gem' from the command_options
162
+ command_options.shift
163
+ gem_out = execute_gem_command(Puppet::Type::Package::ProviderPuppet_gem.provider_command, command_options, gem_env)
171
164
 
172
165
  # There is no method exclude default gems from the local gem list,
173
166
  # for example: psych (default: 2.2.2)
174
167
  # but default gems should not be managed by this (or any) provider.
175
- gem_list = sio_out.string.lines.reject { |gem| gem =~ / \(default\: / }
168
+ gem_list = gem_out.lines.reject { |gem| gem =~ / \(default\: / }
176
169
  gem_list.join("\n")
177
- ensure
178
- Gem.clear_paths
179
170
  end
180
171
  end
@@ -204,7 +204,7 @@ defaultfor :osfamily => :redhat, :operatingsystemmajrelease => (4..7).to_a
204
204
  return should
205
205
  end
206
206
  versions = []
207
- available_versions(@resource[:name]).each do |version|
207
+ available_versions(@resource[:name], disablerepo, enablerepo, disableexcludes).each do |version|
208
208
  begin
209
209
  rpm_version = RPM_VERSION.parse(version)
210
210
  versions << rpm_version if should_range.include?(rpm_version)
@@ -225,8 +225,13 @@ defaultfor :osfamily => :redhat, :operatingsystemmajrelease => (4..7).to_a
225
225
  end
226
226
  end
227
227
 
228
- def available_versions(package_name)
229
- output = execute("yum list #{package_name} --showduplicates | sed -e '1,/Available Packages/ d' | awk '{print $2}'")
228
+ def available_versions(package_name, disablerepo, enablerepo, disableexcludes)
229
+ args = [command(:cmd), 'list', package_name, '--showduplicates']
230
+ args.concat(disablerepo.map { |repo| ["--disablerepo=#{repo}"] }.flatten)
231
+ args.concat(enablerepo.map { |repo| ["--enablerepo=#{repo}"] }.flatten)
232
+ args.concat(disableexcludes.map { |repo| ["--disableexcludes=#{repo}"] }.flatten)
233
+
234
+ output = execute("#{args.compact.join(' ')} | sed -e '1,/Available Packages/ d' | awk '{print $2}'")
230
235
  output.split("\n")
231
236
  end
232
237
 
@@ -147,9 +147,9 @@ Puppet::Type.type(:user).provide :directoryservice do
147
147
  else
148
148
  embedded_binary_plist = get_embedded_binary_plist(attribute_hash[:shadowhashdata])
149
149
  if embedded_binary_plist['SALTED-SHA512-PBKDF2']
150
- attribute_hash[:password] = get_salted_sha512_pbkdf2('entropy', embedded_binary_plist)
151
- attribute_hash[:salt] = get_salted_sha512_pbkdf2('salt', embedded_binary_plist)
152
- attribute_hash[:iterations] = get_salted_sha512_pbkdf2('iterations', embedded_binary_plist)
150
+ attribute_hash[:password] = get_salted_sha512_pbkdf2('entropy', embedded_binary_plist, attribute_hash[:name])
151
+ attribute_hash[:salt] = get_salted_sha512_pbkdf2('salt', embedded_binary_plist, attribute_hash[:name])
152
+ attribute_hash[:iterations] = get_salted_sha512_pbkdf2('iterations', embedded_binary_plist, attribute_hash[:name])
153
153
  elsif embedded_binary_plist['SALTED-SHA512']
154
154
  attribute_hash[:password] = get_salted_sha512(embedded_binary_plist)
155
155
  end
@@ -205,16 +205,18 @@ Puppet::Type.type(:user).provide :directoryservice do
205
205
  # according to which field is passed. Arguments passed are the hash
206
206
  # containing the value read from the 'ShadowHashData' key in the User's
207
207
  # plist, and the field to be read (one of 'entropy', 'salt', or 'iterations')
208
- def self.get_salted_sha512_pbkdf2(field, embedded_binary_plist)
208
+ def self.get_salted_sha512_pbkdf2(field, embedded_binary_plist, user_name = "")
209
209
  case field
210
210
  when 'salt', 'entropy'
211
- embedded_binary_plist['SALTED-SHA512-PBKDF2'][field].unpack('H*').first
211
+ value = embedded_binary_plist['SALTED-SHA512-PBKDF2'][field]
212
+ if value == nil
213
+ raise Puppet::Error, "Invalid #{field} given for user #{user_name}"
214
+ end
215
+ value.unpack('H*').first
212
216
  when 'iterations'
213
217
  Integer(embedded_binary_plist['SALTED-SHA512-PBKDF2'][field])
214
218
  else
215
- raise Puppet::Error, 'Puppet has tried to read an incorrect value from the ' +
216
- "SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', " +
217
- "'entropy', or 'iterations'."
219
+ raise Puppet::Error, "Puppet has tried to read an incorrect value from the user #{user_name} in the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'."
218
220
  end
219
221
  end
220
222
 
@@ -401,6 +403,11 @@ Puppet::Type.type(:user).provide :directoryservice do
401
403
  # we have to treat the ds cache just like you would in the password=
402
404
  # method.
403
405
  def salt=(value)
406
+ if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.15') >= 0)
407
+ if value.length != 64
408
+ self.fail "macOS versions 10.15 and higher require the salt to be 32-bytes. Since Puppet's user resource requires the value to be hex encoded, the length of the salt's string must be 64. Please check your salt and try again."
409
+ end
410
+ end
404
411
  if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.7') > 0)
405
412
  assert_full_pbkdf2_password
406
413
 
@@ -59,17 +59,19 @@ class Puppet::SSL::SSLProvider
59
59
  # refers to the cacerts bundle in the puppet-agent package.
60
60
  #
61
61
  # Connections made from the returned context will authenticate the server,
62
- # i.e. `VERIFY_PEER`, but will not use a client certificate and will not
63
- # perform revocation checking.
62
+ # i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)
63
+ # and will not perform revocation checking.
64
64
  #
65
65
  # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs
66
66
  # @param path [String, nil] A file containing additional trusted CA certs.
67
+ # @param include_client_cert [true, false] If true, the client cert will be added to the context
68
+ # allowing mutual TLS authentication. The default is false. If the client cert doesn't exist
69
+ # then the option will be ignored.
67
70
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
68
71
  # @raise (see #create_context)
69
72
  # @api private
70
- def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
71
- store = create_x509_store(cacerts, [], false)
72
- store.set_default_paths
73
+ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store], include_client_cert: false)
74
+ store = create_x509_store(cacerts, [], false, include_system_store: true)
73
75
 
74
76
  if path
75
77
  stat = Puppet::FileSystem.stat(path)
@@ -89,6 +91,29 @@ class Puppet::SSL::SSLProvider
89
91
  end
90
92
  end
91
93
 
94
+ if include_client_cert
95
+ cert_provider = Puppet::X509::CertProvider.new
96
+ private_key = cert_provider.load_private_key(Puppet[:certname], required: false)
97
+ unless private_key
98
+ Puppet.warning("Private key for '#{Puppet[:certname]}' does not exist")
99
+ end
100
+
101
+ client_cert = cert_provider.load_client_cert(Puppet[:certname], required: false)
102
+ unless client_cert
103
+ Puppet.warning("Client certificate for '#{Puppet[:certname]}' does not exist")
104
+ end
105
+
106
+ if private_key && client_cert
107
+ client_chain = resolve_client_chain(store, client_cert, private_key)
108
+
109
+ return Puppet::SSL::SSLContext.new(
110
+ store: store, cacerts: cacerts, crls: [],
111
+ private_key: private_key, client_cert: client_cert, client_chain: client_chain,
112
+ revocation: false
113
+ ).freeze
114
+ end
115
+ end
116
+
92
117
  Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: [], revocation: false).freeze
93
118
  end
94
119
 
@@ -111,28 +136,21 @@ class Puppet::SSL::SSLProvider
111
136
  # @param client_cert [OpenSSL::X509::Certificate] client's cert whose public
112
137
  # key matches the `private_key`
113
138
  # @param revocation [:chain, :leaf, false] revocation mode
139
+ # @param include_system_store [true, false] Also trust system CA
114
140
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
115
141
  # @raise [Puppet::SSL::CertVerifyError] There was an issue with
116
142
  # one of the certs or CRLs.
117
143
  # @raise [Puppet::SSL::SSLError] There was an issue with the
118
144
  # `private_key`.
119
145
  # @api private
120
- def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation])
146
+ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation], include_system_store: false)
121
147
  raise ArgumentError, _("CA certs are missing") unless cacerts
122
148
  raise ArgumentError, _("CRLs are missing") unless crls
123
149
  raise ArgumentError, _("Private key is missing") unless private_key
124
150
  raise ArgumentError, _("Client cert is missing") unless client_cert
125
151
 
126
- store = create_x509_store(cacerts, crls, revocation)
127
- client_chain = verify_cert_with_store(store, client_cert)
128
-
129
- if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
130
- raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
131
- end
132
-
133
- unless client_cert.check_private_key(private_key)
134
- raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
135
- end
152
+ store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
153
+ client_chain = resolve_client_chain(store, client_cert, private_key)
136
154
 
137
155
  Puppet::SSL::SSLContext.new(
138
156
  store: store, cacerts: cacerts, crls: crls,
@@ -151,12 +169,13 @@ class Puppet::SSL::SSLProvider
151
169
  # @param password [String, nil] If the private key is encrypted, decrypt
152
170
  # it using the password. If the key is encrypted, but a password is
153
171
  # not specified, then the key cannot be loaded.
172
+ # @param include_system_store [true, false] Also trust system CA
154
173
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
155
174
  # @raise [Puppet::SSL::CertVerifyError] There was an issue with
156
175
  # one of the certs or CRLs.
157
176
  # @raise [Puppet::Error] There was an issue with one of the required components.
158
177
  # @api private
159
- def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil)
178
+ def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil, include_system_store: false)
160
179
  cert = Puppet::X509::CertProvider.new
161
180
  cacerts = cert.load_cacerts(required: true)
162
181
  crls = case revocation
@@ -168,7 +187,7 @@ class Puppet::SSL::SSLProvider
168
187
  private_key = cert.load_private_key(certname, required: true, password: password)
169
188
  client_cert = cert.load_client_cert(certname, required: true)
170
189
 
171
- create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation)
190
+ create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation, include_system_store: include_system_store)
172
191
  rescue OpenSSL::PKey::PKeyError => e
173
192
  raise Puppet::SSL::SSLError.new(_("Failed to load private key for host '%{name}': %{message}") % { name: certname, message: e.message }, e)
174
193
  end
@@ -190,6 +209,27 @@ class Puppet::SSL::SSLProvider
190
209
  csr
191
210
  end
192
211
 
212
+ def print(ssl_context, alg = 'SHA256')
213
+ if Puppet::Util::Log.sendlevel?(:debug)
214
+ chain = ssl_context.client_chain
215
+ # print from root to client
216
+ chain.reverse.each_with_index do |cert, i|
217
+ digest = Puppet::SSL::Digest.new(alg, cert.to_der)
218
+ if i == chain.length - 1
219
+ Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
220
+ else
221
+ Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
222
+ end
223
+ end
224
+ ssl_context.crls.each do |crl|
225
+ oid_values = Hash[crl.extensions.map { |ext| [ext.oid, ext.value] }]
226
+ crlNumber = oid_values['crlNumber'] || 'unknown'
227
+ authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp!
228
+ Puppet.debug("Using CRL '#{crl.issuer.to_utf8}' authorityKeyIdentifier '#{authKeyId}' crlNumber '#{crlNumber }'")
229
+ end
230
+ end
231
+ end
232
+
193
233
  private
194
234
 
195
235
  def default_flags
@@ -203,7 +243,7 @@ class Puppet::SSL::SSLProvider
203
243
  end
204
244
  end
205
245
 
206
- def create_x509_store(roots, crls, revocation)
246
+ def create_x509_store(roots, crls, revocation, include_system_store: false)
207
247
  store = OpenSSL::X509::Store.new
208
248
  store.purpose = OpenSSL::X509::PURPOSE_ANY
209
249
  store.flags = default_flags | revocation_mode(revocation)
@@ -211,6 +251,8 @@ class Puppet::SSL::SSLProvider
211
251
  roots.each { |cert| store.add_cert(cert) }
212
252
  crls.each { |crl| store.add_crl(crl) }
213
253
 
254
+ store.set_default_paths if include_system_store
255
+
214
256
  store
215
257
  end
216
258
 
@@ -234,6 +276,20 @@ class Puppet::SSL::SSLProvider
234
276
  end
235
277
  end
236
278
 
279
+ def resolve_client_chain(store, client_cert, private_key)
280
+ client_chain = verify_cert_with_store(store, client_cert)
281
+
282
+ if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
283
+ raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
284
+ end
285
+
286
+ unless client_cert.check_private_key(private_key)
287
+ raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
288
+ end
289
+
290
+ client_chain
291
+ end
292
+
237
293
  def verify_cert_with_store(store, cert)
238
294
  # StoreContext#initialize accepts a chain argument, but it's set to [] because
239
295
  # puppet requires any intermediate CA certs needed to complete the client's
@@ -27,6 +27,15 @@ class Puppet::SSL::StateMachine
27
27
  detail.set_backtrace(cause.backtrace)
28
28
  Error.new(@machine, message, detail)
29
29
  end
30
+
31
+ def log_error(message)
32
+ # When running daemonized we set stdout to /dev/null, so write to the log instead
33
+ if Puppet[:daemonize]
34
+ Puppet.err(message)
35
+ else
36
+ $stdout.puts(message)
37
+ end
38
+ end
30
39
  end
31
40
 
32
41
  # Load existing CA certs or download them. Transition to NeedCRLs.
@@ -270,15 +279,15 @@ class Puppet::SSL::StateMachine
270
279
  def next_state
271
280
  time = @machine.waitforcert
272
281
  if time < 1
273
- puts _("Exiting now because the waitforcert setting is set to 0.")
282
+ log_error(_("Exiting now because the waitforcert setting is set to 0."))
274
283
  exit(1)
275
284
  elsif Time.now.to_i > @machine.wait_deadline
276
- puts _("Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.") % {name: Puppet[:certname] }
285
+ log_error(_("Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.") % {name: Puppet[:certname] })
277
286
  exit(1)
278
287
  else
279
288
  Puppet.info(_("Will try again in %{time} seconds.") % {time: time})
280
289
 
281
- # close persistent connections and session state before sleeping
290
+ # close http/tls and session state before sleeping
282
291
  Puppet.runtime[:http].close
283
292
  @machine.session = Puppet.runtime[:http].create_session
284
293
 
@@ -419,20 +428,7 @@ class Puppet::SSL::StateMachine
419
428
  def ensure_client_certificate
420
429
  final_state = run_machine(NeedLock.new(self), Done)
421
430
  ssl_context = final_state.ssl_context
422
-
423
- if Puppet::Util::Log.sendlevel?(:debug)
424
- chain = ssl_context.client_chain
425
- # print from root to client
426
- chain.reverse.each_with_index do |cert, i|
427
- digest = Puppet::SSL::Digest.new(@digest, cert.to_der)
428
- if i == chain.length - 1
429
- Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
430
- else
431
- Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
432
- end
433
- end
434
- end
435
-
431
+ @ssl_provider.print(ssl_context, @digest)
436
432
  ssl_context
437
433
  end
438
434
 
@@ -276,6 +276,7 @@ class Puppet::Transaction
276
276
 
277
277
  # Evaluate a single resource.
278
278
  def eval_resource(resource, ancestor = nil)
279
+ resolve_resource(resource)
279
280
  propagate_failure(resource)
280
281
  if skip?(resource)
281
282
  resource_status(resource).skipped = true
@@ -464,6 +465,27 @@ class Puppet::Transaction
464
465
  public :skip?
465
466
  public :missing_tags?
466
467
 
468
+ def resolve_resource(resource)
469
+ return unless catalog.host_config?
470
+
471
+ deferred_validate = false
472
+
473
+ resource.eachparameter do |param|
474
+ if param.value.instance_of?(Puppet::Pops::Evaluator::DeferredValue)
475
+ # Puppet::Parameter#value= triggers validation and munging. Puppet::Property#value=
476
+ # overrides the method, but also triggers validation and munging, since we're
477
+ # setting the desired/should value.
478
+ resolved = param.value.resolve
479
+ # resource.notice("Resolved deferred value to #{resolved}")
480
+ param.value = resolved
481
+ deferred_validate = true
482
+ end
483
+ end
484
+
485
+ if deferred_validate
486
+ resource.validate_resource
487
+ end
488
+ end
467
489
  end
468
490
 
469
491
  require_relative 'transaction/report'
@@ -227,6 +227,9 @@ module Puppet
227
227
  * OS X 10.8 and higher use salted SHA512 PBKDF2 hashes. When managing passwords
228
228
  on these systems, the `salt` and `iterations` attributes need to be specified as
229
229
  well as the password.
230
+ * macOS 10.15 and higher require the salt to be 32-bytes. Since Puppet's user
231
+ resource requires the value to be hex encoded, the length of the salt's
232
+ string must be 64.
230
233
  * Windows passwords can be managed only in cleartext, because there is no Windows
231
234
  API for setting the password hash.
232
235
 
data/lib/puppet/type.rb CHANGED
@@ -2282,7 +2282,13 @@ class Type
2282
2282
  # @api public
2283
2283
  #
2284
2284
  def self.validate(&block)
2285
- define_method(:validate, &block)
2285
+ define_method(:unsafe_validate, &block)
2286
+
2287
+ define_method(:validate) do
2288
+ return if enum_for(:eachparameter).any? { |p| p.value.instance_of?(Puppet::Pops::Evaluator::DeferredValue) }
2289
+
2290
+ unsafe_validate
2291
+ end
2286
2292
  end
2287
2293
 
2288
2294
  # @return [String] The file from which this type originates from
@@ -2372,6 +2378,19 @@ class Type
2372
2378
 
2373
2379
  set_parameters(@original_parameters)
2374
2380
 
2381
+ validate_resource
2382
+
2383
+ set_sensitive_parameters(resource.sensitive_parameters)
2384
+ end
2385
+
2386
+ # Optionally validate the resource. This method is a noop if the type has not defined
2387
+ # a `validate` method using the puppet DSL. If validation fails, then an exception will
2388
+ # be raised with this resources as the context.
2389
+ #
2390
+ # @api public
2391
+ #
2392
+ # @return [void]
2393
+ def validate_resource
2375
2394
  begin
2376
2395
  self.validate if self.respond_to?(:validate)
2377
2396
  rescue Puppet::Error, ArgumentError => detail
@@ -2379,8 +2398,6 @@ class Type
2379
2398
  adderrorcontext(error, detail)
2380
2399
  raise error
2381
2400
  end
2382
-
2383
- set_sensitive_parameters(resource.sensitive_parameters)
2384
2401
  end
2385
2402
 
2386
2403
  protected
@@ -6,7 +6,7 @@
6
6
  # Raketasks and such to set the version based on the output of `git describe`
7
7
 
8
8
  module Puppet
9
- PUPPETVERSION = '7.15.0'
9
+ PUPPETVERSION = '7.18.0'
10
10
 
11
11
  ##
12
12
  # version is a public API method intended to always provide a fast and
data/lib/puppet.rb CHANGED
@@ -235,20 +235,7 @@ module Puppet
235
235
 
236
236
  {
237
237
  :environments => Puppet::Environments::Cached.new(Puppet::Environments::Combined.new(*loaders)),
238
- :ssl_context => proc {
239
- begin
240
- cert = Puppet::X509::CertProvider.new
241
- password = cert.load_private_key_password
242
- ssl = Puppet::SSL::SSLProvider.new
243
- ssl.load_context(certname: Puppet[:certname], password: password)
244
- rescue => e
245
- # TRANSLATORS: `message` is an already translated string of why SSL failed to initialize
246
- Puppet.log_exception(e, _("Failed to initialize SSL: %{message}") % { message: e.message })
247
- # TRANSLATORS: `puppet agent -t` is a command and should not be translated
248
- Puppet.err(_("Run `puppet agent -t`"))
249
- raise e
250
- end
251
- },
238
+ :ssl_context => proc { Puppet.runtime[:http].default_ssl_context },
252
239
  :http_session => proc { Puppet.runtime[:http].create_session },
253
240
  :plugins => proc { Puppet::Plugins::Configuration.load_plugins },
254
241
  :rich_data => false
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPETCONF" "5" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPETCONF" "5" "July 2022" "Puppet, Inc." "Puppet manual"
5
5
  \fBThis page is autogenerated; any changes will get overwritten\fR
6
6
  .
7
7
  .SH "Configuration settings"
@@ -929,7 +929,7 @@ The time to wait for data to be read from an HTTP connection\. If nothing is rea
929
929
  The HTTP User\-Agent string to send when making network requests\.
930
930
  .
931
931
  .IP "\(bu" 4
932
- \fIDefault\fR: \fBPuppet/7\.15\.0 Ruby/2\.7\.5\-p203 (x86_64\-linux)\fR
932
+ \fIDefault\fR: \fBPuppet/7\.18\.0 Ruby/2\.7\.5\-p203 (x86_64\-linux)\fR
933
933
  .
934
934
  .IP "" 0
935
935
  .
@@ -1486,6 +1486,14 @@ The preferred means of serializing ruby instances for passing over the wire\. Th
1486
1486
  .
1487
1487
  .IP "" 0
1488
1488
  .
1489
+ .SS "preprocess_deferred"
1490
+ Whether puppet should call deferred functions before applying the catalog\. If set to \fBtrue\fR, then all prerequisites needed for the deferred function must be satified prior to puppet running\. If set to \fBfalse\fR, then deferred functions will follow puppet relationships and ordering\. This allows puppet to install prerequisites needed for a deferred function and call the deferred function in the same run\.
1491
+ .
1492
+ .IP "\(bu" 4
1493
+ \fIDefault\fR: \fBtrue\fR
1494
+ .
1495
+ .IP "" 0
1496
+ .
1489
1497
  .SS "prerun_command"
1490
1498
  A command to run before every agent run\. If this command returns a non\-zero return code, the entire Puppet run will fail\.
1491
1499
  .
@@ -2000,7 +2008,7 @@ Whether to print stack traces on some errors\. Will print internal Ruby stack tr
2000
2008
  .IP "" 0
2001
2009
  .
2002
2010
  .SS "transactionstorefile"
2003
- Transactional storage file for persisting data between transactions for the purposes of infering information (such as corrective_change) on new data received\.
2011
+ Transactional storage file for persisting data between transactions for the purposes of inferring information (such as corrective_change) on new data received\.
2004
2012
  .
2005
2013
  .IP "\(bu" 4
2006
2014
  \fIDefault\fR: \fB$statedir/transactionstore\.yaml\fR
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-AGENT" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-AGENT" "8" "July 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-agent\fR \- The puppet agent daemon
@@ -51,7 +51,7 @@ Some flags are meant specifically for interactive use \-\-\- in particular, \'te
51
51
  \'\-\-tags\' allows you to specify what portions of a configuration you want to apply\. Puppet elements are tagged with all of the class or definition names that contain them, and you can use the \'tags\' flag to specify one of these names, causing only configuration elements contained within that class or definition to be applied\. This is very useful when you are testing new configurations \-\-\- for instance, if you are just starting to manage \'ntpd\', you would put all of the new elements into an \'ntpd\' class, and call puppet with \'\-\-tags ntpd\', which would only apply that small portion of the configuration during your testing, rather than applying the whole thing\.
52
52
  .
53
53
  .P
54
- \'\-\-fingerprint\' is a one\-time flag\. In this mode \'puppet agent\' runs once and displays on the console (and in the log) the current certificate (or certificate request) fingerprint\. Providing the \'\-\-digest\' option allows to use a different digest algorithm to generate the fingerprint\. The main use is to verify that before signing a certificate request on the master, the certificate request the master received is the same as the one the client sent (to prevent against man\-in\-the\-middle attacks when signing certificates)\.
54
+ \'\-\-fingerprint\' is a one\-time flag\. In this mode \'puppet agent\' runs once and displays on the console (and in the log) the current certificate (or certificate request) fingerprint\. Providing the \'\-\-digest\' option allows you to use a different digest algorithm to generate the fingerprint\. The main use is to verify that before signing a certificate request on the master, the certificate request the master received is the same as the one the client sent (to prevent against man\-in\-the\-middle attacks when signing certificates)\.
55
55
  .
56
56
  .P
57
57
  \'\-\-skip_tags\' is a flag used to filter resources\. If this is set, then only resources not tagged with the specified tags will be applied\. Values must be comma\-separated\.
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-APPLY" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-APPLY" "8" "July 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-apply\fR \- Apply Puppet manifests locally
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-CATALOG" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-CATALOG" "8" "July 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-catalog\fR \- Compile, save, view, and convert catalogs\.
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-CONFIG" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-CONFIG" "8" "July 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-config\fR \- Interact with Puppet\'s settings\.