puppet 7.15.0-x64-mingw32 → 7.18.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +75 -14
- data/ext/systemd/puppet.service +1 -1
- data/lib/puppet/agent.rb +47 -11
- data/lib/puppet/application/agent.rb +3 -13
- data/lib/puppet/application/apply.rb +2 -2
- data/lib/puppet/configurer.rb +1 -1
- data/lib/puppet/defaults.rb +11 -1
- data/lib/puppet/http/client.rb +22 -2
- data/lib/puppet/info_service/task_information_service.rb +1 -1
- data/lib/puppet/module/task.rb +5 -1
- data/lib/puppet/parameter.rb +19 -4
- data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
- data/lib/puppet/pops/functions/dispatcher.rb +10 -6
- data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
- data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
- data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
- data/lib/puppet/provider/package/yum.rb +8 -3
- data/lib/puppet/provider/user/directoryservice.rb +15 -8
- data/lib/puppet/ssl/ssl_provider.rb +75 -19
- data/lib/puppet/ssl/state_machine.rb +13 -17
- data/lib/puppet/transaction.rb +22 -0
- data/lib/puppet/type/user.rb +3 -0
- data/lib/puppet/type.rb +20 -3
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet.rb +1 -14
- data/man/man5/puppet.conf.5 +11 -3
- data/man/man8/puppet-agent.8 +2 -2
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-ssl.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/integration/application/agent_spec.rb +157 -0
- data/spec/integration/application/apply_spec.rb +74 -0
- data/spec/integration/http/client_spec.rb +51 -4
- data/spec/lib/puppet_spec/https.rb +1 -1
- data/spec/lib/puppet_spec/puppetserver.rb +39 -2
- data/spec/unit/agent_spec.rb +28 -2
- data/spec/unit/application/agent_spec.rb +26 -16
- data/spec/unit/daemon_spec.rb +2 -11
- data/spec/unit/http/client_spec.rb +18 -0
- data/spec/unit/info_service_spec.rb +11 -3
- data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
- data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
- data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
- data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
- data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
- data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
- data/spec/unit/ssl/state_machine_spec.rb +1 -0
- data/spec/unit/task_spec.rb +56 -13
- data/tasks/generate_cert_fixtures.rake +5 -4
- 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
|
-
|
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(
|
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(
|
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
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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 =
|
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
|
-
|
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]
|
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,
|
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
|
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 =
|
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
|
-
|
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
|
-
|
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
|
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
|
|
data/lib/puppet/transaction.rb
CHANGED
@@ -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'
|
data/lib/puppet/type/user.rb
CHANGED
@@ -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(:
|
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
|
data/lib/puppet/version.rb
CHANGED
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
|
data/man/man5/puppet.conf.5
CHANGED
@@ -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" "
|
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\.
|
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
|
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
|
data/man/man8/puppet-agent.8
CHANGED
@@ -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" "
|
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\.
|
data/man/man8/puppet-apply.8
CHANGED
@@ -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" "
|
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
|
data/man/man8/puppet-catalog.8
CHANGED
@@ -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" "
|
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\.
|
data/man/man8/puppet-config.8
CHANGED
@@ -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" "
|
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\.
|