class Puppet::X509::CertProvider

Class for loading and saving cert related objects. By default the provider loads and saves based on puppet's default settings, such as `Puppet`. The providers sets the permissions on files it saves, such as the private key. All of the `load_*` methods take an optional `required` parameter. If an object doesn't exist, then by default the provider returns `nil`. However, if the `required` parameter is true, then an exception will be raised instead.

@api private

Constants

CERT_DELIMITERS
CRL_DELIMITERS
VALID_CERTNAME

Only allow printing ascii characters, excluding /

Public Class Methods

new(capath: Puppet[:localcacert], crlpath: Puppet[:hostcrl], privatekeydir: Puppet[:privatekeydir], certdir: Puppet[:certdir], requestdir: Puppet[:requestdir], hostprivkey: Puppet.settings.set_by_config?(:hostprivkey) ? Puppet[:hostprivkey] : nil, hostcert: Puppet.settings.set_by_config?(:hostcert) ? Puppet[:hostcert] : nil) click to toggle source
   # File lib/puppet/x509/cert_provider.rb
20 def initialize(capath: Puppet[:localcacert],
21                crlpath: Puppet[:hostcrl],
22                privatekeydir: Puppet[:privatekeydir],
23                certdir: Puppet[:certdir],
24                requestdir: Puppet[:requestdir],
25                hostprivkey: Puppet.settings.set_by_config?(:hostprivkey) ? Puppet[:hostprivkey] : nil,
26                hostcert: Puppet.settings.set_by_config?(:hostcert) ? Puppet[:hostcert] : nil)
27   @capath = capath
28   @crlpath = crlpath
29   @privatekeydir = privatekeydir
30   @certdir = certdir
31   @requestdir = requestdir
32   @hostprivkey = hostprivkey
33   @hostcert = hostcert
34 end

Public Instance Methods

create_request(name, private_key) click to toggle source

Create a certificate signing request (CSR).

@param name [String] the request identity @param private_key [OpenSSL::PKey::RSA] private key @return [Puppet::X509::Request] The request

@api private

    # File lib/puppet/x509/cert_provider.rb
279 def create_request(name, private_key)
280   options = {}
281 
282   if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != ''
283     options[:dns_alt_names] = Puppet[:dns_alt_names]
284   end
285 
286   csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
287   if csr_attributes.load
288     options[:csr_attributes] = csr_attributes.custom_attributes
289     options[:extension_requests] = csr_attributes.extension_requests
290   end
291 
292   csr = Puppet::SSL::CertificateRequest.new(name)
293   csr.generate(private_key, options)
294 end
crl_last_update() click to toggle source

Return the time when the CRL was last updated.

@return [Time, nil] Time when the CRL was last updated, or nil if we don't

have a CRL

@api private

    # File lib/puppet/x509/cert_provider.rb
134 def crl_last_update
135   stat = Puppet::FileSystem.stat(@crlpath)
136   Time.at(stat.mtime)
137 rescue Errno::ENOENT
138   nil
139 end
crl_last_update=(time) click to toggle source

Set the CRL last updated time.

@param time [Time] The last updated time

@api private

    # File lib/puppet/x509/cert_provider.rb
146 def crl_last_update=(time)
147   Puppet::FileSystem.touch(@crlpath, mtime: time)
148 end
delete_request(name) click to toggle source

Delete a named certificate signing request (CSR) from the configured `requestdir`.

@param name [String] The request identity @return [Boolean] true if the CSR was deleted

@api private

    # File lib/puppet/x509/cert_provider.rb
332 def delete_request(name)
333   path = to_path(@requestdir, name)
334   delete_pem(path)
335 rescue SystemCallError => e
336   raise Puppet::Error.new(_("Failed to delete certificate request for '%{name}'") % {name: name}, e)
337 end
load_cacerts(required: false) click to toggle source

Load CA certs from the configured `capath`.

@param required [Boolean] If true, raise if they are missing @return (see load_cacerts_from_pem) @raise (see load_cacerts_from_pem) @raise [Puppet::Error] if the certs cannot be loaded

@api private

   # File lib/puppet/x509/cert_provider.rb
56 def load_cacerts(required: false)
57   pem = load_pem(@capath)
58   if !pem && required
59     raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath }
60   end
61   pem ? load_cacerts_from_pem(pem) : nil
62 rescue SystemCallError => e
63   raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % {capath: @capath}, e)
64 end
load_cacerts_from_pem(pem) click to toggle source

Load PEM encoded CA certificates.

@param pem [String] PEM encoded certificate(s) @return [Array<OpenSSL::X509::Certificate>] Array of CA certs @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert

@api private

   # File lib/puppet/x509/cert_provider.rb
73 def load_cacerts_from_pem(pem)
74   # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
75   raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS
76 
77   pem.scan(CERT_DELIMITERS).map do |text|
78     OpenSSL::X509::Certificate.new(text)
79   end
80 end
load_client_cert(name, required: false) click to toggle source

