puppet 6.3.0-universal-darwin → 6.4.0-universal-darwin
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 +30 -0
- data/Gemfile.lock +9 -9
- data/lib/puppet.rb +13 -0
- data/lib/puppet/application/agent.rb +8 -12
- data/lib/puppet/application/device.rb +2 -3
- data/lib/puppet/application/filebucket.rb +6 -1
- data/lib/puppet/application/ssl.rb +102 -55
- data/lib/puppet/configurer.rb +8 -7
- data/lib/puppet/defaults.rb +3 -1
- data/lib/puppet/file_system.rb +24 -4
- data/lib/puppet/file_system/file_impl.rb +25 -0
- data/lib/puppet/file_system/jruby.rb +23 -0
- data/lib/puppet/file_system/windows.rb +84 -0
- data/lib/puppet/indirector/rest.rb +4 -2
- data/lib/puppet/loaders.rb +1 -0
- data/lib/puppet/network/http.rb +1 -0
- data/lib/puppet/network/http/base_pool.rb +18 -0
- data/lib/puppet/network/http/connection.rb +49 -17
- data/lib/puppet/network/http/nocache_pool.rb +9 -4
- data/lib/puppet/network/http/pool.rb +10 -11
- data/lib/puppet/network/http/session.rb +3 -2
- data/lib/puppet/network/http_pool.rb +32 -0
- data/lib/puppet/pops/loader/generic_plan_instantiator.rb +28 -0
- data/lib/puppet/pops/loader/loader_paths.rb +46 -10
- data/lib/puppet/pops/loader/module_loaders.rb +10 -3
- data/lib/puppet/provider/file/windows.rb +49 -1
- data/lib/puppet/provider/package/windows.rb +5 -1
- data/lib/puppet/reports/http.rb +2 -1
- data/lib/puppet/rest/client.rb +7 -3
- data/lib/puppet/rest/routes.rb +9 -44
- data/lib/puppet/ssl.rb +6 -0
- data/lib/puppet/ssl/error.rb +26 -0
- data/lib/puppet/ssl/host.rb +9 -92
- data/lib/puppet/ssl/ssl_context.rb +30 -0
- data/lib/puppet/ssl/ssl_provider.rb +232 -0
- data/lib/puppet/ssl/state_machine.rb +261 -0
- data/lib/puppet/ssl/validator.rb +1 -0
- data/lib/puppet/ssl/validator/default_validator.rb +1 -0
- data/lib/puppet/ssl/validator/no_validator.rb +2 -0
- data/lib/puppet/ssl/verifier.rb +134 -0
- data/lib/puppet/ssl/verifier_adapter.rb +48 -0
- data/lib/puppet/test/test_helper.rb +2 -1
- data/lib/puppet/type/exec.rb +30 -6
- data/lib/puppet/type/file/mode.rb +6 -1
- data/lib/puppet/type/file/source.rb +2 -2
- data/lib/puppet/type/filebucket.rb +12 -8
- data/lib/puppet/type/user.rb +14 -1
- data/lib/puppet/util/connection.rb +10 -5
- data/lib/puppet/util/feature.rb +11 -2
- data/lib/puppet/util/http_proxy.rb +3 -2
- data/lib/puppet/util/pidlock.rb +1 -1
- data/lib/puppet/util/ssl.rb +1 -10
- data/lib/puppet/util/windows/security.rb +29 -8
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet/x509.rb +7 -0
- data/lib/puppet/x509/cert_provider.rb +286 -0
- data/lib/puppet/x509/pem_store.rb +55 -0
- data/locales/ja/puppet.po +740 -590
- data/locales/puppet.pot +433 -208
- data/man/man5/puppet.conf.5 +6 -3
- 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 +6 -2
- 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 +1 -1
- 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 +5 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/ssl/127.0.0.1-key.pem +67 -0
- data/spec/fixtures/ssl/127.0.0.1.pem +48 -0
- data/spec/fixtures/ssl/bad-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/bad-int-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/ca.pem +59 -0
- data/spec/fixtures/ssl/crl.pem +30 -0
- data/spec/fixtures/ssl/encrypted-key.pem +70 -0
- data/spec/fixtures/ssl/intermediate-agent-crl.pem +31 -0
- data/spec/fixtures/ssl/intermediate-agent.pem +60 -0
- data/spec/fixtures/ssl/intermediate-crl.pem +36 -0
- data/spec/fixtures/ssl/intermediate.pem +60 -0
- data/spec/fixtures/ssl/netlock-arany-utf8.pem +23 -0
- data/spec/fixtures/ssl/pluto-key.pem +67 -0
- data/spec/fixtures/ssl/pluto.pem +44 -0
- data/spec/fixtures/ssl/request-key.pem +67 -0
- data/spec/fixtures/ssl/request.pem +39 -0
- data/spec/fixtures/ssl/revoked-key.pem +67 -0
- data/spec/fixtures/ssl/revoked.pem +44 -0
- data/spec/fixtures/ssl/signed-key.pem +67 -0
- data/spec/fixtures/ssl/signed.pem +44 -0
- data/spec/fixtures/ssl/tampered-cert.pem +44 -0
- data/spec/fixtures/ssl/tampered-csr.pem +39 -0
- data/spec/integration/network/http_pool_spec.rb +222 -0
- data/spec/integration/provider/file/windows_spec.rb +162 -0
- data/spec/integration/rest/client_spec.rb +73 -0
- data/spec/integration/type/file_spec.rb +0 -19
- data/spec/lib/puppet/test_ca.rb +87 -50
- data/spec/lib/puppet_spec/fixtures.rb +20 -0
- data/spec/lib/puppet_spec/https.rb +84 -0
- data/spec/unit/application/agent_spec.rb +29 -30
- data/spec/unit/application/device_spec.rb +12 -49
- data/spec/unit/application/ssl_spec.rb +24 -38
- data/spec/unit/configurer_spec.rb +11 -11
- data/spec/unit/file_system/uniquefile_spec.rb +6 -0
- data/spec/unit/file_system_spec.rb +214 -0
- data/spec/unit/indirector/rest_spec.rb +3 -3
- data/spec/unit/network/http/connection_spec.rb +30 -90
- data/spec/unit/network/http/factory_spec.rb +1 -0
- data/spec/unit/network/http/nocache_pool_spec.rb +8 -8
- data/spec/unit/network/http/pool_spec.rb +63 -33
- data/spec/unit/network/http/session_spec.rb +8 -1
- data/spec/unit/network/http_pool_spec.rb +36 -0
- data/spec/unit/pops/loaders/loader_spec.rb +26 -1
- data/spec/unit/provider/package/windows_spec.rb +12 -1
- data/spec/unit/reports/http_spec.rb +7 -7
- data/spec/unit/rest/client_spec.rb +4 -6
- data/spec/unit/ssl/host_spec.rb +39 -33
- data/spec/unit/ssl/ssl_provider_spec.rb +428 -0
- data/spec/unit/ssl/state_machine_spec.rb +502 -0
- data/spec/unit/ssl/verifier_spec.rb +123 -0
- data/spec/unit/type/exec_spec.rb +63 -0
- data/spec/unit/type/file/source_spec.rb +5 -5
- data/spec/unit/type/filebucket_spec.rb +8 -6
- data/spec/unit/util/feature_spec.rb +2 -2
- data/spec/unit/util/storage_spec.rb +19 -19
- data/spec/unit/x509/cert_provider_spec.rb +527 -0
- data/spec/unit/x509/pem_store_spec.rb +160 -0
- data/tasks/generate_cert_fixtures.rake +158 -0
- metadata +78 -4
- data/MAINTAINERS +0 -47
- data/lib/puppet/rest/ssl_context.rb +0 -13
@@ -182,6 +182,7 @@ module Puppet::Util::HttpProxy
|
|
182
182
|
end
|
183
183
|
|
184
184
|
response = proxy.send(:head, current_uri.path, headers)
|
185
|
+
Puppet.debug("HTTP HEAD request to #{current_uri} returned #{response.code} #{response.message}")
|
185
186
|
|
186
187
|
if [301, 302, 307].include?(response.code.to_i)
|
187
188
|
# handle the redirection
|
@@ -195,9 +196,9 @@ module Puppet::Util::HttpProxy
|
|
195
196
|
else
|
196
197
|
response = proxy.send(method, current_uri.path, headers)
|
197
198
|
end
|
198
|
-
end
|
199
199
|
|
200
|
-
|
200
|
+
Puppet.debug("HTTP #{method.to_s.upcase} request to #{current_uri} returned #{response.code} #{response.message}")
|
201
|
+
end
|
201
202
|
|
202
203
|
return response
|
203
204
|
end
|
data/lib/puppet/util/pidlock.rb
CHANGED
@@ -62,7 +62,7 @@ class Puppet::Util::Pidlock
|
|
62
62
|
# POSIX and Windows platforms (PUP-9247).
|
63
63
|
if Puppet.features.posix?
|
64
64
|
procname = Puppet::Util::Execution.execute(["ps", "-p", lock_pid, "-o", "comm="]).strip
|
65
|
-
@lockfile.unlock unless procname =~ /puppet
|
65
|
+
@lockfile.unlock unless procname =~ /puppet(-.*)?$/
|
66
66
|
elsif Puppet.features.microsoft_windows?
|
67
67
|
# On Windows, we're checking if the filesystem path name of the running
|
68
68
|
# process is our vendored ruby:
|
data/lib/puppet/util/ssl.rb
CHANGED
@@ -75,16 +75,7 @@ module Puppet::Util::SSL
|
|
75
75
|
msg << ": [" + verifier.verify_errors.join('; ') + "]"
|
76
76
|
raise Puppet::Error, msg, error.backtrace
|
77
77
|
elsif peer_cert && !OpenSSL::SSL.verify_certificate_identity(peer_cert, host)
|
78
|
-
|
79
|
-
*Puppet::SSL::Certificate.subject_alt_names_for(peer_cert)].uniq
|
80
|
-
if valid_certnames.size > 1
|
81
|
-
expected_certnames = _("expected one of %{certnames}") % { certnames: valid_certnames.join(', ') }
|
82
|
-
else
|
83
|
-
expected_certnames = _("expected %{certname}") % { certname: valid_certnames.first }
|
84
|
-
end
|
85
|
-
|
86
|
-
msg = _("Server hostname '%{host}' did not match server certificate; %{expected_certnames}") % { host: host, expected_certnames: expected_certnames }
|
87
|
-
raise Puppet::Error, msg, error.backtrace
|
78
|
+
raise Puppet::SSL::CertMismatchError.new(peer_cert, host)
|
88
79
|
else
|
89
80
|
raise error
|
90
81
|
end
|
@@ -274,7 +274,7 @@ module Puppet::Util::Windows::Security
|
|
274
274
|
# mode. Only a user with the SE_BACKUP_NAME and SE_RESTORE_NAME
|
275
275
|
# privileges in their process token can change the mode for objects
|
276
276
|
# that they do not have read and write access to.
|
277
|
-
def set_mode(mode, path, protected = true)
|
277
|
+
def set_mode(mode, path, protected = true, managing_owner = false, managing_group = false)
|
278
278
|
sd = get_security_descriptor(path)
|
279
279
|
well_known_world_sid = Puppet::Util::Windows::SID::Everyone
|
280
280
|
well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody
|
@@ -319,6 +319,8 @@ module Puppet::Util::Windows::Security
|
|
319
319
|
nobody_allow |= FILE::FILE_APPEND_DATA;
|
320
320
|
end
|
321
321
|
|
322
|
+
isownergroup = sd.owner == sd.group
|
323
|
+
|
322
324
|
# caller is NOT managing SYSTEM by using group or owner, so set to FULL
|
323
325
|
if ! [sd.owner, sd.group].include? well_known_system_sid
|
324
326
|
# we don't check S_ISYSTEM_MISSING bit, but automatically carry over existing SYSTEM perms
|
@@ -328,15 +330,35 @@ module Puppet::Util::Windows::Security
|
|
328
330
|
# It is possible to set SYSTEM with a mode other than Full Control (7) however this makes no sense and in practical terms
|
329
331
|
# should not be done. We can trap these instances and correct them before being applied.
|
330
332
|
if (sd.owner == well_known_system_sid) && (owner_allow != FILE::FILE_ALL_ACCESS)
|
331
|
-
#
|
332
|
-
|
333
|
-
|
333
|
+
# If owner and group are both SYSTEM but group is unmanaged the control rights of system will be set to FullControl by
|
334
|
+
# the unmanaged group, so there is no need for the warning
|
335
|
+
if managing_owner && (!isownergroup || managing_group)
|
336
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
337
|
+
Puppet.warning _("Setting control rights for %{path} owner SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path }
|
338
|
+
elsif managing_owner && isownergroup
|
339
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
340
|
+
Puppet.debug _("%{path} owner and group both set to user SYSTEM, but group is not managed directly: SYSTEM user rights will be set to FullControl by group") % { path: path }
|
341
|
+
else
|
342
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
343
|
+
Puppet.debug _("An attempt to set mode %{mode} on item %{path} would result in the owner, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path }
|
344
|
+
owner_allow = FILE::FILE_ALL_ACCESS
|
345
|
+
end
|
334
346
|
end
|
335
347
|
|
336
348
|
if (sd.group == well_known_system_sid) && (group_allow != FILE::FILE_ALL_ACCESS)
|
337
|
-
#
|
338
|
-
|
339
|
-
|
349
|
+
# If owner and group are both SYSTEM but owner is unmanaged the control rights of system will be set to FullControl by
|
350
|
+
# the unmanaged owner, so there is no need for the warning.
|
351
|
+
if managing_group && (!isownergroup || managing_owner)
|
352
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
353
|
+
Puppet.warning _("Setting control rights for %{path} group SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path }
|
354
|
+
elsif managing_group && isownergroup
|
355
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
356
|
+
Puppet.debug _("%{path} owner and group both set to user SYSTEM, but owner is not managed directly: SYSTEM user rights will be set to FullControl by owner") % { path: path }
|
357
|
+
else
|
358
|
+
#TRANSLATORS 'SYSTEM' is a Windows name and should not be translated
|
359
|
+
Puppet.debug _("An attempt to set mode %{mode} on item %{path} would result in the group, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path }
|
360
|
+
group_allow = FILE::FILE_ALL_ACCESS
|
361
|
+
end
|
340
362
|
end
|
341
363
|
end
|
342
364
|
|
@@ -353,7 +375,6 @@ module Puppet::Util::Windows::Security
|
|
353
375
|
end
|
354
376
|
|
355
377
|
# if owner and group the same, then map group permissions to the one owner ACE
|
356
|
-
isownergroup = sd.owner == sd.group
|
357
378
|
if isownergroup
|
358
379
|
owner_allow |= group_allow
|
359
380
|
end
|
data/lib/puppet/version.rb
CHANGED
data/lib/puppet/x509.rb
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'puppet/x509'
|
2
|
+
|
3
|
+
# Class for loading and saving cert related objects.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
class Puppet::X509::CertProvider
|
7
|
+
include Puppet::X509::PemStore
|
8
|
+
|
9
|
+
# Only allow printing ascii characters, excluding /
|
10
|
+
VALID_CERTNAME = /\A[ -.0-~]+\Z/
|
11
|
+
CERT_DELIMITERS = /-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m
|
12
|
+
CRL_DELIMITERS = /-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m
|
13
|
+
|
14
|
+
def initialize(capath: Puppet[:localcacert],
|
15
|
+
crlpath: Puppet[:hostcrl],
|
16
|
+
privatekeydir: Puppet[:privatekeydir],
|
17
|
+
certdir: Puppet[:certdir],
|
18
|
+
requestdir: Puppet[:requestdir])
|
19
|
+
@capath = capath
|
20
|
+
@crlpath = crlpath
|
21
|
+
@privatekeydir = privatekeydir
|
22
|
+
@certdir = certdir
|
23
|
+
@requestdir = requestdir
|
24
|
+
end
|
25
|
+
|
26
|
+
# Save `certs` to the configured `capath`.
|
27
|
+
#
|
28
|
+
# @param certs [Array<OpenSSL::X509::Certificate>] Array of CA certs to save
|
29
|
+
# @raise [Puppet::Error] if the certs cannot be saved
|
30
|
+
# @api private
|
31
|
+
def save_cacerts(certs)
|
32
|
+
save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert))
|
33
|
+
rescue SystemCallError => e
|
34
|
+
raise Puppet::Error.new(_("Failed to save CA certificates to '%{capath}'") % {capath: @capath}, e)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load CA certs from the configured `capath`.
|
38
|
+
#
|
39
|
+
# @param required [Boolean] If true, raise if they are missing
|
40
|
+
# @return (see #load_cacerts_from_pem)
|
41
|
+
# @raise (see #load_cacerts_from_pem)
|
42
|
+
# @raise [Puppet::Error] if the certs cannot be loaded
|
43
|
+
# @api private
|
44
|
+
def load_cacerts(required: false)
|
45
|
+
pem = load_pem(@capath)
|
46
|
+
if !pem && required
|
47
|
+
raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath }
|
48
|
+
end
|
49
|
+
pem ? load_cacerts_from_pem(pem) : nil
|
50
|
+
rescue SystemCallError => e
|
51
|
+
raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % {capath: @capath}, e)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Load PEM encoded CA certificates.
|
55
|
+
#
|
56
|
+
# @param pem [String] PEM encoded certificate(s)
|
57
|
+
# @return [Array<OpenSSL::X509::Certificate>] Array of CA certs
|
58
|
+
# @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert
|
59
|
+
# @api private
|
60
|
+
def load_cacerts_from_pem(pem)
|
61
|
+
# TRANSLATORS 'PEM' is an acronym and shouldn't be translated
|
62
|
+
raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS
|
63
|
+
|
64
|
+
pem.scan(CERT_DELIMITERS).map do |text|
|
65
|
+
OpenSSL::X509::Certificate.new(text)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Save `crls` to the configured `crlpath`.
|
70
|
+
#
|
71
|
+
# @param crls [Array<OpenSSL::X509::CRL>] Array of CRLs to save
|
72
|
+
# @raise [Puppet::Error] if the CRLs cannot be saved
|
73
|
+
# @api private
|
74
|
+
def save_crls(crls)
|
75
|
+
save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))
|
76
|
+
rescue SystemCallError => e
|
77
|
+
raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % {crlpath: @crlpath}, e)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Load CRLs from the configured `crlpath` path.
|
81
|
+
#
|
82
|
+
# @param required [Boolean] If true, raise if they are missing
|
83
|
+
# @return (see #load_crls_from_pem)
|
84
|
+
# @raise (see #load_crls_from_pem)
|
85
|
+
# @raise [Puppet::Error] if the CRLs cannot be loaded
|
86
|
+
# @api private
|
87
|
+
def load_crls(required: false)
|
88
|
+
pem = load_pem(@crlpath)
|
89
|
+
if !pem && required
|
90
|
+
raise Puppet::Error, _("The CRL is missing from '%{path}'") % { path: @crlpath }
|
91
|
+
end
|
92
|
+
pem ? load_crls_from_pem(pem) : nil
|
93
|
+
rescue SystemCallError => e
|
94
|
+
raise Puppet::Error.new(_("Failed to load CRLs from '%{crlpath}'") % {crlpath: @crlpath}, e)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Load PEM encoded CRL(s).
|
98
|
+
#
|
99
|
+
# @param pem [String] PEM encoded CRL(s)
|
100
|
+
# @return [Array<OpenSSL::X509::CRL>] Array of CRLs
|
101
|
+
# @raise [OpenSSL::X509::CRLError] The `pem` text does not contain a valid CRL
|
102
|
+
# @api private
|
103
|
+
def load_crls_from_pem(pem)
|
104
|
+
# TRANSLATORS 'PEM' is an acronym and shouldn't be translated
|
105
|
+
raise OpenSSL::X509::CRLError, _("Failed to parse CRLs as PEM") if pem !~ CRL_DELIMITERS
|
106
|
+
|
107
|
+
pem.scan(CRL_DELIMITERS).map do |text|
|
108
|
+
OpenSSL::X509::CRL.new(text)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Save named private key in the configured `privatekeydir`. For
|
113
|
+
# historical reasons, names are case insensitive.
|
114
|
+
#
|
115
|
+
# @param name [String] The private key identity
|
116
|
+
# @param key [OpenSSL::PKey::RSA] private key
|
117
|
+
# @raise [Puppet::Error] if the private key cannot be saved
|
118
|
+
# @api private
|
119
|
+
def save_private_key(name, key)
|
120
|
+
path = to_path(@privatekeydir, name)
|
121
|
+
save_pem(key.to_pem, path, **permissions_for_setting(:hostprivkey))
|
122
|
+
rescue SystemCallError => e
|
123
|
+
raise Puppet::Error.new(_("Failed to save private key for '%{name}'") % {name: name}, e)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Load a private key from the configured `privatekeydir`. For
|
127
|
+
# historical reasons, names are case-insensitive.
|
128
|
+
#
|
129
|
+
# @param name [String] The private key identity
|
130
|
+
# @param required [Boolean] If true, raise if it is missing
|
131
|
+
# @return (see #load_private_key_from_pem)
|
132
|
+
# @raise (see #load_private_key_from_pem)
|
133
|
+
# @raise [Puppet::Error] if the private key cannot be loaded
|
134
|
+
# @api private
|
135
|
+
def load_private_key(name, required: false)
|
136
|
+
path = to_path(@privatekeydir, name)
|
137
|
+
pem = load_pem(path)
|
138
|
+
if !pem && required
|
139
|
+
raise Puppet::Error, _("The private key is missing from '%{path}'") % { path: path }
|
140
|
+
end
|
141
|
+
pem ? load_private_key_from_pem(pem) : nil
|
142
|
+
rescue SystemCallError => e
|
143
|
+
raise Puppet::Error.new(_("Failed to load private key for '%{name}'") % {name: name}, e)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Load a PEM encoded private key.
|
147
|
+
#
|
148
|
+
# @param pem [String] PEM encoded private key
|
149
|
+
# @return [OpenSSL::PKey::RSA] The private key
|
150
|
+
# @raise [OpenSSL::PKey::RSAError] The `pem` text does not contain a valid key
|
151
|
+
# @api private
|
152
|
+
def load_private_key_from_pem(pem)
|
153
|
+
# set a non-nil passphrase to ensure openssl doesn't prompt
|
154
|
+
# but ruby 2.4.0 & 2.4.1 require at least 4 bytes, see
|
155
|
+
# https://github.com/ruby/ruby/commit/f012932218fd609f75f9268812df61fb26e2d0f1#diff-40e4270ec386990ac60d7ab5ff8045a4
|
156
|
+
OpenSSL::PKey::RSA.new(pem, ' ')
|
157
|
+
end
|
158
|
+
|
159
|
+
# Save a named client cert to the configured `certdir`.
|
160
|
+
#
|
161
|
+
# @param name [String] The client cert identity
|
162
|
+
# @param cert [OpenSSL::X509::Certificate] The cert to save
|
163
|
+
# @raise [Puppet::Error] if the client cert cannot be saved
|
164
|
+
# @api private
|
165
|
+
def save_client_cert(name, cert)
|
166
|
+
path = to_path(@certdir, name)
|
167
|
+
save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert))
|
168
|
+
rescue SystemCallError => e
|
169
|
+
raise Puppet::Error.new(_("Failed to save client certificate for '%{name}'") % {name: name}, e)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Load a named client cert from the configured `certdir`.
|
173
|
+
#
|
174
|
+
# @param name [String] The client cert identity
|
175
|
+
# @param required [Boolean] If true, raise it is missing
|
176
|
+
# @return (see #load_request_from_pem)
|
177
|
+
# @raise (see #load_client_cert_from_pem)
|
178
|
+
# @raise [Puppet::Error] if the client cert cannot be loaded
|
179
|
+
# @api private
|
180
|
+
def load_client_cert(name, required: false)
|
181
|
+
path = to_path(@certdir, name)
|
182
|
+
pem = load_pem(path)
|
183
|
+
if !pem && required
|
184
|
+
raise Puppet::Error, _("The client certificate is missing from '%{path}'") % { path: path }
|
185
|
+
end
|
186
|
+
pem ? load_client_cert_from_pem(pem) : nil
|
187
|
+
rescue SystemCallError => e
|
188
|
+
raise Puppet::Error.new(_("Failed to load client certificate for '%{name}'") % {name: name}, e)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Load a PEM encoded certificate.
|
192
|
+
#
|
193
|
+
# @param pem [String] PEM encoded cert
|
194
|
+
# @return [OpenSSL::X509::Certificate] the certificate
|
195
|
+
# @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert
|
196
|
+
# @api private
|
197
|
+
def load_client_cert_from_pem(pem)
|
198
|
+
OpenSSL::X509::Certificate.new(pem)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Create a certificate signing request (CSR).
|
202
|
+
#
|
203
|
+
# @param name [String] the request identity
|
204
|
+
# @param private_key [OpenSSL::PKey::RSA] private key
|
205
|
+
# @return [Puppet::X509::Request] The request
|
206
|
+
#
|
207
|
+
def create_request(name, private_key)
|
208
|
+
options = {}
|
209
|
+
|
210
|
+
if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != ''
|
211
|
+
options[:dns_alt_names] = Puppet[:dns_alt_names]
|
212
|
+
end
|
213
|
+
|
214
|
+
csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
|
215
|
+
if csr_attributes.load
|
216
|
+
options[:csr_attributes] = csr_attributes.custom_attributes
|
217
|
+
options[:extension_requests] = csr_attributes.extension_requests
|
218
|
+
end
|
219
|
+
|
220
|
+
csr = Puppet::SSL::CertificateRequest.new(name)
|
221
|
+
csr.generate(private_key, options)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Save a certificate signing request (CSR) to the configured `requestdir`.
|
225
|
+
#
|
226
|
+
# @param name [String] the request identity
|
227
|
+
# @param csr [OpenSSL::X509::Request] the request
|
228
|
+
# @raise [Puppet::Error] if the cert request cannot be saved
|
229
|
+
# @api private
|
230
|
+
def save_request(name, csr)
|
231
|
+
path = to_path(@requestdir, name)
|
232
|
+
save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr))
|
233
|
+
rescue SystemCallError => e
|
234
|
+
raise Puppet::Error.new(_("Failed to save certificate request for '%{name}'") % {name: name}, e)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Load a named certificate signing request (CSR) from the configured `requestdir`.
|
238
|
+
#
|
239
|
+
# @param name [String] The request identity
|
240
|
+
# @return (see #load_request_from_pem)
|
241
|
+
# @raise (see #load_request_from_pem)
|
242
|
+
# @raise [Puppet::Error] if the cert request cannot be saved
|
243
|
+
# @api private
|
244
|
+
def load_request(name)
|
245
|
+
path = to_path(@requestdir, name)
|
246
|
+
pem = load_pem(path)
|
247
|
+
pem ? load_request_from_pem(pem) : nil
|
248
|
+
rescue SystemCallError => e
|
249
|
+
raise Puppet::Error.new(_("Failed to load certificate request for '%{name}'") % {name: name}, e)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Delete a named certificate signing request (CSR) from the configured `requestdir`.
|
253
|
+
#
|
254
|
+
# @param name [String] The request identity
|
255
|
+
# @return [Boolean] true if the CSR was deleted
|
256
|
+
def delete_request(name)
|
257
|
+
path = to_path(@requestdir, name)
|
258
|
+
delete_pem(path)
|
259
|
+
rescue SystemCallError => e
|
260
|
+
raise Puppet::Error.new(_("Failed to delete certificate request for '%{name}'") % {name: name}, e)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Load a PEM encoded certificate signing request (CSR).
|
264
|
+
#
|
265
|
+
# @param pem [String] PEM encoded request
|
266
|
+
# @return [OpenSSL::X509::Request] the request
|
267
|
+
# @raise [OpenSSL::X509::RequestError] The `pem` text does not contain a valid request
|
268
|
+
# @api private
|
269
|
+
def load_request_from_pem(pem)
|
270
|
+
OpenSSL::X509::Request.new(pem)
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def to_path(base, name)
|
276
|
+
raise _("Certname %{name} must not contain unprintable or non-ASCII characters") % { name: name.inspect } unless name =~ VALID_CERTNAME
|
277
|
+
File.join(base, "#{name.downcase}.pem")
|
278
|
+
end
|
279
|
+
|
280
|
+
def permissions_for_setting(name)
|
281
|
+
setting = Puppet.settings.setting(name)
|
282
|
+
perm = { mode: setting.mode.to_i(8) }
|
283
|
+
perm.merge!(owner: setting.owner, group: setting.group) if Puppet.features.root? && !Puppet::Util::Platform.windows?
|
284
|
+
perm
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'puppet/x509'
|
2
|
+
|
3
|
+
# Methods for managing PEM encoded files. While PEM encoded strings are
|
4
|
+
# always ASCII, the files may contain user specified comments, so they are
|
5
|
+
# UTF-8 encoded.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
module Puppet::X509::PemStore
|
9
|
+
# Load a pem encoded object.
|
10
|
+
#
|
11
|
+
# @param path [String] file path
|
12
|
+
# @return [String, nil] The PEM encoded object or nil if the
|
13
|
+
# path does not exist
|
14
|
+
# @raise [Errno::EACCES] if permission is denied
|
15
|
+
# @api private
|
16
|
+
def load_pem(path)
|
17
|
+
Puppet::FileSystem.read(path, encoding: 'UTF-8')
|
18
|
+
rescue Errno::ENOENT
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# Save pem encoded content to a file. If the file doesn't exist, it
|
23
|
+
# will be created. Otherwise, the file will be overwritten. In both
|
24
|
+
# cases the contents will be overwritten atomically so other
|
25
|
+
# processes don't see a partially written file.
|
26
|
+
#
|
27
|
+
# @param pem [String] The PEM encoded object to write
|
28
|
+
# @param path [String] The file path to write to
|
29
|
+
# @raise [Errno::EACCES] if permission is denied
|
30
|
+
# @raise [Errno::EPERM] if the operation cannot be completed
|
31
|
+
# @api private
|
32
|
+
def save_pem(pem, path, owner: nil, group: nil, mode: 0644)
|
33
|
+
Puppet::FileSystem.replace_file(path, mode) do |f|
|
34
|
+
f.set_encoding('UTF-8')
|
35
|
+
f.write(pem.encode('UTF-8'))
|
36
|
+
end
|
37
|
+
|
38
|
+
if !Puppet::Util::Platform.windows? && Puppet.features.root? && (owner || group)
|
39
|
+
FileUtils.chown(owner, group, path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Delete a pem encoded object, if it exists.
|
44
|
+
#
|
45
|
+
# @param path [String] The file path to delete
|
46
|
+
# @return [Boolean] Returns true if the file was deleted, false otherwise
|
47
|
+
# @raise [Errno::EACCES] if permission is denied
|
48
|
+
# @api private
|
49
|
+
def delete_pem(path)
|
50
|
+
Puppet::FileSystem.unlink(path)
|
51
|
+
true
|
52
|
+
rescue Errno::ENOENT
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|