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.



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

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

#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



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/puppet/x509/cert_provider.rb', line 278

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

  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



133
134
135
136
137
138
# File 'lib/puppet/x509/cert_provider.rb', line 133

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



145
146
147
# File 'lib/puppet/x509/cert_provider.rb', line 145

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



331
332
333
334
335
336
# File 'lib/puppet/x509/cert_provider.rb', line 331

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



55
56
57
58
59
60
61
62
63
# File 'lib/puppet/x509/cert_provider.rb', line 55

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



72
73
74
75
76
77
78
79
# File 'lib/puppet/x509/cert_provider.rb', line 72

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



249
250
251
252
253
254
255
256
257
258
# File 'lib/puppet/x509/cert_provider.rb', line 249

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



267
268
269
# File 'lib/puppet/x509/cert_provider.rb', line 267

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



101
102
103
104
105
106
107
108
109
# File 'lib/puppet/x509/cert_provider.rb', line 101

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



118
119
120
121
122
123
124
125
# File 'lib/puppet/x509/cert_provider.rb', line 118

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



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

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



207
208
209
210
211
212
# File 'lib/puppet/x509/cert_provider.rb', line 207

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.



220
221
222
223
224
# File 'lib/puppet/x509/cert_provider.rb', line 220

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



317
318
319
320
321
322
323
# File 'lib/puppet/x509/cert_provider.rb', line 317

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



345
346
347
# File 'lib/puppet/x509/cert_provider.rb', line 345

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:



41
42
43
44
45
# File 'lib/puppet/x509/cert_provider.rb', line 41

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:



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

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:



87
88
89
90
91
# File 'lib/puppet/x509/cert_provider.rb', line 87

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:



160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/puppet/x509/cert_provider.rb', line 160

def save_private_key(name, key, password: nil)
  pem = if password
          cipher = OpenSSL::Cipher::AES.new(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:



302
303
304
305
306
307
# File 'lib/puppet/x509/cert_provider.rb', line 302

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



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

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