Load a named client cert from the configured `certdir`.

@param name [String] The client cert identity @param required [Boolean] If true, raise it is missing @return (see load_request_from_pem) @raise (see load_client_cert_from_pem) @raise [Puppet::Error] if the client cert cannot be loaded

@api private

    # File lib/puppet/x509/cert_provider.rb
250 def load_client_cert(name, required: false)
251   path = @hostcert || to_path(@certdir, name)
252   pem = load_pem(path)
253   if !pem && required
254     raise Puppet::Error, _("The client certificate is missing from '%{path}'") % { path: path }
255   end
256   pem ? load_client_cert_from_pem(pem) : nil
257 rescue SystemCallError => e
258   raise Puppet::Error.new(_("Failed to load client certificate for '%{name}'") % {name: name}, e)
259 end
load_client_cert_from_pem(pem) click to toggle source

Load a PEM encoded certificate.

@param pem [String] PEM encoded cert @return [OpenSSL::X509::Certificate] the certificate @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert

@api private

    # File lib/puppet/x509/cert_provider.rb
268 def load_client_cert_from_pem(pem)
269   OpenSSL::X509::Certificate.new(pem)
270 end
load_crls(required: false) click to toggle source

Load CRLs from the configured `crlpath` path.

@param required [Boolean] If true, raise if they are missing @return (see load_crls_from_pem) @raise (see load_crls_from_pem) @raise [Puppet::Error] if the CRLs cannot be loaded

@api private

    # File lib/puppet/x509/cert_provider.rb
102 def load_crls(required: false)
103   pem = load_pem(@crlpath)
104   if !pem && required
105     raise Puppet::Error, _("The CRL is missing from '%{path}'") % { path: @crlpath }
106   end
107   pem ? load_crls_from_pem(pem) : nil
108 rescue SystemCallError => e
109   raise Puppet::Error.new(_("Failed to load CRLs from '%{crlpath}'") % {crlpath: @crlpath}, e)
110 end
load_crls_from_pem(pem) click to toggle source

Load PEM encoded CRL(s).

@param pem [String] PEM encoded CRL(s) @return [Array<OpenSSL::X509::CRL>] Array of CRLs @raise [OpenSSL::X509::CRLError] The `pem` text does not contain a valid CRL

@api private

    # File lib/puppet/x509/cert_provider.rb
119 def load_crls_from_pem(pem)
120   # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
121   raise OpenSSL::X509::CRLError, _("Failed to parse CRLs as PEM") if pem !~ CRL_DELIMITERS
122 
123   pem.scan(CRL_DELIMITERS).map do |text|
124     OpenSSL::X509::CRL.new(text)
125   end
126 end
load_private_key(name, required: false, password: nil) click to toggle source

Load a private key from the configured `privatekeydir`. For historical reasons, names are case-insensitive.

@param name [String] The private key identity @param required [Boolean] If true, raise if it is missing @param password [String, nil] If the private key is encrypted, decrypt

it using the password. If the key is encrypted, but a password is
not specified, then the key cannot be loaded.

@return (see load_private_key_from_pem) @raise (see load_private_key_from_pem) @raise [Puppet::Error] if the private key cannot be loaded

@api private

    # File lib/puppet/x509/cert_provider.rb
187 def load_private_key(name, required: false, password: nil)
188   path = @hostprivkey || to_path(@privatekeydir, name)
189   pem = load_pem(path)
190   if !pem && required
191     raise Puppet::Error, _("The private key is missing from '%{path}'") % { path: path }
192   end
193   pem ? load_private_key_from_pem(pem, password: password) : nil
194 rescue SystemCallError => e
195   raise Puppet::Error.new(_("Failed to load private key for '%{name}'") % {name: name}, e)
196 end
load_private_key_from_pem(pem, password: nil) click to toggle source

Load a PEM encoded private key.

@param pem [String] PEM encoded private key @param password [String, nil] If the private key is encrypted, decrypt

it using the password. If the key is encrypted, but a password is
not specified, then the key cannot be loaded.

@return [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] The private key @raise [OpenSSL::PKey::PKeyError] The `pem` text does not contain a valid key

@api private

    # File lib/puppet/x509/cert_provider.rb
208 def load_private_key_from_pem(pem, password: nil)
209   # set a non-nil password to ensure openssl doesn't prompt
210   password ||= ''
211 
212   OpenSSL::PKey.read(pem, password)
213 end
load_private_key_password() click to toggle source

Load the private key password.

@return [String, nil] The private key password as a binary string or nil

if there is none.

@api private

    # File lib/puppet/x509/cert_provider.rb
221 def load_private_key_password
222   Puppet::FileSystem.read(Puppet[:passfile], :encoding => Encoding::BINARY)
223 rescue Errno::ENOENT
224   nil
225 end
load_request(name) click to toggle source

