puppet 6.25.1 → 6.28.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CODEOWNERS +1 -1
- data/Gemfile +2 -2
- data/Gemfile.lock +101 -34
- data/lib/puppet/agent.rb +47 -11
- data/lib/puppet/application/agent.rb +2 -12
- data/lib/puppet/application/lookup.rb +74 -24
- data/lib/puppet/concurrent/thread_local_singleton.rb +5 -3
- data/lib/puppet/configurer.rb +8 -14
- data/lib/puppet/defaults.rb +13 -3
- data/lib/puppet/face/generate.rb +2 -0
- data/lib/puppet/file_serving/metadata.rb +3 -0
- data/lib/puppet/file_system/file_impl.rb +7 -7
- data/lib/puppet/file_system/jruby.rb +1 -1
- data/lib/puppet/file_system/windows.rb +4 -4
- data/lib/puppet/file_system.rb +1 -1
- data/lib/puppet/functions/next.rb +18 -1
- data/lib/puppet/functions/tree_each.rb +0 -1
- data/lib/puppet/functions/versioncmp.rb +6 -2
- data/lib/puppet/generate/type.rb +9 -0
- data/lib/puppet/http/client.rb +22 -2
- data/lib/puppet/node.rb +1 -1
- data/lib/puppet/pops/parser/code_merger.rb +4 -4
- data/lib/puppet/pops/parser/egrammar.ra +2 -0
- data/lib/puppet/pops/parser/eparser.rb +813 -794
- data/lib/puppet/pops/serialization/to_data_converter.rb +6 -18
- data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
- data/lib/puppet/provider/package/windows/exe_package.rb +30 -1
- data/lib/puppet/provider/package/windows/package.rb +2 -1
- data/lib/puppet/provider/package/windows.rb +14 -1
- data/lib/puppet/provider/service/init.rb +5 -4
- data/lib/puppet/provider/user/directoryservice.rb +5 -0
- data/lib/puppet/ssl/ssl_provider.rb +75 -19
- data/lib/puppet/ssl/state_machine.rb +13 -17
- data/lib/puppet/ssl/verifier.rb +6 -0
- data/lib/puppet/transaction/persistence.rb +22 -12
- data/lib/puppet/type/exec.rb +1 -1
- data/lib/puppet/type/file/data_sync.rb +1 -1
- data/lib/puppet/type/user.rb +43 -38
- data/lib/puppet/util/json.rb +17 -0
- data/lib/puppet/util/log.rb +7 -2
- data/lib/puppet/util/monkey_patches.rb +6 -2
- data/lib/puppet/util/package.rb +25 -16
- data/lib/puppet/util/yaml.rb +21 -2
- data/lib/puppet/util.rb +1 -2
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet.rb +2 -14
- data/locales/puppet.pot +5 -10454
- data/man/man5/puppet.conf.5 +21 -2
- data/man/man8/puppet-agent.8 +1 -1
- 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-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +9 -6
- data/man/man8/puppet-man.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-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/unit/forge/bacula.json +1 -1
- data/spec/integration/application/agent_spec.rb +108 -0
- data/spec/integration/application/lookup_spec.rb +81 -50
- data/spec/integration/application/resource_spec.rb +6 -2
- 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/shared_contexts/l10n.rb +5 -0
- data/spec/unit/agent_spec.rb +28 -2
- data/spec/unit/application/agent_spec.rb +26 -16
- data/spec/unit/application/lookup_spec.rb +131 -10
- data/spec/unit/concurrent/thread_local_singleton_spec.rb +39 -0
- data/spec/unit/configurer_spec.rb +124 -61
- data/spec/unit/daemon_spec.rb +2 -11
- data/spec/unit/face/generate_spec.rb +64 -0
- data/spec/unit/file_system_spec.rb +34 -4
- data/spec/unit/forge/module_release_spec.rb +3 -3
- data/spec/unit/functions/versioncmp_spec.rb +40 -4
- data/spec/unit/http/client_spec.rb +18 -0
- data/spec/unit/node_spec.rb +6 -0
- data/spec/unit/pops/parser/parse_containers_spec.rb +2 -2
- data/spec/unit/pops/serialization/to_from_hr_spec.rb +0 -58
- data/spec/unit/pops/validator/validator_spec.rb +5 -0
- data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
- data/spec/unit/provider/package/windows/exe_package_spec.rb +17 -0
- data/spec/unit/provider/service/gentoo_spec.rb +6 -5
- data/spec/unit/provider/service/init_spec.rb +15 -9
- data/spec/unit/provider/service/openwrt_spec.rb +21 -29
- data/spec/unit/provider/service/redhat_spec.rb +3 -2
- data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
- data/spec/unit/ssl/state_machine_spec.rb +1 -0
- data/spec/unit/transaction/persistence_spec.rb +51 -0
- data/spec/unit/type/user_spec.rb +0 -45
- data/spec/unit/util/json_spec.rb +126 -0
- data/spec/unit/util/windows_spec.rb +23 -0
- data/spec/unit/util/yaml_spec.rb +54 -29
- data/tasks/generate_cert_fixtures.rake +5 -4
- metadata +9 -3
@@ -14,8 +14,6 @@ module Serialization
|
|
14
14
|
# @option options [Boolean] :local_reference use local references instead of duplicating complex entries
|
15
15
|
# @option options [Boolean] :type_by_reference `true` if Object types are converted to references rather than embedded.
|
16
16
|
# @option options [Boolean] :symbol_as_string `true` if Symbols should be converted to strings (with type loss)
|
17
|
-
# @option options [Boolean] :force_symbol `false` if Symbols should not be converted (rich_data and symbol_as_string must be false)
|
18
|
-
# @option options [Boolean] :silence_warnings `false` if warnings should be silenced
|
19
17
|
# @option options [String] :message_prefix String to prepend to in warnings and errors
|
20
18
|
# @return [Data] the processed result. An object assignable to `Data`.
|
21
19
|
#
|
@@ -43,12 +41,6 @@ module Serialization
|
|
43
41
|
@symbol_as_string = options[:symbol_as_string]
|
44
42
|
@symbol_as_string = false if @symbol_as_string.nil?
|
45
43
|
|
46
|
-
@force_symbol = options[:force_symbol]
|
47
|
-
@force_symbol = false if @force_symbol.nil?
|
48
|
-
|
49
|
-
@silence_warnings = options[:silence_warnings]
|
50
|
-
@silence_warnings = false if @silence_warnings.nil?
|
51
|
-
|
52
44
|
@rich_data = options[:rich_data]
|
53
45
|
@rich_data = false if @rich_data.nil?
|
54
46
|
|
@@ -100,11 +92,7 @@ module Serialization
|
|
100
92
|
elsif @rich_data
|
101
93
|
{ PCORE_TYPE_KEY => PCORE_TYPE_SYMBOL, PCORE_VALUE_KEY => value.to_s }
|
102
94
|
else
|
103
|
-
|
104
|
-
value
|
105
|
-
else
|
106
|
-
@silence_warnings ? unknown_to_string(value) : unknown_to_string_with_warning(value)
|
107
|
-
end
|
95
|
+
unknown_to_string_with_warning(value)
|
108
96
|
end
|
109
97
|
elsif value.instance_of?(Array)
|
110
98
|
process(value) do
|
@@ -129,11 +117,7 @@ module Serialization
|
|
129
117
|
{ PCORE_TYPE_KEY => PCORE_TYPE_SENSITIVE, PCORE_VALUE_KEY => to_data(value.unwrap) }
|
130
118
|
end
|
131
119
|
else
|
132
|
-
|
133
|
-
value_to_data_hash(value)
|
134
|
-
else
|
135
|
-
@silence_warnings ? unknown_to_string(value) : unknown_to_string_with_warning(value)
|
136
|
-
end
|
120
|
+
unknown_to_data(value)
|
137
121
|
end
|
138
122
|
end
|
139
123
|
|
@@ -207,6 +191,10 @@ module Serialization
|
|
207
191
|
v
|
208
192
|
end
|
209
193
|
|
194
|
+
def unknown_to_data(value)
|
195
|
+
@rich_data ? value_to_data_hash(value) : unknown_to_string_with_warning(value)
|
196
|
+
end
|
197
|
+
|
210
198
|
def unknown_key_to_string_with_warning(value)
|
211
199
|
str = unknown_to_string(value)
|
212
200
|
serialization_issue(Issues::SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING, :path => path_to_s, :klass => value.class, :value => str)
|
@@ -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
|
@@ -17,6 +17,11 @@ class Puppet::Provider::Package::Windows
|
|
17
17
|
'WindowsInstaller',
|
18
18
|
]
|
19
19
|
|
20
|
+
def self.register(path)
|
21
|
+
Puppet::Type::Package::ProviderWindows.paths ||= []
|
22
|
+
Puppet::Type::Package::ProviderWindows.paths << path
|
23
|
+
end
|
24
|
+
|
20
25
|
# Return an instance of the package from the registry, or nil
|
21
26
|
def self.from_registry(name, values)
|
22
27
|
if valid?(name, values)
|
@@ -55,7 +60,31 @@ class Puppet::Provider::Package::Windows
|
|
55
60
|
end
|
56
61
|
|
57
62
|
def self.install_command(resource)
|
58
|
-
|
63
|
+
file_location = resource[:source]
|
64
|
+
if file_location.start_with?('http://', 'https://')
|
65
|
+
tempfile = Tempfile.new(['','.exe'])
|
66
|
+
begin
|
67
|
+
uri = URI(Puppet::Util.uri_encode(file_location))
|
68
|
+
client = Puppet.runtime[:http]
|
69
|
+
client.get(uri, options: { include_system_store: true }) do |response|
|
70
|
+
raise Puppet::HTTP::ResponseError.new(response) unless response.success?
|
71
|
+
|
72
|
+
File.open(tempfile.path, 'wb') do |file|
|
73
|
+
response.read_body do |data|
|
74
|
+
file.write(data)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue => detail
|
79
|
+
raise Puppet::Error.new(_("Error when installing %{package}: %{detail}") % { package: resource[:name] ,detail: detail.message}, detail)
|
80
|
+
ensure
|
81
|
+
self.register(tempfile.path)
|
82
|
+
tempfile.close()
|
83
|
+
file_location = tempfile.path
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
munge(file_location)
|
59
88
|
end
|
60
89
|
|
61
90
|
def uninstall_command
|
@@ -67,7 +67,8 @@ class Puppet::Provider::Package::Windows
|
|
67
67
|
# REMIND: what about msp, etc
|
68
68
|
MsiPackage
|
69
69
|
when /\.exe"?\Z/i
|
70
|
-
fail(_("The source does not exist: '%{source}'") % { source: resource[:source] }) unless
|
70
|
+
fail(_("The source does not exist: '%{source}'") % { source: resource[:source] }) unless
|
71
|
+
Puppet::FileSystem.exist?(resource[:source]) || resource[:source].start_with?('http://', 'https://')
|
71
72
|
ExePackage
|
72
73
|
else
|
73
74
|
fail(_("Don't know how to install '%{source}'") % { source: resource[:source] })
|
@@ -30,6 +30,19 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
|
|
30
30
|
has_feature :versionable
|
31
31
|
|
32
32
|
attr_accessor :package
|
33
|
+
class << self
|
34
|
+
attr_accessor :paths
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.post_resource_eval
|
38
|
+
@paths.each do |path|
|
39
|
+
begin
|
40
|
+
Puppet::FileSystem.unlink(path)
|
41
|
+
rescue => detail
|
42
|
+
raise Puppet::Error.new(_("Error when unlinking %{path}: %{detail}") % { path: path ,detail: detail.message}, detail)
|
43
|
+
end
|
44
|
+
end if @paths
|
45
|
+
end
|
33
46
|
|
34
47
|
# Return an array of provider instances
|
35
48
|
def self.instances
|
@@ -64,7 +77,7 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
|
|
64
77
|
|
65
78
|
command = [installer.install_command(resource), install_options].flatten.compact.join(' ')
|
66
79
|
working_dir = File.dirname(resource[:source])
|
67
|
-
|
80
|
+
unless Puppet::FileSystem.exist?(working_dir)
|
68
81
|
working_dir = nil
|
69
82
|
end
|
70
83
|
output = execute(command, :failonfail => false, :combine => true, :cwd => working_dir, :suppress_window => true)
|
@@ -84,7 +84,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
|
|
84
84
|
defpath = [defpath] unless defpath.is_a? Array
|
85
85
|
instances = []
|
86
86
|
defpath.each do |path|
|
87
|
-
unless
|
87
|
+
unless Puppet::FileSystem.directory?(path)
|
88
88
|
Puppet.debug "Service path #{path} does not exist"
|
89
89
|
next
|
90
90
|
end
|
@@ -97,8 +97,9 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
|
|
97
97
|
fullpath = File.join(path, name)
|
98
98
|
next if name =~ /^\./
|
99
99
|
next if exclude.include? name
|
100
|
-
next if
|
101
|
-
next
|
100
|
+
next if Puppet::FileSystem.directory?(fullpath)
|
101
|
+
next unless Puppet::FileSystem.executable?(fullpath)
|
102
|
+
next unless is_init?(fullpath)
|
102
103
|
instances << new(:name => name, :path => path, :hasstatus => true)
|
103
104
|
end
|
104
105
|
end
|
@@ -122,7 +123,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
|
|
122
123
|
|
123
124
|
def paths
|
124
125
|
@paths ||= @resource[:path].find_all do |path|
|
125
|
-
if
|
126
|
+
if Puppet::FileSystem.directory?(path)
|
126
127
|
true
|
127
128
|
else
|
128
129
|
if Puppet::FileSystem.exist?(path)
|
@@ -401,6 +401,11 @@ Puppet::Type.type(:user).provide :directoryservice do
|
|
401
401
|
# we have to treat the ds cache just like you would in the password=
|
402
402
|
# method.
|
403
403
|
def salt=(value)
|
404
|
+
if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.15') >= 0)
|
405
|
+
if value.length != 64
|
406
|
+
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."
|
407
|
+
end
|
408
|
+
end
|
404
409
|
if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.7') > 0)
|
405
410
|
assert_full_pbkdf2_password
|
406
411
|
|
@@ -42,17 +42,19 @@ class Puppet::SSL::SSLProvider
|
|
42
42
|
# refers to the cacerts bundle in the puppet-agent package.
|
43
43
|
#
|
44
44
|
# Connections made from the returned context will authenticate the server,
|
45
|
-
# i.e. `VERIFY_PEER`, but will not use a client certificate
|
46
|
-
# perform revocation checking.
|
45
|
+
# i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)
|
46
|
+
# and will not perform revocation checking.
|
47
47
|
#
|
48
48
|
# @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs
|
49
49
|
# @param path [String, nil] A file containing additional trusted CA certs.
|
50
|
+
# @param include_client_cert [true, false] If true, the client cert will be added to the context
|
51
|
+
# allowing mutual TLS authentication. The default is false. If the client cert doesn't exist
|
52
|
+
# then the option will be ignored.
|
50
53
|
# @return [Puppet::SSL::SSLContext] A context to use to create connections
|
51
54
|
# @raise (see #create_context)
|
52
55
|
# @api private
|
53
|
-
def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
|
54
|
-
store = create_x509_store(cacerts, [], false)
|
55
|
-
store.set_default_paths
|
56
|
+
def create_system_context(cacerts:, path: Puppet[:ssl_trust_store], include_client_cert: false)
|
57
|
+
store = create_x509_store(cacerts, [], false, include_system_store: true)
|
56
58
|
|
57
59
|
if path
|
58
60
|
stat = Puppet::FileSystem.stat(path)
|
@@ -72,6 +74,29 @@ class Puppet::SSL::SSLProvider
|
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
77
|
+
if include_client_cert
|
78
|
+
cert_provider = Puppet::X509::CertProvider.new
|
79
|
+
private_key = cert_provider.load_private_key(Puppet[:certname], required: false)
|
80
|
+
unless private_key
|
81
|
+
Puppet.warning("Private key for '#{Puppet[:certname]}' does not exist")
|
82
|
+
end
|
83
|
+
|
84
|
+
client_cert = cert_provider.load_client_cert(Puppet[:certname], required: false)
|
85
|
+
unless client_cert
|
86
|
+
Puppet.warning("Client certificate for '#{Puppet[:certname]}' does not exist")
|
87
|
+
end
|
88
|
+
|
89
|
+
if private_key && client_cert
|
90
|
+
client_chain = resolve_client_chain(store, client_cert, private_key)
|
91
|
+
|
92
|
+
return Puppet::SSL::SSLContext.new(
|
93
|
+
store: store, cacerts: cacerts, crls: [],
|
94
|
+
private_key: private_key, client_cert: client_cert, client_chain: client_chain,
|
95
|
+
revocation: false
|
96
|
+
).freeze
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
75
100
|
Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: [], revocation: false).freeze
|
76
101
|
end
|
77
102
|
|
@@ -94,28 +119,21 @@ class Puppet::SSL::SSLProvider
|
|
94
119
|
# @param client_cert [OpenSSL::X509::Certificate] client's cert whose public
|
95
120
|
# key matches the `private_key`
|
96
121
|
# @param revocation [:chain, :leaf, false] revocation mode
|
122
|
+
# @param include_system_store [true, false] Also trust system CA
|
97
123
|
# @return [Puppet::SSL::SSLContext] A context to use to create connections
|
98
124
|
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
|
99
125
|
# one of the certs or CRLs.
|
100
126
|
# @raise [Puppet::SSL::SSLError] There was an issue with the
|
101
127
|
# `private_key`.
|
102
128
|
# @api private
|
103
|
-
def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation])
|
129
|
+
def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation], include_system_store: false)
|
104
130
|
raise ArgumentError, _("CA certs are missing") unless cacerts
|
105
131
|
raise ArgumentError, _("CRLs are missing") unless crls
|
106
132
|
raise ArgumentError, _("Private key is missing") unless private_key
|
107
133
|
raise ArgumentError, _("Client cert is missing") unless client_cert
|
108
134
|
|
109
|
-
store = create_x509_store(cacerts, crls, revocation)
|
110
|
-
client_chain =
|
111
|
-
|
112
|
-
if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
|
113
|
-
raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
|
114
|
-
end
|
115
|
-
|
116
|
-
unless client_cert.check_private_key(private_key)
|
117
|
-
raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
|
118
|
-
end
|
135
|
+
store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
|
136
|
+
client_chain = resolve_client_chain(store, client_cert, private_key)
|
119
137
|
|
120
138
|
Puppet::SSL::SSLContext.new(
|
121
139
|
store: store, cacerts: cacerts, crls: crls,
|
@@ -134,12 +152,13 @@ class Puppet::SSL::SSLProvider
|
|
134
152
|
# @param password [String, nil] If the private key is encrypted, decrypt
|
135
153
|
# it using the password. If the key is encrypted, but a password is
|
136
154
|
# not specified, then the key cannot be loaded.
|
155
|
+
# @param include_system_store [true, false] Also trust system CA
|
137
156
|
# @return [Puppet::SSL::SSLContext] A context to use to create connections
|
138
157
|
# @raise [Puppet::SSL::CertVerifyError] There was an issue with
|
139
158
|
# one of the certs or CRLs.
|
140
159
|
# @raise [Puppet::Error] There was an issue with one of the required components.
|
141
160
|
# @api private
|
142
|
-
def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil)
|
161
|
+
def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil, include_system_store: false)
|
143
162
|
cert = Puppet::X509::CertProvider.new
|
144
163
|
cacerts = cert.load_cacerts(required: true)
|
145
164
|
crls = case revocation
|
@@ -151,7 +170,7 @@ class Puppet::SSL::SSLProvider
|
|
151
170
|
private_key = cert.load_private_key(certname, required: true, password: password)
|
152
171
|
client_cert = cert.load_client_cert(certname, required: true)
|
153
172
|
|
154
|
-
create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation)
|
173
|
+
create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation, include_system_store: include_system_store)
|
155
174
|
rescue OpenSSL::PKey::PKeyError => e
|
156
175
|
raise Puppet::SSL::SSLError.new(_("Failed to load private key for host '%{name}': %{message}") % { name: certname, message: e.message }, e)
|
157
176
|
end
|
@@ -173,6 +192,27 @@ class Puppet::SSL::SSLProvider
|
|
173
192
|
csr
|
174
193
|
end
|
175
194
|
|
195
|
+
def print(ssl_context, alg = 'SHA256')
|
196
|
+
if Puppet::Util::Log.sendlevel?(:debug)
|
197
|
+
chain = ssl_context.client_chain
|
198
|
+
# print from root to client
|
199
|
+
chain.reverse.each_with_index do |cert, i|
|
200
|
+
digest = Puppet::SSL::Digest.new(alg, cert.to_der)
|
201
|
+
if i == chain.length - 1
|
202
|
+
Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
|
203
|
+
else
|
204
|
+
Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
|
205
|
+
end
|
206
|
+
end
|
207
|
+
ssl_context.crls.each do |crl|
|
208
|
+
oid_values = Hash[crl.extensions.map { |ext| [ext.oid, ext.value] }]
|
209
|
+
crlNumber = oid_values['crlNumber'] || 'unknown'
|
210
|
+
authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp!
|
211
|
+
Puppet.debug("Using CRL '#{crl.issuer.to_utf8}' authorityKeyIdentifier '#{authKeyId}' crlNumber '#{crlNumber }'")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
176
216
|
private
|
177
217
|
|
178
218
|
def default_flags
|
@@ -186,7 +226,7 @@ class Puppet::SSL::SSLProvider
|
|
186
226
|
end
|
187
227
|
end
|
188
228
|
|
189
|
-
def create_x509_store(roots, crls, revocation)
|
229
|
+
def create_x509_store(roots, crls, revocation, include_system_store: false)
|
190
230
|
store = OpenSSL::X509::Store.new
|
191
231
|
store.purpose = OpenSSL::X509::PURPOSE_ANY
|
192
232
|
store.flags = default_flags | revocation_mode(revocation)
|
@@ -194,6 +234,8 @@ class Puppet::SSL::SSLProvider
|
|
194
234
|
roots.each { |cert| store.add_cert(cert) }
|
195
235
|
crls.each { |crl| store.add_crl(crl) }
|
196
236
|
|
237
|
+
store.set_default_paths if include_system_store
|
238
|
+
|
197
239
|
store
|
198
240
|
end
|
199
241
|
|
@@ -217,6 +259,20 @@ class Puppet::SSL::SSLProvider
|
|
217
259
|
end
|
218
260
|
end
|
219
261
|
|
262
|
+
def resolve_client_chain(store, client_cert, private_key)
|
263
|
+
client_chain = verify_cert_with_store(store, client_cert)
|
264
|
+
|
265
|
+
if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
|
266
|
+
raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
|
267
|
+
end
|
268
|
+
|
269
|
+
unless client_cert.check_private_key(private_key)
|
270
|
+
raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
|
271
|
+
end
|
272
|
+
|
273
|
+
client_chain
|
274
|
+
end
|
275
|
+
|
220
276
|
def verify_cert_with_store(store, cert)
|
221
277
|
# StoreContext#initialize accepts a chain argument, but it's set to [] because
|
222
278
|
# 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
|
|
@@ -417,20 +426,7 @@ class Puppet::SSL::StateMachine
|
|
417
426
|
def ensure_client_certificate
|
418
427
|
final_state = run_machine(NeedLock.new(self), Done)
|
419
428
|
ssl_context = final_state.ssl_context
|
420
|
-
|
421
|
-
if Puppet::Util::Log.sendlevel?(:debug)
|
422
|
-
chain = ssl_context.client_chain
|
423
|
-
# print from root to client
|
424
|
-
chain.reverse.each_with_index do |cert, i|
|
425
|
-
digest = Puppet::SSL::Digest.new(@digest, cert.to_der)
|
426
|
-
if i == chain.length - 1
|
427
|
-
Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
|
428
|
-
else
|
429
|
-
Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
|
430
|
-
end
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
429
|
+
@ssl_provider.print(ssl_context, @digest)
|
434
430
|
ssl_context
|
435
431
|
end
|
436
432
|
|
data/lib/puppet/ssl/verifier.rb
CHANGED
@@ -115,6 +115,12 @@ class Puppet::SSL::Verifier
|
|
115
115
|
return false
|
116
116
|
end
|
117
117
|
|
118
|
+
# ruby-openssl#74ef8c0cc56b840b772240f2ee2b0fc0aafa2743 now sets the
|
119
|
+
# store_context error when the cert is mismatched
|
120
|
+
when OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH
|
121
|
+
@last_error = Puppet::SSL::CertMismatchError.new(peer_cert, @hostname)
|
122
|
+
return false
|
123
|
+
|
118
124
|
when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID
|
119
125
|
crl = store_context.current_crl
|
120
126
|
if crl && crl.last_update && crl.last_update < Time.now + FIVE_MINUTES_AS_SECONDS
|
@@ -6,6 +6,26 @@ require 'puppet/util/yaml'
|
|
6
6
|
# as calculating corrective_change).
|
7
7
|
# @api private
|
8
8
|
class Puppet::Transaction::Persistence
|
9
|
+
|
10
|
+
def self.allowed_classes
|
11
|
+
@allowed_classes ||= [
|
12
|
+
Symbol,
|
13
|
+
Time,
|
14
|
+
Regexp,
|
15
|
+
# URI is excluded, because it serializes all instance variables including the
|
16
|
+
# URI parser. Better to serialize the URL encoded representation.
|
17
|
+
SemanticPuppet::Version,
|
18
|
+
# SemanticPuppet::VersionRange has many nested classes and is unlikely to be
|
19
|
+
# used directly, so ignore it
|
20
|
+
Puppet::Pops::Time::Timestamp,
|
21
|
+
Puppet::Pops::Time::TimeData,
|
22
|
+
Puppet::Pops::Time::Timespan,
|
23
|
+
Puppet::Pops::Types::PBinaryType::Binary,
|
24
|
+
# Puppet::Pops::Types::PSensitiveType::Sensitive values are excluded from
|
25
|
+
# the persistence store, ignore it.
|
26
|
+
].freeze
|
27
|
+
end
|
28
|
+
|
9
29
|
def initialize
|
10
30
|
@old_data = {}
|
11
31
|
@new_data = {"resources" => {}}
|
@@ -62,7 +82,7 @@ class Puppet::Transaction::Persistence
|
|
62
82
|
result = nil
|
63
83
|
Puppet::Util.benchmark(:debug, _("Loaded transaction store file in %{seconds} seconds")) do
|
64
84
|
begin
|
65
|
-
result = Puppet::Util::Yaml.safe_load_file(filename,
|
85
|
+
result = Puppet::Util::Yaml.safe_load_file(filename, self.class.allowed_classes)
|
66
86
|
rescue Puppet::Util::Yaml::YamlLoadError => detail
|
67
87
|
Puppet.log_exception(detail, _("Transaction store file %{filename} is corrupt (%{detail}); replacing") % { filename: filename, detail: detail })
|
68
88
|
|
@@ -87,17 +107,7 @@ class Puppet::Transaction::Persistence
|
|
87
107
|
|
88
108
|
# Save data from internal class to persistence store on disk.
|
89
109
|
def save
|
90
|
-
|
91
|
-
@new_data, {
|
92
|
-
symbol_as_string: false,
|
93
|
-
local_reference: false,
|
94
|
-
type_by_reference: true,
|
95
|
-
force_symbol: true,
|
96
|
-
silence_warnings: true,
|
97
|
-
message_prefix: to_s
|
98
|
-
}
|
99
|
-
)
|
100
|
-
Puppet::Util::Yaml.dump(converted_data, Puppet[:transactionstorefile])
|
110
|
+
Puppet::Util::Yaml.dump(@new_data, Puppet[:transactionstorefile])
|
101
111
|
end
|
102
112
|
|
103
113
|
# Use the catalog and run_mode to determine if persistence should be enabled or not
|
data/lib/puppet/type/exec.rb
CHANGED
@@ -457,7 +457,7 @@ module Puppet
|
|
457
457
|
|
458
458
|
exec { '/bin/echo root >> /usr/lib/cron/cron.allow':
|
459
459
|
path => '/usr/bin:/usr/sbin:/bin',
|
460
|
-
unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
|
460
|
+
unless => 'grep ^root$ /usr/lib/cron/cron.allow 2>/dev/null',
|
461
461
|
}
|
462
462
|
|
463
463
|
This would add `root` to the cron.allow file (on Solaris) unless
|
@@ -79,7 +79,7 @@ module Puppet
|
|
79
79
|
return :absent unless stat
|
80
80
|
ftype = stat.ftype
|
81
81
|
# Don't even try to manage the content on directories or links
|
82
|
-
return nil if [
|
82
|
+
return nil if ['directory', 'link', 'fifo', 'socket'].include?(ftype)
|
83
83
|
|
84
84
|
begin
|
85
85
|
resource.parameter(:checksum).sum_file(resource[:path])
|