puppet 7.16.0-universal-darwin → 7.19.0-universal-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +76 -15
- data/ext/systemd/puppet.service +2 -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/face/help.rb +1 -1
- data/lib/puppet/face/module/list.rb +16 -7
- data/lib/puppet/functions/capitalize.rb +1 -1
- data/lib/puppet/generate/type.rb +1 -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/parser/templatewrapper.rb +1 -1
- 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/reference/configuration.rb +2 -0
- data/lib/puppet/ssl/ssl_provider.rb +65 -12
- data/lib/puppet/ssl/state_machine.rb +13 -17
- data/lib/puppet/transaction.rb +22 -0
- data/lib/puppet/type/tidy.rb +1 -1
- data/lib/puppet/type/user.rb +3 -0
- data/lib/puppet/type.rb +20 -3
- data/lib/puppet/util/json.rb +5 -2
- data/lib/puppet/util/resource_template.rb +1 -1
- data/lib/puppet/util/selinux.rb +1 -1
- data/lib/puppet/util.rb +12 -1
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet.rb +1 -14
- data/man/man5/puppet.conf.5 +12 -4
- 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 +27 -10
- 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/face/module/list_spec.rb +26 -0
- 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/spec/unit/util/resource_template_spec.rb +1 -1
- data/spec/unit/util/selinux_spec.rb +5 -0
- data/spec/unit/util_spec.rb +11 -1
- data/tasks/generate_cert_fixtures.rake +5 -4
- metadata +2 -2
@@ -3,6 +3,16 @@ require_relative '../../../puppet/parser/script_compiler'
|
|
3
3
|
module Puppet::Pops
|
4
4
|
module Evaluator
|
5
5
|
|
6
|
+
class DeferredValue
|
7
|
+
def initialize(proc)
|
8
|
+
@proc = proc
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve
|
12
|
+
@proc.call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
# Utility class to help resolve instances of Puppet::Pops::Types::PDeferredType::Deferred
|
7
17
|
#
|
8
18
|
class DeferredResolver
|
@@ -20,9 +30,9 @@ class DeferredResolver
|
|
20
30
|
# are to be mixed into the scope
|
21
31
|
# @return [nil] does not return anything - the catalog is modified as a side effect
|
22
32
|
#
|
23
|
-
def self.resolve_and_replace(facts, catalog, environment = catalog.environment_instance)
|
24
|
-
compiler = Puppet::Parser::ScriptCompiler.new(environment, catalog.name,
|
25
|
-
resolver = new(compiler)
|
33
|
+
def self.resolve_and_replace(facts, catalog, environment = catalog.environment_instance, preprocess_deferred = true)
|
34
|
+
compiler = Puppet::Parser::ScriptCompiler.new(environment, catalog.name, preprocess_deferred)
|
35
|
+
resolver = new(compiler, preprocess_deferred)
|
26
36
|
resolver.set_facts_variable(facts)
|
27
37
|
# TODO:
|
28
38
|
# # When scripting the trusted data are always local, but set them anyway
|
@@ -53,11 +63,12 @@ class DeferredResolver
|
|
53
63
|
resolver.resolve(value)
|
54
64
|
end
|
55
65
|
|
56
|
-
def initialize(compiler)
|
66
|
+
def initialize(compiler, preprocess_deferred = true)
|
57
67
|
@compiler = compiler
|
58
68
|
# Always resolve in top scope
|
59
69
|
@scope = @compiler.topscope
|
60
70
|
@deferred_class = Puppet::Pops::Types::TypeFactory.deferred.implementation_class
|
71
|
+
@preprocess_deferred = preprocess_deferred
|
61
72
|
end
|
62
73
|
|
63
74
|
# @param facts [Puppet::Node::Facts] the facts to set in $facts in the compiler's topscope
|
@@ -106,6 +117,24 @@ class DeferredResolver
|
|
106
117
|
end
|
107
118
|
end
|
108
119
|
|
120
|
+
def resolve_lazy_args(x)
|
121
|
+
if x.is_a?(DeferredValue)
|
122
|
+
x.resolve
|
123
|
+
elsif x.is_a?(Array)
|
124
|
+
x.map {|v| resolve_lazy_args(v) }
|
125
|
+
elsif x.is_a?(Hash)
|
126
|
+
result = {}
|
127
|
+
x.each_pair {|k,v| result[k] = resolve_lazy_args(v) }
|
128
|
+
result
|
129
|
+
elsif x.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
|
130
|
+
# rewrap in a new Sensitive after resolving any nested deferred values
|
131
|
+
Puppet::Pops::Types::PSensitiveType::Sensitive.new(resolve_lazy_args(x.unwrap))
|
132
|
+
else
|
133
|
+
x
|
134
|
+
end
|
135
|
+
end
|
136
|
+
private :resolve_lazy_args
|
137
|
+
|
109
138
|
def resolve_future(f)
|
110
139
|
# If any of the arguments to a future is a future it needs to be resolved first
|
111
140
|
func_name = f.name
|
@@ -117,8 +146,19 @@ class DeferredResolver
|
|
117
146
|
mapped_arguments.insert(0, @scope[var_name])
|
118
147
|
end
|
119
148
|
|
120
|
-
|
121
|
-
|
149
|
+
if @preprocess_deferred
|
150
|
+
# call the function (name in deferred, or 'dig' for a variable)
|
151
|
+
@scope.call_function(func_name, mapped_arguments)
|
152
|
+
else
|
153
|
+
# call the function later
|
154
|
+
DeferredValue.new(
|
155
|
+
Proc.new {
|
156
|
+
# deferred functions can have nested deferred arguments
|
157
|
+
resolved_arguments = mapped_arguments.map { |arg| resolve_lazy_args(arg) }
|
158
|
+
@scope.call_function(func_name, resolved_arguments)
|
159
|
+
}
|
160
|
+
)
|
161
|
+
end
|
122
162
|
end
|
123
163
|
|
124
164
|
def map_arguments(args)
|
@@ -19,6 +19,10 @@ class Puppet::Pops::Functions::Dispatcher
|
|
19
19
|
@dispatchers.empty?
|
20
20
|
end
|
21
21
|
|
22
|
+
def find_matching_dispatcher(args, &block)
|
23
|
+
@dispatchers.find { |d| d.type.callable_with?(args, block) }
|
24
|
+
end
|
25
|
+
|
22
26
|
# Dispatches the call to the first found signature (entry with matching type).
|
23
27
|
#
|
24
28
|
# @param instance [Puppet::Functions::Function] - the function to call
|
@@ -28,19 +32,19 @@ class Puppet::Pops::Functions::Dispatcher
|
|
28
32
|
#
|
29
33
|
# @api private
|
30
34
|
def dispatch(instance, calling_scope, args, &block)
|
31
|
-
|
32
|
-
|
35
|
+
|
36
|
+
dispatcher = find_matching_dispatcher(args, &block)
|
37
|
+
unless dispatcher
|
33
38
|
args_type = Puppet::Pops::Types::TypeCalculator.singleton.infer_set(block_given? ? args + [block] : args)
|
34
39
|
raise ArgumentError, Puppet::Pops::Types::TypeMismatchDescriber.describe_signatures(instance.class.name, signatures, args_type)
|
35
40
|
end
|
36
|
-
|
37
|
-
|
38
|
-
msg = found.invoke(instance, calling_scope, args)
|
41
|
+
if dispatcher.argument_mismatch_handler?
|
42
|
+
msg = dispatcher.invoke(instance, calling_scope, args)
|
39
43
|
raise ArgumentError, "'#{instance.class.name}' #{msg}"
|
40
44
|
end
|
41
45
|
|
42
46
|
catch(:next) do
|
43
|
-
|
47
|
+
dispatcher.invoke(instance, calling_scope, args, &block)
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
@@ -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
|
|
@@ -38,6 +38,8 @@ config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc
|
|
38
38
|
val = '$confdir/hiera.yaml. However, for backwards compatibility, if a file exists at $codedir/hiera.yaml, Puppet uses that instead.'
|
39
39
|
elsif name.to_s == 'certname'
|
40
40
|
val = "the Host's fully qualified domain name, as determined by Facter"
|
41
|
+
elsif name.to_s == 'srv_domain'
|
42
|
+
val = 'example.com'
|
41
43
|
end
|
42
44
|
|
43
45
|
# Leave out the section information; it was apparently confusing people.
|
@@ -59,15 +59,18 @@ 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])
|
73
|
+
def create_system_context(cacerts:, path: Puppet[:ssl_trust_store], include_client_cert: false)
|
71
74
|
store = create_x509_store(cacerts, [], false, include_system_store: true)
|
72
75
|
|
73
76
|
if path
|
@@ -88,6 +91,29 @@ class Puppet::SSL::SSLProvider
|
|
88
91
|
end
|
89
92
|
end
|
90
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
|
+
|
91
117
|
Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: [], revocation: false).freeze
|
92
118
|
end
|
93
119
|
|
@@ -124,15 +150,7 @@ class Puppet::SSL::SSLProvider
|
|
124
150
|
raise ArgumentError, _("Client cert is missing") unless client_cert
|
125
151
|
|
126
152
|
store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
|
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
|
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,
|
@@ -191,6 +209,27 @@ class Puppet::SSL::SSLProvider
|
|
191
209
|
csr
|
192
210
|
end
|
193
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
|
+
|
194
233
|
private
|
195
234
|
|
196
235
|
def default_flags
|
@@ -237,6 +276,20 @@ class Puppet::SSL::SSLProvider
|
|
237
276
|
end
|
238
277
|
end
|
239
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
|
+
|
240
293
|
def verify_cert_with_store(store, cert)
|
241
294
|
# StoreContext#initialize accepts a chain argument, but it's set to [] because
|
242
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/tidy.rb
CHANGED
@@ -6,7 +6,7 @@ Puppet::Type.newtype(:tidy) do
|
|
6
6
|
|
7
7
|
@doc = "Remove unwanted files based on specific criteria. Multiple
|
8
8
|
criteria are OR'd together, so a file that is too large but is not
|
9
|
-
old enough will still get tidied.
|
9
|
+
old enough will still get tidied. Ignores managed resources.
|
10
10
|
|
11
11
|
If you don't specify either `age` or `size`, then all files will
|
12
12
|
be removed.
|
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/util/json.rb
CHANGED
@@ -49,13 +49,16 @@ module Puppet::Util
|
|
49
49
|
def self.load(string, options = {})
|
50
50
|
if defined? MultiJson
|
51
51
|
begin
|
52
|
-
# This ensures that JrJackson will parse very large or very small
|
52
|
+
# This ensures that JrJackson and Oj will parse very large or very small
|
53
53
|
# numbers as floats rather than BigDecimals, which are serialized as
|
54
54
|
# strings by the built-in JSON gem and therefore can cause schema errors,
|
55
55
|
# for example, when we are rendering reports to JSON using `to_pson` in
|
56
56
|
# PuppetDB.
|
57
|
-
|
57
|
+
case MultiJson.adapter.name
|
58
|
+
when "MultiJson::Adapters::JrJackson"
|
58
59
|
options[:use_bigdecimal] = false
|
60
|
+
when "MultiJson::Adapters::Oj"
|
61
|
+
options[:bigdecimal_load] = :float
|
59
62
|
end
|
60
63
|
|
61
64
|
MultiJson.load(string, options)
|
@@ -40,7 +40,7 @@ class Puppet::Util::ResourceTemplate
|
|
40
40
|
|
41
41
|
def evaluate
|
42
42
|
set_resource_variables
|
43
|
-
|
43
|
+
Puppet::Util.create_erb(Puppet::FileSystem.read(@file, :encoding => 'utf-8')).result(binding)
|
44
44
|
end
|
45
45
|
|
46
46
|
def initialize(file, resource)
|
data/lib/puppet/util/selinux.rb
CHANGED
@@ -204,7 +204,7 @@ module Puppet::Util::SELinux
|
|
204
204
|
def selinux_label_support?(file)
|
205
205
|
fstype = find_fs(file)
|
206
206
|
return false if fstype.nil?
|
207
|
-
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs', 'tmpfs']
|
207
|
+
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs', 'btrfs', 'tmpfs', 'zfs']
|
208
208
|
filesystems.include?(fstype)
|
209
209
|
end
|
210
210
|
|