Class: Puppet::X509::CertProvider Private

Inherits:
Object
  • Object
show all
Includes:
PemStore
Defined in:
lib/puppet/x509/cert_provider.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

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.

Constant Summary collapse

VALID_CERTNAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Only allow printing ascii characters, excluding /

/\A[ -.0-~]+\Z/
CERT_DELIMITERS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m
CRL_DELIMITERS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m

Instance Method Summary collapse

Methods included from PemStore

#delete_pem, #load_pem, #save_pem

Constructor Details

#initialize(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) ⇒ CertProvider

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of CertProvider.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/puppet/x509/cert_provider.rb', line 21

def initialize(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)
  @capath = capath
  @crlpath = crlpath
  @privatekeydir = privatekeydir
  @certdir = certdir
  @requestdir = requestdir
  @hostprivkey = hostprivkey
  @hostcert = hostcert
end

Instance Method Details

#ca_last_updateTime?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return the time when the CA bundle was last updated.

Returns:

  • (Time, nil)

    Time when the CA bundle was last updated, or nil if we don’t have a CA bundle



159
160
161
162
163
164
# File 'lib/puppet/x509/cert_provider.rb', line 159

def ca_last_update
  stat = Puppet::FileSystem.stat(@capath)
  Time.at(stat.mtime)
rescue Errno::ENOENT
  nil
end

#ca_last_update=(time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set the CA bundle last updated time.

Parameters:

  • time (Time)

    The last updated time



171
172
173
# File 'lib/puppet/x509/cert_provider.rb', line 171

def ca_last_update=(time)
  Puppet::FileSystem.touch(@capath, mtime: time)
end

#create_request(name, private_key) ⇒ Puppet::X509::Request

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create a certificate signing request (CSR).

Parameters:

Returns:

  • (Puppet::X509::Request)

    The request



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/puppet/x509/cert_provider.rb', line 306

def create_request(name, private_key)
  options = {}

  if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != ''
    options[:dns_alt_names] = Puppet[:dns_alt_names]
  end

  csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
  if csr_attributes.load
    options[:csr_attributes] = csr_attributes.custom_attributes
    options[:extension_requests] = csr_attributes.extension_requests
  end

  # Adds auto-renew attribute to CSR if the agent supports auto-renewal of
  # certificates
  if Puppet[:hostcert_renewal_interval] && Puppet[:hostcert_renewal_interval] > 0
    options[:csr_attributes] ||= {}
    options[:csr_attributes].merge!({ '1.3.6.1.4.1.34380.1.3.2' => 'true' })
  end

  csr = Puppet::SSL::CertificateRequest.new(name)
  csr.generate(private_key, options)
end

#crl_last_updateTime?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return the time when the CRL was last updated.

Returns:

  • (Time, nil)

    Time when the CRL was last updated, or nil if we don’t have a CRL



137
138
139
140
141
142
# File 'lib/puppet/x509/cert_provider.rb', line 137

def crl_last_update
  stat = Puppet::FileSystem.stat(@crlpath)
  Time.at(stat.mtime)
rescue Errno::ENOENT
  nil
end

#crl_last_update=(time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set the CRL last updated time.

Parameters:

  • time (Time)

    The last updated time



149
150
151
# File 'lib/puppet/x509/cert_provider.rb', line 149

def crl_last_update=(time)
  Puppet::FileSystem.touch(@crlpath, mtime: time)
end

#delete_request(name) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • name (String)

    The request identity

Returns:

  • (Boolean)

    true if the CSR was deleted



366
367
368
369
370
371
# File 'lib/puppet/x509/cert_provider.rb', line 366

def delete_request(name)
  path = to_path(@requestdir, name)
  delete_pem(path)
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to delete certificate request for '%{name}'") % { name: name }, e)
end

#load_cacerts(required: false) ⇒ Array<OpenSSL::X509::Certificate>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load CA certs from the configured ‘capath`.

Parameters:

  • required (Boolean) (defaults to: false)

    If true, raise if they are missing

Returns:

Raises:

  • (Puppet::Error)

    if the certs cannot be loaded

  • (OpenSSL::X509::CertificateError)

    The ‘pem` text does not contain a valid cert



57
58
59
60
61
62
63
64
65
66
# File 'lib/puppet/x509/cert_provider.rb', line 57

def load_cacerts(required: false)
  pem = load_pem(@capath)
  if !pem && required
    raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath }
  end

  pem ? load_cacerts_from_pem(pem) : nil
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % { capath: @capath }, e)
end

#load_cacerts_from_pem(pem) ⇒ Array<OpenSSL::X509::Certificate>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load PEM encoded CA certificates.

Parameters:

  • pem (String)

    PEM encoded certificate(s)

Returns:

Raises:

  • (OpenSSL::X509::CertificateError)

    The ‘pem` text does not contain a valid cert



