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.

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
EC_HEADER =

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 EC PRIVATE KEY-----/

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.


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/puppet/x509/cert_provider.rb', line 15

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


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

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


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

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


132
133
134
# File 'lib/puppet/x509/cert_provider.rb', line 132

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


322
323
324
325
326
327
# File 'lib/puppet/x509/cert_provider.rb', line 322

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


49
50
51
52
53
54
55
56
57
# File 'lib/puppet/x509/cert_provider.rb', line 49

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


65
66
67
68
69
70
71
72
# File 'lib/puppet/x509/cert_provider.rb', line 65

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


246
247
248
249
250
251
252
253
254
255
# File 'lib/puppet/x509/cert_provider.rb', line 246

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


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

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


92
93
94
95
96
97
98
99
100
# File 'lib/puppet/x509/cert_provider.rb', line 92

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


108
109
110
111
112
113
114
115
# File 'lib/puppet/x509/cert_provider.rb', line 108

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


171
172
173
174
175
176
177
178
179
180
# File 'lib/puppet/x509/cert_provider.rb', line 171

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


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/puppet/x509/cert_provider.rb', line 191

def load_private_key_from_pem(pem, password: nil)
  # set a non-nil password to ensure openssl doesn't prompt
  # but ruby 2.4.0 & 2.4.1 require at least 4 bytes due to
  # https://github.com/ruby/openssl/commit/f38501249f33bff7ca9d208670b8cde695ea8b7b
  # and corrected in https://github.com/ruby/openssl/commit/a896c3d1dfa090e92dec1abf8ac12843af6af721
  password ||= '    '

  # Can't use OpenSSL::PKey.read, because it's broken in MRI 2.3, doesn't exist
  # in JRuby 9.1, and is broken in JRuby 9.2
  begin
    if pem =~ EC_HEADER
      OpenSSL::PKey::EC.new(pem, password)
    else
      OpenSSL::PKey::RSA.new(pem, password)
    end
  rescue OpenSSL::PKey::PKeyError => e
    if e.message =~ /Neither PUB key nor PRIV key/
      raise OpenSSL::PKey::PKeyError, "Could not parse PKey: no start line"
    else
      raise e
    end
  end
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.


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

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


310
311
312
313
314
315
316
# File 'lib/puppet/x509/cert_provider.rb', line 310

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


335
336
337
# File 'lib/puppet/x509/cert_provider.rb', line 335

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:


36
37
38
39
40
# File 'lib/puppet/x509/cert_provider.rb', line 36

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:


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

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:


79
80
81
82
83
# File 'lib/puppet/x509/cert_provider.rb', line 79

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:


146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/puppet/x509/cert_provider.rb', line 146

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:


296
297
298
299
300
301
# File 'lib/puppet/x509/cert_provider.rb', line 296

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