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
19 def initialize(capath: Puppet[:localcacert],
20                crlpath: Puppet[:hostcrl],
21                privatekeydir: Puppet[:privatekeydir],
22                certdir: Puppet[:certdir],
23                requestdir: Puppet[:requestdir],
24                hostprivkey: Puppet.settings.set_by_config?(:hostprivkey) ? Puppet[:hostprivkey] : nil,
25                hostcert: Puppet.settings.set_by_config?(:hostcert) ? Puppet[:hostcert] : nil)
26   @capath = capath
27   @crlpath = crlpath
28   @privatekeydir = privatekeydir
29   @certdir = certdir
30   @requestdir = requestdir
31   @hostprivkey = hostprivkey
32   @hostcert = hostcert
33 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
278 def create_request(name, private_key)
279   options = {}
280 
281   if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != ''
282     options[:dns_alt_names] = Puppet[:dns_alt_names]
283   end
284 
285   csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
286   if csr_attributes.load
287     options[:csr_attributes] = csr_attributes.custom_attributes
288     options[:extension_requests] = csr_attributes.extension_requests
289   end
290 
291   csr = Puppet::SSL::CertificateRequest.new(name)
292   csr.generate(private_key, options)
293 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
133 def crl_last_update
134   stat = Puppet::FileSystem.stat(@crlpath)
135   Time.at(stat.mtime)
136 rescue Errno::ENOENT
137   nil
138 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
145 def crl_last_update=(time)
146   Puppet::FileSystem.touch(@crlpath, mtime: time)
147 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
331 def delete_request(name)
332   path = to_path(@requestdir, name)
333   delete_pem(path)
334 rescue SystemCallError => e
335   raise Puppet::Error.new(_("Failed to delete certificate request for '%{name}'") % {name: name}, e)
336 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
55 def load_cacerts(required: false)
56   pem = load_pem(@capath)
57   if !pem && required
58     raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath }
59   end
60   pem ? load_cacerts_from_pem(pem) : nil
61 rescue SystemCallError => e
62   raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % {capath: @capath}, e)
63 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
72 def load_cacerts_from_pem(pem)
73   # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
74   raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS
75 
76   pem.scan(CERT_DELIMITERS).map do |text|
77     OpenSSL::X509::Certificate.new(text)
78   end
79 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
249 def load_client_cert(name, required: false)
250   path = @hostcert || to_path(@certdir, name)
251   pem = load_pem(path)
252   if !pem && required
253     raise Puppet::Error, _("The client certificate is missing from '%{path}'") % { path: path }
254   end
255   pem ? load_client_cert_from_pem(pem) : nil
256 rescue SystemCallError => e
257   raise Puppet::Error.new(_("Failed to load client certificate for '%{name}'") % {name: name}, e)
258 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
267 def load_client_cert_from_pem(pem)
268   OpenSSL::X509::Certificate.new(pem)
269 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
101 def load_crls(required: false)
102   pem = load_pem(@crlpath)
103   if !pem && required
104     raise Puppet::Error, _("The CRL is missing from '%{path}'") % { path: @crlpath }
105   end
106   pem ? load_crls_from_pem(pem) : nil
107 rescue SystemCallError => e
108   raise Puppet::Error.new(_("Failed to load CRLs from '%{crlpath}'") % {crlpath: @crlpath}, e)
109 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
118 def load_crls_from_pem(pem)
119   # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
120   raise OpenSSL::X509::CRLError, _("Failed to parse CRLs as PEM") if pem !~ CRL_DELIMITERS
121 
122   pem.scan(CRL_DELIMITERS).map do |text|
123     OpenSSL::X509::CRL.new(text)
124   end
125 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
186 def load_private_key(name, required: false, password: nil)
187   path = @hostprivkey || to_path(@privatekeydir, name)
188   pem = load_pem(path)
189   if !pem && required
190     raise Puppet::Error, _("The private key is missing from '%{path}'") % { path: path }
191   end
192   pem ? load_private_key_from_pem(pem, password: password) : nil
193 rescue SystemCallError => e
194   raise Puppet::Error.new(_("Failed to load private key for '%{name}'") % {name: name}, e)
195 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
207 def load_private_key_from_pem(pem, password: nil)
208   # set a non-nil password to ensure openssl doesn't prompt
209   password ||= ''
210 
211   OpenSSL::PKey.read(pem, password)
212 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
220 def load_private_key_password
221   Puppet::FileSystem.read(Puppet[:passfile], :encoding => Encoding::BINARY)
222 rescue Errno::ENOENT
223   nil
224 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
317 def load_request(name)
318   path = to_path(@requestdir, name)
319   pem = load_pem(path)
320   pem ? load_request_from_pem(pem) : nil
321 rescue SystemCallError => e
322   raise Puppet::Error.new(_("Failed to load certificate request for '%{name}'") % {name: name}, e)
323 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
345 def load_request_from_pem(pem)
346   OpenSSL::X509::Request.new(pem)
347 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
41 def save_cacerts(certs)
42   save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert))
43 rescue SystemCallError => e
44   raise Puppet::Error.new(_("Failed to save CA certificates to '%{capath}'") % {capath: @capath}, e)
45 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
233 def save_client_cert(name, cert)
234   path = @hostcert || to_path(@certdir, name)
235   save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert))
236 rescue SystemCallError => e
237   raise Puppet::Error.new(_("Failed to save client certificate for '%{name}'") % {name: name}, e)
238 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
87 def save_crls(crls)
88   save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))
89 rescue SystemCallError => e
90   raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % {crlpath: @crlpath}, e)
91 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
160 def save_private_key(name, key, password: nil)
161   pem = if password
162           cipher = OpenSSL::Cipher::AES.new(128, :CBC)
163           key.export(cipher, password)
164         else
165           key.to_pem
166         end
167   path = @hostprivkey || to_path(@privatekeydir, name)
168   save_pem(pem, path, **permissions_for_setting(:hostprivkey))
169 rescue SystemCallError => e
170   raise Puppet::Error.new(_("Failed to save private key for '%{name}'") % {name: name}, e)
171 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
302 def save_request(name, csr)
303   path = to_path(@requestdir, name)
304   save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr))
305 rescue SystemCallError => e
306   raise Puppet::Error.new(_("Failed to save certificate request for '%{name}'") % {name: name}, e)
307 end

Private Instance Methods

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