75
76
77
78
79
80
81
82
# File 'lib/puppet/x509/cert_provider.rb', line 75

def load_cacerts_from_pem(pem)
  # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
  raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS

  pem.scan(CERT_DELIMITERS).map do |text|
    OpenSSL::X509::Certificate.new(text)
  end
end

#load_client_cert(name, required: false) ⇒ OpenSSL::X509::Request

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • name (String)

    The client cert identity

  • required (Boolean) (defaults to: false)

    If true, raise it is missing

Returns:

Raises:

  • (Puppet::Error)

    if the client cert cannot be loaded

  • (OpenSSL::X509::CertificateError)

    The ‘pem` text does not contain a valid cert



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/puppet/x509/cert_provider.rb', line 276

def load_client_cert(name, required: false)
  path = @hostcert || to_path(@certdir, name)
  pem = load_pem(path)
  if !pem && required
    raise Puppet::Error, _("The client certificate is missing from '%{path}'") % { path: path }
  end

  pem ? load_client_cert_from_pem(pem) : nil
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to load client certificate for '%{name}'") % { name: name }, e)
end

#load_client_cert_from_pem(pem) ⇒ OpenSSL::X509::Certificate

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load a PEM encoded certificate.

Parameters:

  • pem (String)

    PEM encoded cert

Returns:

Raises:

  • (OpenSSL::X509::CertificateError)

    The ‘pem` text does not contain a valid cert



295
296
297
# File 'lib/puppet/x509/cert_provider.rb', line 295

def load_client_cert_from_pem(pem)
  OpenSSL::X509::Certificate.new(pem)
end

#load_crls(required: false) ⇒ Array<OpenSSL::X509::CRL>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load CRLs from the configured ‘crlpath` path.

Parameters:

  • required (Boolean) (defaults to: false)

    If true, raise if they are missing

Returns:

  • (Array<OpenSSL::X509::CRL>)

    Array of CRLs

Raises:

  • (Puppet::Error)

    if the CRLs cannot be loaded

  • (OpenSSL::X509::CRLError)

    The ‘pem` text does not contain a valid CRL



104
105
106
107
108
109
110
111
112
113
# File 'lib/puppet/x509/cert_provider.rb', line 104

def load_crls(required: false)
  pem = load_pem(@crlpath)
  if !pem && required
    raise Puppet::Error, _("The CRL is missing from '%{path}'") % { path: @crlpath }
  end

  pem ? load_crls_from_pem(pem) : nil
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to load CRLs from '%{crlpath}'") % { crlpath: @crlpath }, e)
end

#load_crls_from_pem(pem) ⇒ Array<OpenSSL::X509::CRL>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load PEM encoded CRL(s).

Parameters:

  • pem (String)

    PEM encoded CRL(s)

Returns:

  • (Array<OpenSSL::X509::CRL>)

    Array of CRLs

Raises:

  • (OpenSSL::X509::CRLError)

    The ‘pem` text does not contain a valid CRL



122
123
124
125
126
127
128
129
# File 'lib/puppet/x509/cert_provider.rb', line 122

def load_crls_from_pem(pem)
  # TRANSLATORS 'PEM' is an acronym and shouldn't be translated
  raise OpenSSL::X509::CRLError, _("Failed to parse CRLs as PEM") if pem !~ CRL_DELIMITERS

  pem.scan(CRL_DELIMITERS).map do |text|
    OpenSSL::X509::CRL.new(text)
  end
end

