diff options
author | Luke Kanies <luke@madstop.com> | 2008-04-19 14:50:18 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-04-19 14:50:18 -0500 |
commit | 809fc77bc767fb3acabc83d55183686200b1e384 (patch) | |
tree | 26f0fa4954f693168f7f366c5ea8653531de3ac6 /lib/puppet | |
parent | 16056a24c65a7c6485b65f15700ff3971781031b (diff) | |
download | puppet-809fc77bc767fb3acabc83d55183686200b1e384.tar.gz puppet-809fc77bc767fb3acabc83d55183686200b1e384.tar.xz puppet-809fc77bc767fb3acabc83d55183686200b1e384.zip |
Finishing the interface between the CA and the CRL.
Certificate revocation now works, the CA knows how
to generate the CRL, and the SSL::Host class knows
how to configure the CRL class for indirection.
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/ssl/certificate_authority.rb | 140 | ||||
-rw-r--r-- | lib/puppet/ssl/certificate_revocation_list.rb | 4 | ||||
-rw-r--r-- | lib/puppet/ssl/host.rb | 23 | ||||
-rw-r--r-- | lib/puppet/ssl/inventory.rb | 11 |
4 files changed, 128 insertions, 50 deletions
diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 7b30e08f7..62e799ef6 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -12,8 +12,41 @@ require 'puppet/ssl/certificate_request' class Puppet::SSL::CertificateAuthority require 'puppet/ssl/certificate_factory' require 'puppet/ssl/inventory' + require 'puppet/ssl/certificate_revocation_list' - attr_reader :name, :host, :inventory + attr_reader :name, :host + + # Retrieve (or create, if necessary) the certificate revocation list. + def crl + unless defined?(@crl) + # The crl is disabled. + if ["false", false].include?(Puppet[:cacrl]) + @crl = nil + return @crl + end + + unless @crl = Puppet::SSL::CertificateRevocationList.find("whatever") + @crl = Puppet::SSL::CertificateRevocationList.new("whatever") + @crl.generate(host.certificate.content) + end + end + @crl + end + + # Delegate this to our Host class. + def destroy(name) + Puppet::SSL::Host.destroy(name) + end + + # Generate a new certificate. + def generate(name) + raise ArgumentError, "A Certificate already exists for %s" % name if Puppet::SSL::Certificate.find(name) + host = Puppet::SSL::Host.new(name) + + host.generate_certificate_request + + sign(name) + end # Generate our CA certificate. def generate_ca_certificate @@ -37,41 +70,14 @@ class Puppet::SSL::CertificateAuthority @name = Puppet[:certname] @host = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name) - - @inventory = Puppet::SSL::Inventory.new end - # Sign a given certificate request. - def sign(hostname, cert_type = :server, self_signing_csr = nil) - # This is a self-signed certificate - if self_signing_csr - csr = self_signing_csr - issuer = csr.content - else - generate_ca_certificate unless host.certificate - - unless csr = Puppet::SSL::CertificateRequest.find(hostname) - raise ArgumentError, "Could not find certificate request for %s" % hostname - end - issuer = host.certificate.content + # Retrieve (or create, if necessary) our inventory manager. + def inventory + unless defined?(@inventory) + @inventory = Puppet::SSL::Inventory.new end - - cert = Puppet::SSL::Certificate.new(hostname) - cert.content = Puppet::SSL::CertificateFactory.new(cert_type, csr.content, issuer, next_serial).result - cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new) - - Puppet.notice "Signed certificate request for %s" % hostname - - # Add the cert to the inventory before we save it, since - # otherwise we could end up with it being duplicated, if - # this is the first time we build the inventory file. - inventory.add(cert) - - # Save the now-signed cert. This should get routed correctly depending - # on the certificate type. - cert.save - - return cert + @inventory end # Generate a new password for the CA. @@ -112,4 +118,72 @@ class Puppet::SSL::CertificateAuthority def password? FileTest.exist? Puppet[:capass] end + + # Revoke a given certificate. + def revoke(name) + raise ArgumentError, "Cannot revoke certificates when the CRL is disabled" unless crl + + if cert = Puppet::SSL::Certificate.find(name) + serial = cert.content.serial + elsif ! serial = inventory.serial(name) + raise ArgumentError, "Could not find a serial number for %s" % name + end + crl.revoke(serial, host.key.content) + end + + # Sign a given certificate request. + def sign(hostname, cert_type = :server, self_signing_csr = nil) + # This is a self-signed certificate + if self_signing_csr + csr = self_signing_csr + issuer = csr.content + else + generate_ca_certificate unless host.certificate + + unless csr = Puppet::SSL::CertificateRequest.find(hostname) + raise ArgumentError, "Could not find certificate request for %s" % hostname + end + issuer = host.certificate.content + end + + cert = Puppet::SSL::Certificate.new(hostname) + cert.content = Puppet::SSL::CertificateFactory.new(cert_type, csr.content, issuer, next_serial).result + cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new) + + Puppet.notice "Signed certificate request for %s" % hostname + + # Add the cert to the inventory before we save it, since + # otherwise we could end up with it being duplicated, if + # this is the first time we build the inventory file. + inventory.add(cert) + + # Save the now-signed cert. This should get routed correctly depending + # on the certificate type. + cert.save + + # And remove the CSR if this wasn't self signed. + Puppet::SSL::CertificateRequest.destroy(csr.name) unless self_signing_csr + + return cert + end + + # Verify a given host's certificate. + def verify(name) + unless cert = Puppet::SSL::Certificate.find(name) + raise ArgumentError, "Could not find a certificate for %s" % name + end + store = OpenSSL::X509::Store.new + store.add_file Puppet[:cacert] + store.add_crl Puppet[:cacrl] if self.crl + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + + unless store.verify(cert.content) + raise "Certificate for %s failed verification" % name + end + end + + # List the waiting certificate requests. + def waiting? + Puppet::SSL::CertificateRequest.search("*").collect { |r| r.name } + end end diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb index aab1ec5ec..ca7b7db65 100644 --- a/lib/puppet/ssl/certificate_revocation_list.rb +++ b/lib/puppet/ssl/certificate_revocation_list.rb @@ -18,7 +18,9 @@ class Puppet::SSL::CertificateRevocationList < Puppet::SSL::Base @content end - def initialize + # The name doesn't actually matter; there's only one CRL. + # We just need the name so our Indirector stuff all works more easily. + def initialize(fakename) raise Puppet::Error, "Cannot manage the CRL when :cacrl is set to false" if [false, "false"].include?(Puppet[:cacrl]) @name = "crl" diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index 9c7ca767e..c1dac2050 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -2,6 +2,7 @@ require 'puppet/ssl' require 'puppet/ssl/key' require 'puppet/ssl/certificate' require 'puppet/ssl/certificate_request' +require 'puppet/ssl/certificate_revocation_list' require 'puppet/util/constant_inflector' # The class that manages all aspects of our SSL certificates -- @@ -9,8 +10,9 @@ require 'puppet/util/constant_inflector' class Puppet::SSL::Host # Yay, ruby's strange constant lookups. Key = Puppet::SSL::Key - CertificateRequest = Puppet::SSL::CertificateRequest Certificate = Puppet::SSL::Certificate + CertificateRequest = Puppet::SSL::CertificateRequest + CertificateRevocationList = Puppet::SSL::CertificateRevocationList extend Puppet::Util::ConstantInflector @@ -35,9 +37,10 @@ class Puppet::SSL::Host def self.configure_indirection(terminus, cache = nil) Certificate.terminus_class = terminus CertificateRequest.terminus_class = terminus + CertificateRevocationList.terminus_class = terminus if cache - # This is weird; we don't actually cache our keys, we + # This is weird; we don't actually cache our keys or CRL, we # use what would otherwise be the cache as our normal # terminus. Key.terminus_class = cache @@ -48,12 +51,13 @@ class Puppet::SSL::Host if cache Certificate.cache_class = cache CertificateRequest.cache_class = cache + CertificateRevocationList.cache_class = cache end end # Specify how we expect to interact with our certificate authority. def self.ca_location=(mode) - raise ArgumentError, "CA Mode can only be :local, :remote, or :none" unless [:local, :remote, :only, :none].include?(mode) + raise ArgumentError, "CA Mode can only be :local, :remote, or :none" unless [:local, :remote, :none].include?(mode) @ca_mode = mode @@ -64,25 +68,12 @@ class Puppet::SSL::Host configure_indirection :ca_file, :file when :remote: configure_indirection :rest, :file - when :only: - # We are the CA, so we just interact with CA stuff. - configure_indirection :ca_file when :none: # We have no CA, so we just look in the local file store. configure_indirection :file end end - # Set the cache class for the files we manage. - def self.cache_class=(value) - [Key, CertificateRequest, Certificate].each { |klass| klass.terminus_class = value } - end - - # Set the terminus class for the files we manage. - def self.terminus_class=(value) - [Key, CertificateRequest, Certificate].each { |klass| klass.terminus_class = value } - end - # Search for more than one host, optionally only specifying # an interest in hosts with a given file type. # This just allows our non-indirected class to have one of diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb index 3b32b6d7b..38cbf46e9 100644 --- a/lib/puppet/ssl/inventory.rb +++ b/lib/puppet/ssl/inventory.rb @@ -38,4 +38,15 @@ class Puppet::SSL::Inventory Puppet::SSL::Certificate.search("*").each { |cert| add(cert) } end + + # Find the serial number for a given certificate. + def serial(name) + return nil unless FileTest.exist?(@path) + + File.readlines(@path).each do |line| + next unless line =~ /^(\S+).+\/CN=#{name}$/ + + return Integer($1) + end + end end |