class Puppetserver::Ca::Host
Constants
- PRIVATE_CSR_ATTRIBUTES
Exclude OIDs that may conflict with how Puppet creates CSRs.
We only have nominal support for Microsoft extension requests, but since we ultimately respect that field when looking for extension requests in a CSR we need to prevent that field from being written to directly.
- PRIVATE_EXTENSIONS
- PUPPET_SHORT_NAMES
A mapping of Puppet extension short names to their OIDs. These appear in csr_attributes.yaml.
Attributes
errors[R]
Public Class Methods
new(digest)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 56 def initialize(digest) @digest = digest @errors = [] end
Public Instance Methods
add_csr_attributes(csr, csr_attributes)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 128 def add_csr_attributes(csr, csr_attributes) if csr_attributes csr_attributes.each do |oid, value| begin if PRIVATE_CSR_ATTRIBUTES.include? oid @errors << "Cannot specify CSR attribute #{oid}: conflicts with internally used CSR attribute" end oid = PUPPET_SHORT_NAMES[oid] || oid encoded = OpenSSL::ASN1::PrintableString.new(value.to_s) attr_set = OpenSSL::ASN1::Set.new([encoded]) csr.add_attribute(OpenSSL::X509::Attribute.new(oid, attr_set)) rescue OpenSSL::X509::AttributeError => e @errors << "Cannot create CSR with attribute #{oid}: #{e.message}" end end end end
add_csr_extensions(csr, extension_requests, cli_extensions)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 147 def add_csr_extensions(csr, extension_requests, cli_extensions) if extension_requests || cli_extensions.any? extensions = if extension_requests validated_extensions(extension_requests) + cli_extensions else cli_extensions end csr.add_attribute(extension_attribute(extensions)) end end
create_csr(name:, key:, cli_extensions: [], csr_attributes_path: '')
click to toggle source
# File lib/puppetserver/ca/host.rb, line 79 def create_csr(name:, key:, cli_extensions: [], csr_attributes_path: '') csr = OpenSSL::X509::Request.new csr.public_key = key.public_key csr.subject = OpenSSL::X509::Name.new([["CN", name]]) csr.version = 2 custom_attributes = get_custom_attributes(csr_attributes_path) extension_requests = get_extension_requests(csr_attributes_path) add_csr_attributes(csr, custom_attributes) add_csr_extensions(csr, extension_requests, cli_extensions) csr.sign(key, @digest) if @errors.empty? csr end
create_private_key(keylength, private_path = '', public_path = '')
click to toggle source
If both the private and public keys exist for a server then we want to honor them here, if only one key exists we want to surface an error, and if neither exist we generate a new key. This logic is necessary for proper bootstrapping for certain server workflows.
# File lib/puppetserver/ca/host.rb, line 65 def create_private_key(keylength, private_path = '', public_path = '') if File.exist?(private_path) && File.exist?(public_path) return OpenSSL::PKey.read(File.read(private_path)) elsif !File.exist?(private_path) && !File.exist?(public_path) return OpenSSL::PKey::RSA.new(keylength) elsif !File.exist?(private_path) && File.exist?(public_path) @errors << "Missing private key to match public key at #{public_path}" return nil elsif File.exist?(private_path) && !File.exist?(public_path) @errors << "Missing public key to match private key at #{private_path}" return nil end end
extension_attribute(extensions)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 96 def extension_attribute(extensions) seq = OpenSSL::ASN1::Sequence(extensions) ext_req = OpenSSL::ASN1::Set([seq]) OpenSSL::X509::Attribute.new("extReq", ext_req) end
get_custom_attributes(attributes_path)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 102 def get_custom_attributes(attributes_path) if csr_attributes = load_csr_attributes(attributes_path) csr_attributes['custom_attributes'] end end
get_extension_requests(attributes_path)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 108 def get_extension_requests(attributes_path) if csr_attributes = load_csr_attributes(attributes_path) csr_attributes['extension_requests'] end end
load_csr_attributes(attributes_path)
click to toggle source
This loads all the custom_attributes and extension requests from the csr_attributes.yaml
# File lib/puppetserver/ca/host.rb, line 116 def load_csr_attributes(attributes_path) @custom_csr_attributes ||= if File.exist?(attributes_path) yaml = YAML.load_file(attributes_path) if !yaml.is_a?(Hash) @errors << "Invalid CSR attributes, expected instance of Hash, received instance of #{yaml.class}" return end yaml end end
validated_extensions(extension_requests)
click to toggle source
# File lib/puppetserver/ca/host.rb, line 159 def validated_extensions(extension_requests) extensions = [] extension_requests.each do |oid, value| begin if PRIVATE_EXTENSIONS.include? oid @errors << "Cannot specify CSR extension request #{oid}: conflicts with internally used extension request" end oid = PUPPET_SHORT_NAMES[oid] || oid ext = OpenSSL::X509::Extension.new(oid, OpenSSL::ASN1::UTF8String.new(value.to_s).to_der, false) extensions << ext rescue OpenSSL::X509::ExtensionError => e @errors << "Cannot create CSR with extension request #{oid}: #{e.message}" end end extensions end