Load a named certificate signing request (CSR) from the configured `requestdir`.

@param name [String] The request identity @return (see load_request_from_pem) @raise (see load_request_from_pem) @raise [Puppet::Error] if the cert request cannot be saved

@api private

    # File lib/puppet/x509/cert_provider.rb
318 def load_request(name)
319   path = to_path(@requestdir, name)
320   pem = load_pem(path)
321   pem ? load_request_from_pem(pem) : nil
322 rescue SystemCallError => e
323   raise Puppet::Error.new(_("Failed to load certificate request for '%{name}'") % {name: name}, e)
324 end
load_request_from_pem(pem) click to toggle source

Load a PEM encoded certificate signing request (CSR).

@param pem [String] PEM encoded request @return [OpenSSL::X509::Request] the request @raise [OpenSSL::X509::RequestError] The `pem` text does not contain a valid request

@api private

    # File lib/puppet/x509/cert_provider.rb
346 def load_request_from_pem(pem)
347   OpenSSL::X509::Request.new(pem)
348 end
save_cacerts(certs) click to toggle source

Save `certs` to the configured `capath`.

@param certs [Array<OpenSSL::X509::Certificate>] Array of CA certs to save @raise [Puppet::Error] if the certs cannot be saved

@api private

   # File lib/puppet/x509/cert_provider.rb
42 def save_cacerts(certs)
43   save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert))
44 rescue SystemCallError => e
45   raise Puppet::Error.new(_("Failed to save CA certificates to '%{capath}'") % {capath: @capath}, e)
46 end
save_client_cert(name, cert) click to toggle source

Save a named client cert to the configured `certdir`.

@param name [String] The client cert identity @param cert [OpenSSL::X509::Certificate] The cert to save @raise [Puppet::Error] if the client cert cannot be saved

@api private

    # File lib/puppet/x509/cert_provider.rb
234 def save_client_cert(name, cert)
235   path = @hostcert || to_path(@certdir, name)
236   save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert))
237 rescue SystemCallError => e
238   raise Puppet::Error.new(_("Failed to save client certificate for '%{name}'") % {name: name}, e)
239 end
save_crls(crls) click to toggle source

Save `crls` to the configured `crlpath`.

@param crls [Array<OpenSSL::X509::CRL>] Array of CRLs to save @raise [Puppet::Error] if the CRLs cannot be saved

@api private

   # File lib/puppet/x509/cert_provider.rb
88 def save_crls(crls)
89   save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))
90 rescue SystemCallError => e
91   raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % {crlpath: @crlpath}, e)
92 end
save_private_key(name, key, password: nil) click to toggle source

Save named private key in the configured `privatekeydir`. For historical reasons, names are case insensitive.

@param name [String] The private key identity @param key [OpenSSL::PKey::RSA] private key @param password [String, nil] If non-nil, derive an encryption key

from the password, and use that to encrypt the private key. If nil,
save the private key unencrypted.

@raise [Puppet::Error] if the private key cannot be saved

@api private

    # File lib/puppet/x509/cert_provider.rb
161 def save_private_key(name, key, password: nil)
162   pem = if password
163           cipher = OpenSSL::Cipher::AES.new(128, :CBC)
164           key.export(cipher, password)
165         else
166           key.to_pem
167         end
168   path = @hostprivkey || to_path(@privatekeydir, name)
169   save_pem(pem, path, **permissions_for_setting(:hostprivkey))
170 rescue SystemCallError => e
171   raise Puppet::Error.new(_("Failed to save private key for '%{name}'") % {name: name}, e)
172 end
save_request(name, csr) click to toggle source

Save a certificate signing request (CSR) to the configured `requestdir`.

@param name [String] the request identity @param csr [OpenSSL::X509::Request] the request @raise [Puppet::Error] if the cert request cannot be saved

@api private

    # File lib/puppet/x509/cert_provider.rb
303 def save_request(name, csr)
304   path = to_path(@requestdir, name)
305   save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr))
306 rescue SystemCallError => e
307   raise Puppet::Error.new(_("Failed to save certificate request for '%{name}'") % {name: name}, e)
308 end

Private Instance Methods

permissions_for_setting(name) click to toggle source
    # File lib/puppet/x509/cert_provider.rb
357 def permissions_for_setting(name)
358   setting = Puppet.settings.setting(name)
359   perm = { mode: setting.mode.to_i(8) }
360   if Puppet.features.root? && !Puppet::Util::Platform.windows?
361     perm[:owner] = setting.owner
362     perm[:group] = setting.group
363   end
364   perm
365 end
to_path(base, name) click to toggle source
    # File lib/puppet/x509/cert_provider.rb
352 def to_path(base, name)
353   raise _("Certname %{name} must not contain unprintable or non-ASCII characters") % { name: name.inspect } unless name =~ VALID_CERTNAME
354   File.join(base, "#{name.downcase}.pem")
355 end