class Puppetserver::Ca::LocalCertificateAuthority

Constants

CA_EXTENSIONS
CERT_VALID_FROM

Make the certificate valid as of yesterday, because so many people's clocks are out of sync. This gives one more day of validity than people might expect, but is better than making every person who has a messed up clock fail, and better than having every cert we generate expire a day before the user expected it to when they asked for “one year”.

CLI_AUTH_EXT_OID
MASTER_EXTENSIONS
SSL_CLIENT_CERT
SSL_SERVER_CERT

Attributes

cert[R]
cert_bundle[R]
crl[R]
crl_chain[R]
key[R]

Public Class Methods

new(digest, settings) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 42
def initialize(digest, settings)
  @digest = digest
  @host = Host.new(digest)
  @settings = settings
  @errors = []

  if ssl_assets_exist?
    loader = Puppetserver::Ca::X509Loader.new(@settings[:cacert], @settings[:cakey], @settings[:cacrl])
    if loader.errors.empty?
      load_ssl_components(loader)
    else
      @errors += loader.errors
      @errors << "CA not initialized. Please set up your CA before attempting to generate certs offline."
    end
  end
end

Public Instance Methods

add_authorized_extensions(cert, ef) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 178
def add_authorized_extensions(cert, ef)
  MASTER_EXTENSIONS.each do |ext|
    extension = ef.create_extension(*ext)
    cert.add_extension(extension)
  end

  # Status API access for the CA CLI
  cli_auth_ext = OpenSSL::X509::Extension.new(CLI_AUTH_EXT_OID, OpenSSL::ASN1::UTF8String.new("true").to_der, false)
  cert.add_extension(cli_auth_ext)
end
add_custom_extensions(cert) click to toggle source

This takes all the extension requests from csr_attributes.yaml and adds those to the cert

# File lib/puppetserver/ca/local_certificate_authority.rb, line 196
def add_custom_extensions(cert)
  extension_requests = @host.get_extension_requests(@settings[:csr_attributes])

  if extension_requests
    extensions = @host.validated_extensions(extension_requests)
    extensions.each do |ext|
      cert.add_extension(ext)
    end
  end

  @host.errors.empty?
end
add_subject_alt_names_extension(alt_names, cert, ef) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 189
def add_subject_alt_names_extension(alt_names, cert, ef)
  alt_names_ext = ef.create_extension("subjectAltName", alt_names, false)
  cert.add_extension(alt_names_ext)
end
create_crl_for(cert, key) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 240
def create_crl_for(cert, key)
  crl = OpenSSL::X509::CRL.new
  crl.version = 1
  crl.issuer = cert.subject

  ef = extension_factory_for(cert)
  crl.add_extension(
    ef.create_extension(["authorityKeyIdentifier", "keyid:always", false]))
  crl.add_extension(
    OpenSSL::X509::Extension.new("crlNumber", OpenSSL::ASN1::Integer(0)))

  crl.last_update = CERT_VALID_FROM
  crl.next_update = valid_until
  crl.sign(key, @digest)

  crl
end
create_intermediate_cert(root_key, root_cert) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 258
def create_intermediate_cert(root_key, root_cert)
  @key = @host.create_private_key(@settings[:keylength])
  int_csr = @host.create_csr(name: @settings[:ca_name], key: @key)
  @cert = sign_intermediate(root_key, root_cert, int_csr)
  @crl = create_crl_for(@cert, @key)

  return nil
end
create_master_cert() click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 135
def create_master_cert
  master_cert = nil
  master_key = @host.create_private_key(@settings[:keylength],
                                        @settings[:hostprivkey],
                                        @settings[:hostpubkey])
  if master_key
    master_csr = @host.create_csr(name: @settings[:certname], key: master_key)
    if @settings[:subject_alt_names].empty?
      alt_names = "DNS:puppet, DNS:#{@settings[:certname]}"
    else
      alt_names = @settings[:subject_alt_names]
    end

    master_cert = sign_authorized_cert(master_csr, alt_names)
  end

  return master_key, master_cert
end
create_root_cert() click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 209
def create_root_cert
  root_key = @host.create_private_key(@settings[:keylength])
  root_cert = self_signed_ca(root_key)
  root_crl = create_crl_for(root_cert, root_key)

  return root_key, root_cert, root_crl
end
errors() click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 102
def errors
  @errors += @host.errors
