Class: Puppetserver::Ca::X509Loader
- Inherits:
-
Object
- Object
- Puppetserver::Ca::X509Loader
- Defined in:
- lib/puppetserver/ca/x509_loader.rb
Overview
Load, validate, and store x509 objects needed by the Puppet Server CA.
Instance Attribute Summary collapse
-
#cert ⇒ Object
readonly
Returns the value of attribute cert.
-
#certs ⇒ Object
readonly
Returns the value of attribute certs.
-
#crl ⇒ Object
Returns the value of attribute crl.
-
#crls ⇒ Object
readonly
Returns the value of attribute crls.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
Instance Method Summary collapse
-
#find_leaf_crl ⇒ OpenSSL::X509::CRL?
Find a CRL in the chain issued by the signing cert.
- #find_signing_cert ⇒ Object
-
#initialize(bundle_path, key_path, chain_path) ⇒ X509Loader
constructor
A new instance of X509Loader.
- #load_certs(bundle_path) ⇒ Object
- #load_crls(chain_path) ⇒ Object
- #load_key(key_path) ⇒ Object
-
#validate(bundle, pkey, chain) ⇒ Object
Only do as much validation as is possible, assume whoever tried to load the objects wrote errors about any invalid ones, but that bundle and chain may be empty arrays and pkey may be nil.
- #validate_cert_and_key(key, cert) ⇒ Object
- #validate_crl_and_cert(crl, cert) ⇒ Object
-
#validate_full_chain(certs, crls) ⇒ Object
By creating an X509::Store and validating the leaf cert with it we: - Ensure a full chain of trust (root to leaf) is within the bundle - If provided, there are CRLs for the CAs - If provided, no CAs within the chain of trust have been revoked However this does allow for: - Additional, ignored, certs and CRLs in the bundle/chain - certs and CRLs in any order.
Constructor Details
#initialize(bundle_path, key_path, chain_path) ⇒ X509Loader
Returns a new instance of X509Loader.
10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 10 def initialize(bundle_path, key_path, chain_path) @errors = [] @certs = load_certs(bundle_path) @key = load_key(key_path) @crls = load_crls(chain_path) @cert = find_signing_cert @crl = find_leaf_crl validate(@certs, @key, @crls) end |
Instance Attribute Details
#cert ⇒ Object (readonly)
Returns the value of attribute cert.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def cert @cert end |
#certs ⇒ Object (readonly)
Returns the value of attribute certs.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def certs @certs end |
#crl ⇒ Object
Returns the value of attribute crl.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def crl @crl end |
#crls ⇒ Object (readonly)
Returns the value of attribute crls.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def crls @crls end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def errors @errors end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
8 9 10 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 8 def key @key end |
Instance Method Details
#find_leaf_crl ⇒ OpenSSL::X509::CRL?
Find a CRL in the chain issued by the signing cert
40 41 42 43 44 45 46 47 48 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 40 def find_leaf_crl return if @crls.empty? || @cert.nil? leaf_crl = @crls.find do |crl| crl.issuer == @cert.subject end leaf_crl end |
#find_signing_cert ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 22 def find_signing_cert return if @key.nil? || @certs.empty? signing_cert = @certs.find do |cert| cert.check_private_key(@key) end if signing_cert.nil? @errors << "Could not find certificate matching private key" end signing_cert end |
#load_certs(bundle_path) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 67 def load_certs(bundle_path) certs, errs = [], [] bundle_string = File.read(bundle_path) cert_strings = bundle_string.scan(/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m) cert_strings.each do |cert_string| begin certs << OpenSSL::X509::Certificate.new(cert_string) rescue OpenSSL::X509::CertificateError errs << "Could not parse entry:\n#{cert_string}" end end if certs.empty? errs << "Could not detect any certs within #{bundle_path}" end unless errs.empty? @errors << "Could not parse #{bundle_path}" @errors += errs end return certs end |
#load_crls(chain_path) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 102 def load_crls(chain_path) errs, crls = [], [] chain_string = File.read(chain_path) crl_strings = chain_string.scan(/-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m) crl_strings.map do |crl_string| begin crls << OpenSSL::X509::CRL.new(crl_string) rescue OpenSSL::X509::CRLError errs << "Could not parse entry:\n#{crl_string}" end end if crls.empty? errs << "Could not detect any crls within #{chain_path}" end unless errs.empty? @errors << "Could not parse #{chain_path}" @errors += errs end return crls end |
#load_key(key_path) ⇒ Object
92 93 94 95 96 97 98 99 100 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 92 def load_key(key_path) begin OpenSSL::PKey.read(File.read(key_path)) rescue ArgumentError, OpenSSL::PKey::PKeyError => e @errors << "Could not parse #{key_path}" return nil end end |
#validate(bundle, pkey, chain) ⇒ Object
Only do as much validation as is possible, assume whoever tried to load the objects wrote errors about any invalid ones, but that bundle and chain may be empty arrays and pkey may be nil.
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 53 def validate(bundle, pkey, chain) if !@crl.nil? && !@cert.nil? validate_crl_and_cert(@crl, @cert) end if pkey && !@cert.nil? validate_cert_and_key(pkey, @cert) end unless bundle.empty? || @cert.nil? || @crl.nil? validate_full_chain(bundle, chain) end end |
#validate_cert_and_key(key, cert) ⇒ Object
136 137 138 139 140 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 136 def validate_cert_and_key(key, cert) unless cert.check_private_key(key) @errors << 'Private key and certificate do not match' end end |
#validate_crl_and_cert(crl, cert) ⇒ Object
142 143 144 145 146 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 142 def validate_crl_and_cert(crl, cert) unless crl.issuer == cert.subject @errors << 'Leaf CRL was not issued by leaf certificate' end end |
#validate_full_chain(certs, crls) ⇒ Object
By creating an X509::Store and validating the leaf cert with it we:
- Ensure a full chain of trust (root to leaf) is within the bundle
- If provided, there are CRLs for the CAs
- If provided, no CAs within the chain of trust have been revoked
However this does allow for:
- Additional, ignored, certs and CRLs in the bundle/chain
- certs and CRLs in any order
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/puppetserver/ca/x509_loader.rb', line 155 def validate_full_chain(certs, crls) store = OpenSSL::X509::Store.new certs.each {|cert| store.add_cert(cert) } if !crls.empty? store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::V_FLAG_CRL_CHECK_ALL crls.each {|crl| store.add_crl(crl) } end unless store.verify(@cert) @errors << 'Leaf certificate could not be validated' @errors << "Validating cert store returned: #{store.error} - #{store.error_string}" end end |