#load_private_key(name, required: false, password: nil) ⇒ OpenSSL::PKey::RSA, OpenSSL::PKey::EC

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • name (String)

    The private key identity

  • required (Boolean) (defaults to: false)

    If true, raise if it is missing

  • password (String, nil) (defaults to: 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.

Returns:

Raises:

  • (Puppet::Error)

    if the private key cannot be loaded

  • (OpenSSL::PKey::PKeyError)

    The ‘pem` text does not contain a valid key



212
213
214
215
216
217
218
219
220
221
222
# File 'lib/puppet/x509/cert_provider.rb', line 212

def load_private_key(name, required: false, password: nil)
  path = @hostprivkey || to_path(@privatekeydir, name)
  pem = load_pem(path)
  if !pem && required
    raise Puppet::Error, _("The private key is missing from '%{path}'") % { path: path }
  end

  pem ? load_private_key_from_pem(pem, password: password) : nil
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to load private key for '%{name}'") % { name: name }, e)
end

#load_private_key_from_pem(pem, password: nil) ⇒ OpenSSL::PKey::RSA, OpenSSL::PKey::EC

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load a PEM encoded private key.

Parameters:

  • pem (String)

    PEM encoded private key

  • password (String, nil) (defaults to: 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.

Returns:

Raises:

  • (OpenSSL::PKey::PKeyError)

    The ‘pem` text does not contain a valid key



234
235
236
237
238
239
# File 'lib/puppet/x509/cert_provider.rb', line 234

def load_private_key_from_pem(pem, password: nil)
  # set a non-nil password to ensure openssl doesn't prompt
  password ||= ''

  OpenSSL::PKey.read(pem, password)
end

#load_private_key_passwordString?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load the private key password.

Returns:

  • (String, nil)

    The private key password as a binary string or nil if there is none.



247
248
249
250
251
# File 'lib/puppet/x509/cert_provider.rb', line 247

def load_private_key_password
  Puppet::FileSystem.read(Puppet[:passfile], :encoding => Encoding::BINARY)
rescue Errno::ENOENT
  nil
end

#load_request(name) ⇒ OpenSSL::X509::Request

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • name (String)

    The request identity

Returns:

Raises:

  • (Puppet::Error)

    if the cert request cannot be saved

  • (OpenSSL::X509::RequestError)

    The ‘pem` text does not contain a valid request



352
353
354
355
356
357
358
# File 'lib/puppet/x509/cert_provider.rb', line 352

def load_request(name)
  path = to_path(@requestdir, name)
  pem = load_pem(path)
  pem ? load_request_from_pem(pem) : nil
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to load certificate request for '%{name}'") % { name: name }, e)
end

#load_request_from_pem(pem) ⇒ OpenSSL::X509::Request

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load a PEM encoded certificate signing request (CSR).

Parameters:

  • pem (String)

    PEM encoded request

Returns:

Raises:

  • (OpenSSL::X509::RequestError)

    The ‘pem` text does not contain a valid request



380
381
382
# File 'lib/puppet/x509/cert_provider.rb', line 380

def load_request_from_pem(pem)
  OpenSSL::X509::Request.new(pem)
end

#save_cacerts(certs) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Save ‘certs` to the configured `capath`.

Parameters:

Raises:



43
44
45
46
47
# File 'lib/puppet/x509/cert_provider.rb', line 43

def save_cacerts(certs)
  save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert))
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to save CA certificates to '%{capath}'") % { capath: @capath }, e)
end

#save_client_cert(name, cert) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

Raises:



260
261
262
263
264
265
# File 'lib/puppet/x509/cert_provider.rb', line 260

def save_client_cert(name, cert)
  path = @hostcert || to_path(@certdir, name)
  save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert))
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to save client certificate for '%{name}'") % { name: name }, e)
end

#save_crls(crls) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Save ‘crls` to the configured `crlpath`.

Parameters:

  • crls (Array<OpenSSL::X509::CRL>)

    Array of CRLs to save

Raises:



90
91
92
93
94
# File 'lib/puppet/x509/cert_provider.rb', line 90

def save_crls(crls)
  save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % { crlpath: @crlpath }, e)
end

#save_private_key(name, key, password: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • name (String)

    The private key identity

  • key (OpenSSL::PKey::RSA)

    private key

  • password (String, nil) (defaults to: 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.

Raises:



186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/puppet/x509/cert_provider.rb', line 186

def save_private_key(name, key, password: nil)
  pem = if password
          cipher = OpenSSL::Cipher.new('aes-128-cbc')
          key.export(cipher, password)
        else
          key.to_pem
        end
  path = @hostprivkey || to_path(@privatekeydir, name)
  save_pem(pem, path, **permissions_for_setting(:hostprivkey))
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to save private key for '%{name}'") % { name: name }, e)
end

#save_request(name, csr) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

Raises:



337
338
339
340
341
342
# File 'lib/puppet/x509/cert_provider.rb', line 337

def save_request(name, csr)
  path = to_path(@requestdir, name)
  save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr))
rescue SystemCallError => e
  raise Puppet::Error.new(_("Failed to save certificate request for '%{name}'") % { name: name }, e)
end

#to_path(base, name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return the path to the cert related object (key, CSR, cert, etc).

Parameters:

  • base (String)

    base directory

  • name (String)

    the name associated with the cert related object



388
389
390
391
392
# File 'lib/puppet/x509/cert_provider.rb', line 388

def to_path(base, name)
  raise _("Certname %{name} must not contain unprintable or non-ASCII characters") % { name: name.inspect } unless name =~ VALID_CERTNAME

  File.join(base, "#{name.downcase}.pem")
end