end
extension_factory_for(ca, cert = nil) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 110
def extension_factory_for(ca, cert = nil)
  ef = OpenSSL::X509::ExtensionFactory.new
  ef.issuer_certificate  = ca
  ef.subject_certificate = cert if cert

  ef
end
format_time(time) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 131
def format_time(time)
  time.strftime('%Y-%m-%dT%H:%M:%S%Z')
end
initialize_ssl_components(loader) click to toggle source

Initialize SSL state

This method is similar to {#load_ssl_components}, but has extra logic for initializing components that may not be present when the CA is set up for the first time. For example, SSL components provided by an external CA will often not include a pre-generated leaf CRL.

@note Check {#errors} after calling this method for issues that

may have occurred during initialization.

@param loader [Puppetserver::Ca::X509Loader] @return [void]

# File lib/puppetserver/ca/local_certificate_authority.rb, line 86
def initialize_ssl_components(loader)
  @cert_bundle = loader.certs
  @key = loader.key
  @cert = loader.cert

  if loader.crl.nil?
    loader.crl = create_crl_for(@cert, @key)

    loader.validate_full_chain(@cert_bundle, loader.crls)
    @errors += loader.errors
  end

  @crl_chain = loader.crls
  @crl = loader.crl
end
inventory_entry(cert) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 118
def inventory_entry(cert)
  "0x%04x %s %s %s" % [cert.serial, format_time(cert.not_before),
                       format_time(cert.not_after), cert.subject]
end
load_ssl_components(loader) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 65
def load_ssl_components(loader)
  @cert_bundle = loader.certs
  @key = loader.key
  @cert = loader.cert
  @crl_chain = loader.crls
  @crl = loader.crl
end
next_serial(serial_file) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 123
def next_serial(serial_file)
  if File.exist?(serial_file)
    File.read(serial_file).to_i(16)
  else
    1
  end
end
self_signed_ca(key) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 217
def self_signed_ca(key)
  cert = OpenSSL::X509::Certificate.new

  cert.public_key = key.public_key
  cert.subject = OpenSSL::X509::Name.new([["CN", @settings[:root_ca_name]]])
  cert.issuer = cert.subject
  cert.version = 2
  cert.serial = 1

  cert.not_before = CERT_VALID_FROM
  cert.not_after  = valid_until

  ef = extension_factory_for(cert, cert)
  CA_EXTENSIONS.each do |ext|
    extension = ef.create_extension(*ext)
    cert.add_extension(extension)
  end

  cert.sign(key, @digest)

  cert
end
sign_authorized_cert(csr, alt_names = '') click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 154
def sign_authorized_cert(csr, alt_names = '')
  cert = OpenSSL::X509::Certificate.new
  cert.public_key = csr.public_key
  cert.subject = csr.subject
  cert.issuer = @cert.subject
  cert.version = 2
  cert.serial = next_serial(@settings[:serial])
  cert.not_before = CERT_VALID_FROM
  cert.not_after = valid_until

  return unless add_custom_extensions(cert)

  ef = extension_factory_for(@cert, cert)
  add_authorized_extensions(cert, ef)

  if !alt_names.empty?
    add_subject_alt_names_extension(alt_names, cert, ef)
  end

  cert.sign(@key, @digest)

  cert
end
sign_intermediate(ca_key, ca_cert, csr) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 267
def sign_intermediate(ca_key, ca_cert, csr)
  cert = OpenSSL::X509::Certificate.new

  cert.public_key = csr.public_key
  cert.subject = csr.subject
  cert.issuer = ca_cert.subject
  cert.version = 2
  cert.serial = 2

  cert.not_before = CERT_VALID_FROM
  cert.not_after = valid_until

  ef = extension_factory_for(ca_cert, cert)
  CA_EXTENSIONS.each do |ext|
    extension = ef.create_extension(*ext)
    cert.add_extension(extension)
  end

  cert.sign(ca_key, @digest)

  cert
end
ssl_assets_exist?() click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 59
def ssl_assets_exist?
  File.exist?(@settings[:cacert]) &&
    File.exist?(@settings[:cakey]) &&
    File.exist?(@settings[:cacrl])
end
update_serial_file(serial) click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 290
def update_serial_file(serial)
  Puppetserver::Ca::Utils::FileSystem.write_file(@settings[:serial], serial.to_s(16), 0644)
end
valid_until() click to toggle source
# File lib/puppetserver/ca/local_certificate_authority.rb, line 106
def valid_until
  Time.now + @settings[:ca_ttl]
end