summaryrefslogtreecommitdiffstats
path: root/lib/puppet/ssl
diff options
context:
space:
mode:
authorMarkus Roberts <Markus@reality.com>2010-07-09 18:12:17 -0700
committerMarkus Roberts <Markus@reality.com>2010-07-09 18:12:17 -0700
commit3180b9d9b2c844dade1d361326600f7001ec66dd (patch)
tree98fe7c5ac7eb942aac9c39f019a17b0b3f5a57f4 /lib/puppet/ssl
parent543225970225de5697734bfaf0a6eee996802c04 (diff)
downloadpuppet-3180b9d9b2c844dade1d361326600f7001ec66dd.tar.gz
puppet-3180b9d9b2c844dade1d361326600f7001ec66dd.tar.xz
puppet-3180b9d9b2c844dade1d361326600f7001ec66dd.zip
Code smell: Two space indentation
Replaced 106806 occurances of ^( +)(.*$) with The ruby community almost universally (i.e. everyone but Luke, Markus, and the other eleven people who learned ruby in the 1900s) uses two-space indentation. 3 Examples: The code: end # Tell getopt which arguments are valid def test_get_getopt_args element = Setting.new :name => "foo", :desc => "anything", :settings => Puppet::Util::Settings.new assert_equal([["--foo", GetoptLong::REQUIRED_ARGUMENT]], element.getopt_args, "Did not produce appropriate getopt args") becomes: end # Tell getopt which arguments are valid def test_get_getopt_args element = Setting.new :name => "foo", :desc => "anything", :settings => Puppet::Util::Settings.new assert_equal([["--foo", GetoptLong::REQUIRED_ARGUMENT]], element.getopt_args, "Did not produce appropriate getopt args") The code: assert_equal(str, val) assert_instance_of(Float, result) end # Now test it with a passed object becomes: assert_equal(str, val) assert_instance_of(Float, result) end # Now test it with a passed object The code: end assert_nothing_raised do klass[:Yay] = "boo" klass["Cool"] = :yayness end becomes: end assert_nothing_raised do klass[:Yay] = "boo" klass["Cool"] = :yayness end
Diffstat (limited to 'lib/puppet/ssl')
-rw-r--r--lib/puppet/ssl/base.rb140
-rw-r--r--lib/puppet/ssl/certificate.rb42
-rw-r--r--lib/puppet/ssl/certificate_authority.rb426
-rw-r--r--lib/puppet/ssl/certificate_authority/interface.rb250
-rw-r--r--lib/puppet/ssl/certificate_factory.rb238
-rw-r--r--lib/puppet/ssl/certificate_request.rb104
-rw-r--r--lib/puppet/ssl/certificate_revocation_list.rb156
-rw-r--r--lib/puppet/ssl/host.rb460
-rw-r--r--lib/puppet/ssl/inventory.rb66
-rw-r--r--lib/puppet/ssl/key.rb74
10 files changed, 978 insertions, 978 deletions
diff --git a/lib/puppet/ssl/base.rb b/lib/puppet/ssl/base.rb
index 745d733dc..f9bbcac64 100644
--- a/lib/puppet/ssl/base.rb
+++ b/lib/puppet/ssl/base.rb
@@ -2,78 +2,78 @@ require 'puppet/ssl'
# The base class for wrapping SSL instances.
class Puppet::SSL::Base
- # For now, use the YAML separator.
- SEPARATOR = "\n---\n"
-
- def self.from_multiple_s(text)
- text.split(SEPARATOR).collect { |inst| from_s(inst) }
- end
-
- def self.to_multiple_s(instances)
- instances.collect { |inst| inst.to_s }.join(SEPARATOR)
- end
-
- def self.wraps(klass)
- @wrapped_class = klass
- end
-
- def self.wrapped_class
- raise(Puppet::DevError, "#{self} has not declared what class it wraps") unless defined?(@wrapped_class)
- @wrapped_class
- end
-
- attr_accessor :name, :content
-
- # Is this file for the CA?
- def ca?
- name == Puppet::SSL::Host.ca_name
- end
-
- def generate
- raise Puppet::DevError, "#{self.class} did not override 'generate'"
- end
-
- def initialize(name)
- @name = name.to_s.downcase
+ # For now, use the YAML separator.
+ SEPARATOR = "\n---\n"
+
+ def self.from_multiple_s(text)
+ text.split(SEPARATOR).collect { |inst| from_s(inst) }
+ end
+
+ def self.to_multiple_s(instances)
+ instances.collect { |inst| inst.to_s }.join(SEPARATOR)
+ end
+
+ def self.wraps(klass)
+ @wrapped_class = klass
+ end
+
+ def self.wrapped_class
+ raise(Puppet::DevError, "#{self} has not declared what class it wraps") unless defined?(@wrapped_class)
+ @wrapped_class
+ end
+
+ attr_accessor :name, :content
+
+ # Is this file for the CA?
+ def ca?
+ name == Puppet::SSL::Host.ca_name
+ end
+
+ def generate
+ raise Puppet::DevError, "#{self.class} did not override 'generate'"
+ end
+
+ def initialize(name)
+ @name = name.to_s.downcase
+ end
+
+ # Read content from disk appropriately.
+ def read(path)
+ @content = wrapped_class.new(File.read(path))
+ end
+
+ # Convert our thing to pem.
+ def to_s
+ return "" unless content
+ content.to_pem
+ end
+
+ # Provide the full text of the thing we're dealing with.
+ def to_text
+ return "" unless content
+ content.to_text
+ end
+
+ def fingerprint(md = :MD5)
+ require 'openssl/digest'
+
+ # ruby 1.8.x openssl digest constants are string
+ # but in 1.9.x they are symbols
+ mds = md.to_s.upcase
+ if OpenSSL::Digest.constants.include?(mds)
+ md = mds
+ elsif OpenSSL::Digest.constants.include?(mds.to_sym)
+ md = mds.to_sym
+ else
+ raise ArgumentError, "#{md} is not a valid digest algorithm for fingerprinting certificate #{name}"
end
- # Read content from disk appropriately.
- def read(path)
- @content = wrapped_class.new(File.read(path))
- end
+ OpenSSL::Digest.const_get(md).hexdigest(content.to_der).scan(/../).join(':').upcase
+ end
- # Convert our thing to pem.
- def to_s
- return "" unless content
- content.to_pem
- end
+ private
- # Provide the full text of the thing we're dealing with.
- def to_text
- return "" unless content
- content.to_text
- end
-
- def fingerprint(md = :MD5)
- require 'openssl/digest'
-
- # ruby 1.8.x openssl digest constants are string
- # but in 1.9.x they are symbols
- mds = md.to_s.upcase
- if OpenSSL::Digest.constants.include?(mds)
- md = mds
- elsif OpenSSL::Digest.constants.include?(mds.to_sym)
- md = mds.to_sym
- else
- raise ArgumentError, "#{md} is not a valid digest algorithm for fingerprinting certificate #{name}"
- end
-
- OpenSSL::Digest.const_get(md).hexdigest(content.to_der).scan(/../).join(':').upcase
- end
-
- private
-
- def wrapped_class
- self.class.wrapped_class
- end
+ def wrapped_class
+ self.class.wrapped_class
+ end
end
diff --git a/lib/puppet/ssl/certificate.rb b/lib/puppet/ssl/certificate.rb
index 07dd0c8e6..a0e600291 100644
--- a/lib/puppet/ssl/certificate.rb
+++ b/lib/puppet/ssl/certificate.rb
@@ -6,29 +6,29 @@ require 'puppet/ssl/base'
# retrieve them from the CA (or not, as is often
# the case).
class Puppet::SSL::Certificate < Puppet::SSL::Base
- # This is defined from the base class
- wraps OpenSSL::X509::Certificate
+ # This is defined from the base class
+ wraps OpenSSL::X509::Certificate
- extend Puppet::Indirector
- indirects :certificate, :terminus_class => :file
+ extend Puppet::Indirector
+ indirects :certificate, :terminus_class => :file
- # Convert a string into an instance.
- def self.from_s(string)
- instance = wrapped_class.new(string)
- name = instance.subject.to_s.sub(/\/CN=/i, '').downcase
- result = new(name)
- result.content = instance
- result
- end
+ # Convert a string into an instance.
+ def self.from_s(string)
+ instance = wrapped_class.new(string)
+ name = instance.subject.to_s.sub(/\/CN=/i, '').downcase
+ result = new(name)
+ result.content = instance
+ result
+ end
- # Because of how the format handler class is included, this
- # can't be in the base class.
- def self.supported_formats
- [:s]
- end
+ # Because of how the format handler class is included, this
+ # can't be in the base class.
+ def self.supported_formats
+ [:s]
+ end
- def expiration
- return nil unless content
- content.not_after
- end
+ def expiration
+ return nil unless content
+ content.not_after
+ end
end
diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb
index 26febb3d3..efe562d4c 100644
--- a/lib/puppet/ssl/certificate_authority.rb
+++ b/lib/puppet/ssl/certificate_authority.rb
@@ -11,274 +11,274 @@ require 'puppet/util/cacher'
# it can also be seen as a general interface into all of the
# SSL stuff.
class Puppet::SSL::CertificateAuthority
- require 'puppet/ssl/certificate_factory'
- require 'puppet/ssl/inventory'
- require 'puppet/ssl/certificate_revocation_list'
+ require 'puppet/ssl/certificate_factory'
+ require 'puppet/ssl/inventory'
+ require 'puppet/ssl/certificate_revocation_list'
- require 'puppet/ssl/certificate_authority/interface'
+ require 'puppet/ssl/certificate_authority/interface'
- class CertificateVerificationError < RuntimeError
- attr_accessor :error_code
+ class CertificateVerificationError < RuntimeError
+ attr_accessor :error_code
- def initialize(code)
- @error_code = code
- end
+ def initialize(code)
+ @error_code = code
end
+ end
- class << self
- include Puppet::Util::Cacher
+ class << self
+ include Puppet::Util::Cacher
- cached_attr(:singleton_instance) { new }
- end
+ cached_attr(:singleton_instance) { new }
+ end
- def self.ca?
- return false unless Puppet[:ca]
- return false unless Puppet.run_mode.master?
- true
- end
+ def self.ca?
+ return false unless Puppet[:ca]
+ return false unless Puppet.run_mode.master?
+ true
+ end
- # If this process can function as a CA, then return a singleton
- # instance.
- def self.instance
- return nil unless ca?
+ # If this process can function as a CA, then return a singleton
+ # instance.
+ def self.instance
+ return nil unless ca?
- singleton_instance
- end
+ singleton_instance
+ end
- attr_reader :name, :host
+ attr_reader :name, :host
- # Create and run an applicator. I wanted to build an interface where you could do
- # something like 'ca.apply(:generate).to(:all) but I don't think it's really possible.
- def apply(method, options)
- raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
- applier = Interface.new(method, options)
+ # Create and run an applicator. I wanted to build an interface where you could do
+ # something like 'ca.apply(:generate).to(:all) but I don't think it's really possible.
+ def apply(method, options)
+ raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
+ applier = Interface.new(method, options)
- applier.apply(self)
- end
+ applier.apply(self)
+ end
- # If autosign is configured, then autosign all CSRs that match our configuration.
- def autosign
- return unless auto = autosign?
+ # If autosign is configured, then autosign all CSRs that match our configuration.
+ def autosign
+ return unless auto = autosign?
- store = nil
- store = autosign_store(auto) if auto != true
+ store = nil
+ store = autosign_store(auto) if auto != true
- Puppet::SSL::CertificateRequest.search("*").each do |csr|
- sign(csr.name) if auto == true or store.allowed?(csr.name, "127.1.1.1")
- end
+ Puppet::SSL::CertificateRequest.search("*").each do |csr|
+ sign(csr.name) if auto == true or store.allowed?(csr.name, "127.1.1.1")
+ end
+ end
+
+ # Do we autosign? This returns true, false, or a filename.
+ def autosign?
+ auto = Puppet[:autosign]
+ return false if ['false', false].include?(auto)
+ return true if ['true', true].include?(auto)
+
+ raise ArgumentError, "The autosign configuration '#{auto}' must be a fully qualified file" unless auto =~ /^\//
+ FileTest.exist?(auto) && auto
+ end
+
+ # Create an AuthStore for autosigning.
+ def autosign_store(file)
+ auth = Puppet::Network::AuthStore.new
+ File.readlines(file).each do |line|
+ next if line =~ /^\s*#/
+ next if line =~ /^\s*$/
+ auth.allow(line.chomp)
end
- # Do we autosign? This returns true, false, or a filename.
- def autosign?
- auto = Puppet[:autosign]
- return false if ['false', false].include?(auto)
- return true if ['true', true].include?(auto)
-
- raise ArgumentError, "The autosign configuration '#{auto}' must be a fully qualified file" unless auto =~ /^\//
- FileTest.exist?(auto) && auto
+ auth
+ end
+
+ # Retrieve (or create, if necessary) the certificate revocation list.
+ def crl
+ unless defined?(@crl)
+ unless @crl = Puppet::SSL::CertificateRevocationList.find(Puppet::SSL::CA_NAME)
+ @crl = Puppet::SSL::CertificateRevocationList.new(Puppet::SSL::CA_NAME)
+ @crl.generate(host.certificate.content, host.key.content)
+ @crl.save
+ end
end
+ @crl
+ end
- # Create an AuthStore for autosigning.
- def autosign_store(file)
- auth = Puppet::Network::AuthStore.new
- File.readlines(file).each do |line|
- next if line =~ /^\s*#/
- next if line =~ /^\s*$/
- auth.allow(line.chomp)
- end
+ # Delegate this to our Host class.
+ def destroy(name)
+ Puppet::SSL::Host.destroy(name)
+ end
- auth
- end
+ # Generate a new certificate.
+ def generate(name)
+ raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.find(name)
+ host = Puppet::SSL::Host.new(name)
- # Retrieve (or create, if necessary) the certificate revocation list.
- def crl
- unless defined?(@crl)
- unless @crl = Puppet::SSL::CertificateRevocationList.find(Puppet::SSL::CA_NAME)
- @crl = Puppet::SSL::CertificateRevocationList.new(Puppet::SSL::CA_NAME)
- @crl.generate(host.certificate.content, host.key.content)
- @crl.save
- end
- end
- @crl
- end
+ host.generate_certificate_request
- # Delegate this to our Host class.
- def destroy(name)
- Puppet::SSL::Host.destroy(name)
- end
+ sign(name)
+ end
- # Generate a new certificate.
- def generate(name)
- raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.find(name)
- host = Puppet::SSL::Host.new(name)
+ # Generate our CA certificate.
+ def generate_ca_certificate
+ generate_password unless password?
- host.generate_certificate_request
+ host.generate_key unless host.key
- sign(name)
- end
+ # Create a new cert request. We do this
+ # specially, because we don't want to actually
+ # save the request anywhere.
+ request = Puppet::SSL::CertificateRequest.new(host.name)
+ request.generate(host.key)
- # Generate our CA certificate.
- def generate_ca_certificate
- generate_password unless password?
+ # Create a self-signed certificate.
+ @certificate = sign(host.name, :ca, request)
- host.generate_key unless host.key
+ # And make sure we initialize our CRL.
+ crl
+ end
- # Create a new cert request. We do this
- # specially, because we don't want to actually
- # save the request anywhere.
- request = Puppet::SSL::CertificateRequest.new(host.name)
- request.generate(host.key)
+ def initialize
+ Puppet.settings.use :main, :ssl, :ca
- # Create a self-signed certificate.
- @certificate = sign(host.name, :ca, request)
+ @name = Puppet[:certname]
- # And make sure we initialize our CRL.
- crl
- end
+ @host = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name)
- def initialize
- Puppet.settings.use :main, :ssl, :ca
+ setup
+ end
- @name = Puppet[:certname]
+ # Retrieve (or create, if necessary) our inventory manager.
+ def inventory
+ @inventory ||= Puppet::SSL::Inventory.new
+ end
- @host = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name)
+ # Generate a new password for the CA.
+ def generate_password
+ pass = ""
+ 20.times { pass += (rand(74) + 48).chr }
- setup
+ begin
+ Puppet.settings.write(:capass) { |f| f.print pass }
+ rescue Errno::EACCES => detail
+ raise Puppet::Error, "Could not write CA password: #{detail}"
end
- # Retrieve (or create, if necessary) our inventory manager.
- def inventory
- @inventory ||= Puppet::SSL::Inventory.new
- end
+ @password = pass
- # Generate a new password for the CA.
- def generate_password
- pass = ""
- 20.times { pass += (rand(74) + 48).chr }
+ pass
+ end
- begin
- Puppet.settings.write(:capass) { |f| f.print pass }
- rescue Errno::EACCES => detail
- raise Puppet::Error, "Could not write CA password: #{detail}"
- end
+ # List all signed certificates.
+ def list
+ Puppet::SSL::Certificate.search("*").collect { |c| c.name }
+ end
- @password = pass
+ # Read the next serial from the serial file, and increment the
+ # file so this one is considered used.
+ def next_serial
+ serial = nil
- pass
- end
+ # This is slightly odd. If the file doesn't exist, our readwritelock creates
+ # it, but with a mode we can't actually read in some cases. So, use
+ # a default before the lock.
+ serial = 0x1 unless FileTest.exist?(Puppet[:serial])
- # List all signed certificates.
- def list
- Puppet::SSL::Certificate.search("*").collect { |c| c.name }
- end
+ Puppet.settings.readwritelock(:serial) { |f|
+ serial ||= File.read(Puppet.settings[:serial]).chomp.hex if FileTest.exist?(Puppet[:serial])
- # Read the next serial from the serial file, and increment the
- # file so this one is considered used.
- def next_serial
- serial = nil
+ # We store the next valid serial, not the one we just used.
+ f << "%04X" % (serial + 1)
+ }
- # This is slightly odd. If the file doesn't exist, our readwritelock creates
- # it, but with a mode we can't actually read in some cases. So, use
- # a default before the lock.
- serial = 0x1 unless FileTest.exist?(Puppet[:serial])
+ serial
+ end
- Puppet.settings.readwritelock(:serial) { |f|
- serial ||= File.read(Puppet.settings[:serial]).chomp.hex if FileTest.exist?(Puppet[:serial])
+ # Does the password file exist?
+ def password?
+ FileTest.exist? Puppet[:capass]
+ end
- # We store the next valid serial, not the one we just used.
- f << "%04X" % (serial + 1)
- }
+ # Print a given host's certificate as text.
+ def print(name)
+ (cert = Puppet::SSL::Certificate.find(name)) ? cert.to_text : nil
+ end
- serial
- end
+ # Revoke a given certificate.
+ def revoke(name)
+ raise ArgumentError, "Cannot revoke certificates when the CRL is disabled" unless crl
- # Does the password file exist?
- def password?
- FileTest.exist? Puppet[:capass]
+ 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 #{name}"
end
-
- # Print a given host's certificate as text.
- def print(name)
- (cert = Puppet::SSL::Certificate.find(name)) ? cert.to_text : nil
+ crl.revoke(serial, host.key.content)
+ end
+
+ # This initializes our CA so it actually works. This should be a private
+ # method, except that you can't any-instance stub private methods, which is
+ # *awesome*. This method only really exists to provide a stub-point during
+ # testing.
+ def setup
+ generate_ca_certificate unless @host.certificate
+ 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
+ unless csr = Puppet::SSL::CertificateRequest.find(hostname)
+ raise ArgumentError, "Could not find certificate request for #{hostname}"
+ end
+ issuer = host.certificate.content
end
- # Revoke a given certificate.
- def revoke(name)
- raise ArgumentError, "Cannot revoke certificates when the CRL is disabled" unless crl
+ 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)
- 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 #{name}"
- end
- crl.revoke(serial, host.key.content)
- end
+ Puppet.notice "Signed certificate request for #{hostname}"
- # This initializes our CA so it actually works. This should be a private
- # method, except that you can't any-instance stub private methods, which is
- # *awesome*. This method only really exists to provide a stub-point during
- # testing.
- def setup
- generate_ca_certificate unless @host.certificate
- end
+ # 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)
- # 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
- unless csr = Puppet::SSL::CertificateRequest.find(hostname)
- raise ArgumentError, "Could not find certificate request for #{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 #{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
-
- cert
- end
+ # Save the now-signed cert. This should get routed correctly depending
+ # on the certificate type.
+ cert.save
- # Verify a given host's certificate.
- def verify(name)
- unless cert = Puppet::SSL::Certificate.find(name)
- raise ArgumentError, "Could not find a certificate for #{name}"
- end
- store = OpenSSL::X509::Store.new
- store.add_file Puppet[:cacert]
- store.add_crl crl.content if self.crl
- store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
- store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
-
- raise CertificateVerificationError.new(store.error), store.error_string unless store.verify(cert.content)
- end
+ # And remove the CSR if this wasn't self signed.
+ Puppet::SSL::CertificateRequest.destroy(csr.name) unless self_signing_csr
- def fingerprint(name, md = :MD5)
- unless cert = Puppet::SSL::Certificate.find(name) || Puppet::SSL::CertificateRequest.find(name)
- raise ArgumentError, "Could not find a certificate or csr for #{name}"
- end
- cert.fingerprint(md)
- end
+ cert
+ end
- # List the waiting certificate requests.
- def waiting?
- Puppet::SSL::CertificateRequest.search("*").collect { |r| r.name }
+ # Verify a given host's certificate.
+ def verify(name)
+ unless cert = Puppet::SSL::Certificate.find(name)
+ raise ArgumentError, "Could not find a certificate for #{name}"
end
+ store = OpenSSL::X509::Store.new
+ store.add_file Puppet[:cacert]
+ store.add_crl crl.content if self.crl
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
+
+ raise CertificateVerificationError.new(store.error), store.error_string unless store.verify(cert.content)
+ end
+
+ def fingerprint(name, md = :MD5)
+ unless cert = Puppet::SSL::Certificate.find(name) || Puppet::SSL::CertificateRequest.find(name)
+ raise ArgumentError, "Could not find a certificate or csr for #{name}"
+ end
+ cert.fingerprint(md)
+ 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_authority/interface.rb b/lib/puppet/ssl/certificate_authority/interface.rb
index e5ede3c6c..f73eff5f3 100644
--- a/lib/puppet/ssl/certificate_authority/interface.rb
+++ b/lib/puppet/ssl/certificate_authority/interface.rb
@@ -2,133 +2,133 @@
# on the CA. It's only used by the 'puppetca' executable, and its
# job is to provide a CLI-like interface to the CA class.
module Puppet
- module SSL
- class CertificateAuthority
- class Interface
- INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify, :fingerprint]
-
- class InterfaceError < ArgumentError; end
-
- attr_reader :method, :subjects, :digest
-
- # Actually perform the work.
- def apply(ca)
- unless subjects or method == :list
- raise ArgumentError, "You must provide hosts or :all when using #{method}"
- end
-
- begin
- return send(method, ca) if respond_to?(method)
-
- (subjects == :all ? ca.list : subjects).each do |host|
- ca.send(method, host)
- end
- rescue InterfaceError
- raise
- rescue => detail
- puts detail.backtrace if Puppet[:trace]
- Puppet.err "Could not call #{method}: #{detail}"
- end
- end
-
- def generate(ca)
- raise InterfaceError, "It makes no sense to generate all hosts; you must specify a list" if subjects == :all
-
- subjects.each do |host|
- ca.generate(host)
- end
- end
-
- def initialize(method, options)
- self.method = method
- self.subjects = options[:to]
- @digest = options[:digest] || :MD5
- end
-
- # List the hosts.
- def list(ca)
- unless subjects
- puts ca.waiting?.join("\n")
- return nil
- end
-
- signed = ca.list
- requests = ca.waiting?
-
- if subjects == :all
- hosts = [signed, requests].flatten
- elsif subjects == :signed
- hosts = signed.flatten
- else
- hosts = subjects
- end
-
- hosts.uniq.sort.each do |host|
- invalid = false
- begin
- ca.verify(host) unless requests.include?(host)
- rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError => details
- invalid = details.to_s
- end
- if not invalid and signed.include?(host)
- puts "+ #{host} (#{ca.fingerprint(host, @digest)})"
- elsif invalid
- puts "- #{host} (#{ca.fingerprint(host, @digest)}) (#{invalid})"
- else
- puts "#{host} (#{ca.fingerprint(host, @digest)})"
- end
- end
- end
-
- # Set the method to apply.
- def method=(method)
- raise ArgumentError, "Invalid method #{method} to apply" unless INTERFACE_METHODS.include?(method)
- @method = method
- end
-
- # Print certificate information.
- def print(ca)
- (subjects == :all ? ca.list : subjects).each do |host|
- if value = ca.print(host)
- puts value
- else
- Puppet.err "Could not find certificate for #{host}"
- end
- end
- end
-
- # Print certificate information.
- def fingerprint(ca)
- (subjects == :all ? ca.list + ca.waiting?: subjects).each do |host|
- if value = ca.fingerprint(host, @digest)
- puts "#{host} #{value}"
- else
- Puppet.err "Could not find certificate for #{host}"
- end
- end
- end
-
- # Sign a given certificate.
- def sign(ca)
- list = subjects == :all ? ca.waiting? : subjects
- raise InterfaceError, "No waiting certificate requests to sign" if list.empty?
- list.each do |host|
- ca.sign(host)
- end
- end
-
- # Set the list of hosts we're operating on. Also supports keywords.
- def subjects=(value)
- unless value == :all or value == :signed or value.is_a?(Array)
- raise ArgumentError, "Subjects must be an array or :all; not #{value}"
- end
-
- value = nil if value.is_a?(Array) and value.empty?
-
- @subjects = value
- end
+ module SSL
+ class CertificateAuthority
+ class Interface
+ INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify, :fingerprint]
+
+ class InterfaceError < ArgumentError; end
+
+ attr_reader :method, :subjects, :digest
+
+ # Actually perform the work.
+ def apply(ca)
+ unless subjects or method == :list
+ raise ArgumentError, "You must provide hosts or :all when using #{method}"
+ end
+
+ begin
+ return send(method, ca) if respond_to?(method)
+
+ (subjects == :all ? ca.list : subjects).each do |host|
+ ca.send(method, host)
+ end
+ rescue InterfaceError
+ raise
+ rescue => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err "Could not call #{method}: #{detail}"
+ end
+ end
+
+ def generate(ca)
+ raise InterfaceError, "It makes no sense to generate all hosts; you must specify a list" if subjects == :all
+
+ subjects.each do |host|
+ ca.generate(host)
+ end
+ end
+
+ def initialize(method, options)
+ self.method = method
+ self.subjects = options[:to]
+ @digest = options[:digest] || :MD5
+ end
+
+ # List the hosts.
+ def list(ca)
+ unless subjects
+ puts ca.waiting?.join("\n")
+ return nil
+ end
+
+ signed = ca.list
+ requests = ca.waiting?
+
+ if subjects == :all
+ hosts = [signed, requests].flatten
+ elsif subjects == :signed
+ hosts = signed.flatten
+ else
+ hosts = subjects
+ end
+
+ hosts.uniq.sort.each do |host|
+ invalid = false
+ begin
+ ca.verify(host) unless requests.include?(host)
+ rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError => details
+ invalid = details.to_s
+ end
+ if not invalid and signed.include?(host)
+ puts "+ #{host} (#{ca.fingerprint(host, @digest)})"
+ elsif invalid
+ puts "- #{host} (#{ca.fingerprint(host, @digest)}) (#{invalid})"
+ else
+ puts "#{host} (#{ca.fingerprint(host, @digest)})"
end
+ end
+ end
+
+ # Set the method to apply.
+ def method=(method)
+ raise ArgumentError, "Invalid method #{method} to apply" unless INTERFACE_METHODS.include?(method)
+ @method = method
+ end
+
+ # Print certificate information.
+ def print(ca)
+ (subjects == :all ? ca.list : subjects).each do |host|
+ if value = ca.print(host)
+ puts value
+ else
+ Puppet.err "Could not find certificate for #{host}"
+ end
+ end
+ end
+
+ # Print certificate information.
+ def fingerprint(ca)
+ (subjects == :all ? ca.list + ca.waiting?: subjects).each do |host|
+ if value = ca.fingerprint(host, @digest)
+ puts "#{host} #{value}"
+ else
+ Puppet.err "Could not find certificate for #{host}"
+ end
+ end
+ end
+
+ # Sign a given certificate.
+ def sign(ca)
+ list = subjects == :all ? ca.waiting? : subjects
+ raise InterfaceError, "No waiting certificate requests to sign" if list.empty?
+ list.each do |host|
+ ca.sign(host)
+ end
+ end
+
+ # Set the list of hosts we're operating on. Also supports keywords.
+ def subjects=(value)
+ unless value == :all or value == :signed or value.is_a?(Array)
+ raise ArgumentError, "Subjects must be an array or :all; not #{value}"
+ end
+
+ value = nil if value.is_a?(Array) and value.empty?
+
+ @subjects = value
end
+ end
end
+ end
end
diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb
index 9a5507dea..73290e9cf 100644
--- a/lib/puppet/ssl/certificate_factory.rb
+++ b/lib/puppet/ssl/certificate_factory.rb
@@ -3,143 +3,143 @@ require 'puppet/ssl'
# The tedious class that does all the manipulations to the
# certificate to correctly sign it. Yay.
class Puppet::SSL::CertificateFactory
- # How we convert from various units to the required seconds.
- UNITMAP = {
- "y" => 365 * 24 * 60 * 60,
- "d" => 24 * 60 * 60,
- "h" => 60 * 60,
- "s" => 1
- }
+ # How we convert from various units to the required seconds.
+ UNITMAP = {
+ "y" => 365 * 24 * 60 * 60,
+ "d" => 24 * 60 * 60,
+ "h" => 60 * 60,
+ "s" => 1
+ }
- attr_reader :name, :cert_type, :csr, :issuer, :serial
+ attr_reader :name, :cert_type, :csr, :issuer, :serial
- def initialize(cert_type, csr, issuer, serial)
- @cert_type, @csr, @issuer, @serial = cert_type, csr, issuer, serial
+ def initialize(cert_type, csr, issuer, serial)
+ @cert_type, @csr, @issuer, @serial = cert_type, csr, issuer, serial
- @name = @csr.subject
- end
-
- # Actually generate our certificate.
- def result
- @cert = OpenSSL::X509::Certificate.new
-
- @cert.version = 2 # X509v3
- @cert.subject = @csr.subject
- @cert.issuer = @issuer.subject
- @cert.public_key = @csr.public_key
- @cert.serial = @serial
-
- build_extensions
-
- set_ttl
-
- @cert
- end
-
- private
-
- # This is pretty ugly, but I'm not really sure it's even possible to do
- # it any other way.
- def build_extensions
- @ef = OpenSSL::X509::ExtensionFactory.new
-
- @ef.subject_certificate = @cert
+ @name = @csr.subject
+ end
- if @issuer.is_a?(OpenSSL::X509::Request) # It's a self-signed cert
- @ef.issuer_certificate = @cert
- else
- @ef.issuer_certificate = @issuer
- end
+ # Actually generate our certificate.
+ def result
+ @cert = OpenSSL::X509::Certificate.new
- @subject_alt_name = []
- @key_usage = nil
- @ext_key_usage = nil
- @extensions = []
+ @cert.version = 2 # X509v3
+ @cert.subject = @csr.subject
+ @cert.issuer = @issuer.subject
+ @cert.public_key = @csr.public_key
+ @cert.serial = @serial
- method = "add_#{@cert_type.to_s}_extensions"
+ build_extensions
- begin
- send(method)
- rescue NoMethodError
- raise ArgumentError, "#{@cert_type} is an invalid certificate type"
- end
+ set_ttl
- @extensions << @ef.create_extension("nsComment", "Puppet Ruby/OpenSSL Generated Certificate")
- @extensions << @ef.create_extension("basicConstraints", @basic_constraint, true)
- @extensions << @ef.create_extension("subjectKeyIdentifier", "hash")
- @extensions << @ef.create_extension("keyUsage", @key_usage.join(",")) if @key_usage
- @extensions << @ef.create_extension("extendedKeyUsage", @ext_key_usage.join(",")) if @ext_key_usage
- @extensions << @ef.create_extension("subjectAltName", @subject_alt_name.join(",")) if ! @subject_alt_name.empty?
+ @cert
+ end
- @cert.extensions = @extensions
+ private
- # for some reason this _must_ be the last extension added
- @extensions << @ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if @cert_type == :ca
- end
-
- # TTL for new certificates in seconds. If config param :ca_ttl is set,
- # use that, otherwise use :ca_days for backwards compatibility
- def ttl
- ttl = Puppet.settings[:ca_ttl]
-
- return ttl unless ttl.is_a?(String)
-
- raise ArgumentError, "Invalid ca_ttl #{ttl}" unless ttl =~ /^(\d+)(y|d|h|s)$/
-
- $1.to_i * UNITMAP[$2]
- end
+ # This is pretty ugly, but I'm not really sure it's even possible to do
+ # it any other way.
+ def build_extensions
+ @ef = OpenSSL::X509::ExtensionFactory.new
- def set_ttl
- # Make the certificate valid as of yesterday, because
- # so many people's clocks are out of sync.
- from = Time.now - (60*60*24)
- @cert.not_before = from
- @cert.not_after = from + ttl
- end
+ @ef.subject_certificate = @cert
- # Woot! We're a CA.
- def add_ca_extensions
- @basic_constraint = "CA:TRUE"
- @key_usage = %w{cRLSign keyCertSign}
+ if @issuer.is_a?(OpenSSL::X509::Request) # It's a self-signed cert
+ @ef.issuer_certificate = @cert
+ else
+ @ef.issuer_certificate = @issuer
end
- # We're a terminal CA, probably not self-signed.
- def add_terminalsubca_extensions
- @basic_constraint = "CA:TRUE,pathlen:0"
- @key_usage = %w{cRLSign keyCertSign}
- end
+ @subject_alt_name = []
+ @key_usage = nil
+ @ext_key_usage = nil
+ @extensions = []
- # We're a normal server.
- def add_server_extensions
- @basic_constraint = "CA:FALSE"
- dnsnames = Puppet[:certdnsnames]
- name = @name.to_s.sub(%r{/CN=},'')
- if dnsnames != ""
- dnsnames.split(':').each { |d| @subject_alt_name << 'DNS:' + d }
- @subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
- elsif name == Facter.value(:fqdn) # we're a CA server, and thus probably the server
- @subject_alt_name << 'DNS:' + "puppet" # Add 'puppet' as an alias
- @subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
- @subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias
- end
- @key_usage = %w{digitalSignature keyEncipherment}
- @ext_key_usage = %w{serverAuth clientAuth emailProtection}
- end
+ method = "add_#{@cert_type.to_s}_extensions"
- # Um, no idea.
- def add_ocsp_extensions
- @basic_constraint = "CA:FALSE"
- @key_usage = %w{nonRepudiation digitalSignature}
- @ext_key_usage = %w{serverAuth OCSPSigning}
+ begin
+ send(method)
+ rescue NoMethodError
+ raise ArgumentError, "#{@cert_type} is an invalid certificate type"
end
- # Normal client.
- def add_client_extensions
- @basic_constraint = "CA:FALSE"
- @key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
- @ext_key_usage = %w{clientAuth emailProtection}
-
- @extensions << @ef.create_extension("nsCertType", "client,email")
+ @extensions << @ef.create_extension("nsComment", "Puppet Ruby/OpenSSL Generated Certificate")
+ @extensions << @ef.create_extension("basicConstraints", @basic_constraint, true)
+ @extensions << @ef.create_extension("subjectKeyIdentifier", "hash")
+ @extensions << @ef.create_extension("keyUsage", @key_usage.join(",")) if @key_usage
+ @extensions << @ef.create_extension("extendedKeyUsage", @ext_key_usage.join(",")) if @ext_key_usage
+ @extensions << @ef.create_extension("subjectAltName", @subject_alt_name.join(",")) if ! @subject_alt_name.empty?
+
+ @cert.extensions = @extensions
+
+ # for some reason this _must_ be the last extension added
+ @extensions << @ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if @cert_type == :ca
+ end
+
+ # TTL for new certificates in seconds. If config param :ca_ttl is set,
+ # use that, otherwise use :ca_days for backwards compatibility
+ def ttl
+ ttl = Puppet.settings[:ca_ttl]
+
+ return ttl unless ttl.is_a?(String)
+
+ raise ArgumentError, "Invalid ca_ttl #{ttl}" unless ttl =~ /^(\d+)(y|d|h|s)$/
+
+ $1.to_i * UNITMAP[$2]
+ end
+
+ def set_ttl
+ # Make the certificate valid as of yesterday, because
+ # so many people's clocks are out of sync.
+ from = Time.now - (60*60*24)
+ @cert.not_before = from
+ @cert.not_after = from + ttl
+ end
+
+ # Woot! We're a CA.
+ def add_ca_extensions
+ @basic_constraint = "CA:TRUE"
+ @key_usage = %w{cRLSign keyCertSign}
+ end
+
+ # We're a terminal CA, probably not self-signed.
+ def add_terminalsubca_extensions
+ @basic_constraint = "CA:TRUE,pathlen:0"
+ @key_usage = %w{cRLSign keyCertSign}
+ end
+
+ # We're a normal server.
+ def add_server_extensions
+ @basic_constraint = "CA:FALSE"
+ dnsnames = Puppet[:certdnsnames]
+ name = @name.to_s.sub(%r{/CN=},'')
+ if dnsnames != ""
+ dnsnames.split(':').each { |d| @subject_alt_name << 'DNS:' + d }
+ @subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
+ elsif name == Facter.value(:fqdn) # we're a CA server, and thus probably the server
+ @subject_alt_name << 'DNS:' + "puppet" # Add 'puppet' as an alias
+ @subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
+ @subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias
end
+ @key_usage = %w{digitalSignature keyEncipherment}
+ @ext_key_usage = %w{serverAuth clientAuth emailProtection}
+ end
+
+ # Um, no idea.
+ def add_ocsp_extensions
+ @basic_constraint = "CA:FALSE"
+ @key_usage = %w{nonRepudiation digitalSignature}
+ @ext_key_usage = %w{serverAuth OCSPSigning}
+ end
+
+ # Normal client.
+ def add_client_extensions
+ @basic_constraint = "CA:FALSE"
+ @key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
+ @ext_key_usage = %w{clientAuth emailProtection}
+
+ @extensions << @ef.create_extension("nsCertType", "client,email")
+ end
end
diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb
index 3cd3ce0be..e4d06a039 100644
--- a/lib/puppet/ssl/certificate_request.rb
+++ b/lib/puppet/ssl/certificate_request.rb
@@ -2,57 +2,57 @@ require 'puppet/ssl/base'
# Manage certificate requests.
class Puppet::SSL::CertificateRequest < Puppet::SSL::Base
- wraps OpenSSL::X509::Request
-
- extend Puppet::Indirector
- indirects :certificate_request, :terminus_class => :file
-
- # Convert a string into an instance.
- def self.from_s(string)
- instance = wrapped_class.new(string)
- name = instance.subject.to_s.sub(/\/CN=/i, '').downcase
- result = new(name)
- result.content = instance
- result
- end
-
- # Because of how the format handler class is included, this
- # can't be in the base class.
- def self.supported_formats
- [:s]
- end
-
- # How to create a certificate request with our system defaults.
- def generate(key)
- Puppet.info "Creating a new SSL certificate request for #{name}"
-
- # Support either an actual SSL key, or a Puppet key.
- key = key.content if key.is_a?(Puppet::SSL::Key)
-
- # If we're a CSR for the CA, then use the real certname, rather than the
- # fake 'ca' name. This is mostly for backward compatibility with 0.24.x,
- # but it's also just a good idea.
- common_name = name == Puppet::SSL::CA_NAME ? Puppet.settings[:ca_name] : name
-
- csr = OpenSSL::X509::Request.new
- csr.version = 0
- csr.subject = OpenSSL::X509::Name.new([["CN", common_name]])
- csr.public_key = key.public_key
- csr.sign(key, OpenSSL::Digest::MD5.new)
-
- raise Puppet::Error, "CSR sign verification failed; you need to clean the certificate request for #{name} on the server" unless csr.verify(key.public_key)
-
- @content = csr
- Puppet.info "Certificate Request fingerprint (md5): #{fingerprint}"
- @content
- end
-
- def save(args = {})
- super()
-
- # Try to autosign the CSR.
- if ca = Puppet::SSL::CertificateAuthority.instance
- ca.autosign
- end
+ wraps OpenSSL::X509::Request
+
+ extend Puppet::Indirector
+ indirects :certificate_request, :terminus_class => :file
+
+ # Convert a string into an instance.
+ def self.from_s(string)
+ instance = wrapped_class.new(string)
+ name = instance.subject.to_s.sub(/\/CN=/i, '').downcase
+ result = new(name)
+ result.content = instance
+ result
+ end
+
+ # Because of how the format handler class is included, this
+ # can't be in the base class.
+ def self.supported_formats
+ [:s]
+ end
+
+ # How to create a certificate request with our system defaults.
+ def generate(key)
+ Puppet.info "Creating a new SSL certificate request for #{name}"
+
+ # Support either an actual SSL key, or a Puppet key.
+ key = key.content if key.is_a?(Puppet::SSL::Key)
+
+ # If we're a CSR for the CA, then use the real certname, rather than the
+ # fake 'ca' name. This is mostly for backward compatibility with 0.24.x,
+ # but it's also just a good idea.
+ common_name = name == Puppet::SSL::CA_NAME ? Puppet.settings[:ca_name] : name
+
+ csr = OpenSSL::X509::Request.new
+ csr.version = 0
+ csr.subject = OpenSSL::X509::Name.new([["CN", common_name]])
+ csr.public_key = key.public_key
+ csr.sign(key, OpenSSL::Digest::MD5.new)
+
+ raise Puppet::Error, "CSR sign verification failed; you need to clean the certificate request for #{name} on the server" unless csr.verify(key.public_key)
+
+ @content = csr
+ Puppet.info "Certificate Request fingerprint (md5): #{fingerprint}"
+ @content
+ end
+
+ def save(args = {})
+ super()
+
+ # Try to autosign the CSR.
+ if ca = Puppet::SSL::CertificateAuthority.instance
+ ca.autosign
end
+ end
end
diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb
index b2bff4830..44e0a9e22 100644
--- a/lib/puppet/ssl/certificate_revocation_list.rb
+++ b/lib/puppet/ssl/certificate_revocation_list.rb
@@ -3,82 +3,82 @@ require 'puppet/indirector'
# Manage the CRL.
class Puppet::SSL::CertificateRevocationList < Puppet::SSL::Base
- wraps OpenSSL::X509::CRL
-
- extend Puppet::Indirector
- indirects :certificate_revocation_list, :terminus_class => :file
-
- # Convert a string into an instance.
- def self.from_s(string)
- instance = wrapped_class.new(string)
- result = new('foo') # The name doesn't matter
- result.content = instance
- result
- end
-
- # Because of how the format handler class is included, this
- # can't be in the base class.
- def self.supported_formats
- [:s]
- end
-
- # Knows how to create a CRL with our system defaults.
- def generate(cert, cakey)
- Puppet.info "Creating a new certificate revocation list"
- @content = wrapped_class.new
- @content.issuer = cert.subject
- @content.version = 1
-
- # Init the CRL number.
- crlNum = OpenSSL::ASN1::Integer(0)
- @content.extensions = [OpenSSL::X509::Extension.new("crlNumber", crlNum)]
-
- # Set last/next update
- @content.last_update = Time.now
- # Keep CRL valid for 5 years
- @content.next_update = Time.now + 5 * 365*24*60*60
-
- @content.sign(cakey, OpenSSL::Digest::SHA1.new)
-
- @content
- end
-
- # 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)
- @name = "crl"
- end
-
- # Revoke the certificate with serial number SERIAL issued by this
- # CA, then write the CRL back to disk. The REASON must be one of the
- # OpenSSL::OCSP::REVOKED_* reasons
- def revoke(serial, cakey, reason = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)
- Puppet.notice "Revoked certificate with serial #{serial}"
- time = Time.now
-
- # Add our revocation to the CRL.
- revoked = OpenSSL::X509::Revoked.new
- revoked.serial = serial
- revoked.time = time
- enum = OpenSSL::ASN1::Enumerated(reason)
- ext = OpenSSL::X509::Extension.new("CRLReason", enum)
- revoked.add_extension(ext)
- @content.add_revoked(revoked)
-
- # Increment the crlNumber
- e = @content.extensions.find { |e| e.oid == 'crlNumber' }
- ext = @content.extensions.reject { |e| e.oid == 'crlNumber' }
- crlNum = OpenSSL::ASN1::Integer(e ? e.value.to_i + 1 : 0)
- ext << OpenSSL::X509::Extension.new("crlNumber", crlNum)
- @content.extensions = ext
-
- # Set last/next update
- @content.last_update = time
- # Keep CRL valid for 5 years
- @content.next_update = time + 5 * 365*24*60*60
-
- @content.sign(cakey, OpenSSL::Digest::SHA1.new)
-
- save
- end
+ wraps OpenSSL::X509::CRL
+
+ extend Puppet::Indirector
+ indirects :certificate_revocation_list, :terminus_class => :file
+
+ # Convert a string into an instance.
+ def self.from_s(string)
+ instance = wrapped_class.new(string)
+ result = new('foo') # The name doesn't matter
+ result.content = instance
+ result
+ end
+
+ # Because of how the format handler class is included, this
+ # can't be in the base class.
+ def self.supported_formats
+ [:s]
+ end
+
+ # Knows how to create a CRL with our system defaults.
+ def generate(cert, cakey)
+ Puppet.info "Creating a new certificate revocation list"
+ @content = wrapped_class.new
+ @content.issuer = cert.subject
+ @content.version = 1
+
+ # Init the CRL number.
+ crlNum = OpenSSL::ASN1::Integer(0)
+ @content.extensions = [OpenSSL::X509::Extension.new("crlNumber", crlNum)]
+
+ # Set last/next update
+ @content.last_update = Time.now
+ # Keep CRL valid for 5 years
+ @content.next_update = Time.now + 5 * 365*24*60*60
+
+ @content.sign(cakey, OpenSSL::Digest::SHA1.new)
+
+ @content
+ end
+
+ # 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)
+ @name = "crl"
+ end
+
+ # Revoke the certificate with serial number SERIAL issued by this
+ # CA, then write the CRL back to disk. The REASON must be one of the
+ # OpenSSL::OCSP::REVOKED_* reasons
+ def revoke(serial, cakey, reason = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)
+ Puppet.notice "Revoked certificate with serial #{serial}"
+ time = Time.now
+
+ # Add our revocation to the CRL.
+ revoked = OpenSSL::X509::Revoked.new
+ revoked.serial = serial
+ revoked.time = time
+ enum = OpenSSL::ASN1::Enumerated(reason)
+ ext = OpenSSL::X509::Extension.new("CRLReason", enum)
+ revoked.add_extension(ext)
+ @content.add_revoked(revoked)
+
+ # Increment the crlNumber
+ e = @content.extensions.find { |e| e.oid == 'crlNumber' }
+ ext = @content.extensions.reject { |e| e.oid == 'crlNumber' }
+ crlNum = OpenSSL::ASN1::Integer(e ? e.value.to_i + 1 : 0)
+ ext << OpenSSL::X509::Extension.new("crlNumber", crlNum)
+ @content.extensions = ext
+
+ # Set last/next update
+ @content.last_update = time
+ # Keep CRL valid for 5 years
+ @content.next_update = time + 5 * 365*24*60*60
+
+ @content.sign(cakey, OpenSSL::Digest::SHA1.new)
+
+ save
+ end
end
diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb
index 9aaff8ad2..8a6f0aa6d 100644
--- a/lib/puppet/ssl/host.rb
+++ b/lib/puppet/ssl/host.rb
@@ -8,255 +8,255 @@ require 'puppet/util/cacher'
# The class that manages all aspects of our SSL certificates --
# private keys, public keys, requests, etc.
class Puppet::SSL::Host
- # Yay, ruby's strange constant lookups.
- Key = Puppet::SSL::Key
- CA_NAME = Puppet::SSL::CA_NAME
- Certificate = Puppet::SSL::Certificate
- CertificateRequest = Puppet::SSL::CertificateRequest
- CertificateRevocationList = Puppet::SSL::CertificateRevocationList
-
- attr_reader :name
- attr_accessor :ca
-
- attr_writer :key, :certificate, :certificate_request
-
- class << self
- include Puppet::Util::Cacher
-
- cached_attr(:localhost) do
- result = new
- result.generate unless result.certificate
- result.key # Make sure it's read in
- result
- end
+ # Yay, ruby's strange constant lookups.
+ Key = Puppet::SSL::Key
+ CA_NAME = Puppet::SSL::CA_NAME
+ Certificate = Puppet::SSL::Certificate
+ CertificateRequest = Puppet::SSL::CertificateRequest
+ CertificateRevocationList = Puppet::SSL::CertificateRevocationList
+
+ attr_reader :name
+ attr_accessor :ca
+
+ attr_writer :key, :certificate, :certificate_request
+
+ class << self
+ include Puppet::Util::Cacher
+
+ cached_attr(:localhost) do
+ result = new
+ result.generate unless result.certificate
+ result.key # Make sure it's read in
+ result
end
-
- # This is the constant that people will use to mark that a given host is
- # a certificate authority.
- def self.ca_name
- CA_NAME
+ end
+
+ # This is the constant that people will use to mark that a given host is
+ # a certificate authority.
+ def self.ca_name
+ CA_NAME
+ end
+
+ class << self
+ attr_reader :ca_location
+ end
+
+ # Configure how our various classes interact with their various terminuses.
+ 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
+ # use what would otherwise be the cache as our normal
+ # terminus.
+ Key.terminus_class = cache
+ else
+ Key.terminus_class = terminus
end
- class << self
- attr_reader :ca_location
+ if cache
+ Certificate.cache_class = cache
+ CertificateRequest.cache_class = cache
+ CertificateRevocationList.cache_class = cache
+ else
+ # Make sure we have no cache configured. puppet master
+ # switches the configurations around a bit, so it's important
+ # that we specify the configs for absolutely everything, every
+ # time.
+ Certificate.cache_class = nil
+ CertificateRequest.cache_class = nil
+ CertificateRevocationList.cache_class = nil
end
-
- # Configure how our various classes interact with their various terminuses.
- 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
- # use what would otherwise be the cache as our normal
- # terminus.
- Key.terminus_class = cache
- else
- Key.terminus_class = terminus
- end
-
- if cache
- Certificate.cache_class = cache
- CertificateRequest.cache_class = cache
- CertificateRevocationList.cache_class = cache
- else
- # Make sure we have no cache configured. puppet master
- # switches the configurations around a bit, so it's important
- # that we specify the configs for absolutely everything, every
- # time.
- Certificate.cache_class = nil
- CertificateRequest.cache_class = nil
- CertificateRevocationList.cache_class = nil
- end
+ end
+
+ CA_MODES = {
+ # Our ca is local, so we use it as the ultimate source of information
+ # And we cache files locally.
+ :local => [:ca, :file],
+ # We're a remote CA client.
+ :remote => [:rest, :file],
+ # We are the CA, so we don't have read/write access to the normal certificates.
+ :only => [:ca],
+ # We have no CA, so we just look in the local file store.
+ :none => [:file]
+ }
+
+ # Specify how we expect to interact with our certificate authority.
+ def self.ca_location=(mode)
+ raise ArgumentError, "CA Mode can only be #{CA_MODES.collect { |m| m.to_s }.join(", ")}" unless CA_MODES.include?(mode)
+
+ @ca_location = mode
+
+ configure_indirection(*CA_MODES[@ca_location])
+ end
+
+ # Remove all traces of a given host
+ def self.destroy(name)
+ [Key, Certificate, CertificateRequest].collect { |part| part.destroy(name) }.any? { |x| x }
+ 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
+ # indirection methods.
+ def self.search(options = {})
+ classlist = [options[:for] || [Key, CertificateRequest, Certificate]].flatten
+
+ # Collect the results from each class, flatten them, collect all of the names, make the name list unique,
+ # then create a Host instance for each one.
+ classlist.collect { |klass| klass.search }.flatten.collect { |r| r.name }.uniq.collect do |name|
+ new(name)
end
-
- CA_MODES = {
- # Our ca is local, so we use it as the ultimate source of information
- # And we cache files locally.
- :local => [:ca, :file],
- # We're a remote CA client.
- :remote => [:rest, :file],
- # We are the CA, so we don't have read/write access to the normal certificates.
- :only => [:ca],
- # We have no CA, so we just look in the local file store.
- :none => [:file]
- }
-
- # Specify how we expect to interact with our certificate authority.
- def self.ca_location=(mode)
- raise ArgumentError, "CA Mode can only be #{CA_MODES.collect { |m| m.to_s }.join(", ")}" unless CA_MODES.include?(mode)
-
- @ca_location = mode
-
- configure_indirection(*CA_MODES[@ca_location])
+ end
+
+ # Is this a ca host, meaning that all of its files go in the CA location?
+ def ca?
+ ca
+ end
+
+ def key
+ @key ||= Key.find(name)
+ end
+
+ # This is the private key; we can create it from scratch
+ # with no inputs.
+ def generate_key
+ @key = Key.new(name)
+ @key.generate
+ begin
+ @key.save
+ rescue
+ @key = nil
+ raise
end
-
- # Remove all traces of a given host
- def self.destroy(name)
- [Key, Certificate, CertificateRequest].collect { |part| part.destroy(name) }.any? { |x| x }
+ true
+ end
+
+ def certificate_request
+ @certificate_request ||= CertificateRequest.find(name)
+ end
+
+ # Our certificate request requires the key but that's all.
+ def generate_certificate_request
+ generate_key unless key
+ @certificate_request = CertificateRequest.new(name)
+ @certificate_request.generate(key.content)
+ begin
+ @certificate_request.save
+ rescue
+ @certificate_request = nil
+ raise
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
- # indirection methods.
- def self.search(options = {})
- classlist = [options[:for] || [Key, CertificateRequest, Certificate]].flatten
-
- # Collect the results from each class, flatten them, collect all of the names, make the name list unique,
- # then create a Host instance for each one.
- classlist.collect { |klass| klass.search }.flatten.collect { |r| r.name }.uniq.collect do |name|
- new(name)
- end
- end
+ true
+ end
- # Is this a ca host, meaning that all of its files go in the CA location?
- def ca?
- ca
- end
+ def certificate
+ unless @certificate
+ generate_key unless key
- def key
- @key ||= Key.find(name)
- end
+ # get the CA cert first, since it's required for the normal cert
+ # to be of any use.
+ return nil unless Certificate.find("ca") unless ca?
+ return nil unless @certificate = Certificate.find(name)
- # This is the private key; we can create it from scratch
- # with no inputs.
- def generate_key
- @key = Key.new(name)
- @key.generate
- begin
- @key.save
- rescue
- @key = nil
- raise
- end
- true
+ unless certificate_matches_key?
+ raise Puppet::Error, "Retrieved certificate does not match private key; please remove certificate from server and regenerate it with the current key"
+ end
end
-
- def certificate_request
- @certificate_request ||= CertificateRequest.find(name)
+ @certificate
+ end
+
+ def certificate_matches_key?
+ return false unless key
+ return false unless certificate
+
+ certificate.content.check_private_key(key.content)
+ end
+
+ # Generate all necessary parts of our ssl host.
+ def generate
+ generate_key unless key
+ generate_certificate_request unless certificate_request
+
+ # If we can get a CA instance, then we're a valid CA, and we
+ # should use it to sign our request; else, just try to read
+ # the cert.
+ if ! certificate and ca = Puppet::SSL::CertificateAuthority.instance
+ ca.sign(self.name)
end
-
- # Our certificate request requires the key but that's all.
- def generate_certificate_request
- generate_key unless key
- @certificate_request = CertificateRequest.new(name)
- @certificate_request.generate(key.content)
- begin
- @certificate_request.save
- rescue
- @certificate_request = nil
- raise
- end
-
- true
+ end
+
+ def initialize(name = nil)
+ @name = (name || Puppet[:certname]).downcase
+ @key = @certificate = @certificate_request = nil
+ @ca = (name == self.class.ca_name)
+ end
+
+ # Extract the public key from the private key.
+ def public_key
+ key.content.public_key
+ end
+
+ # Create/return a store that uses our SSL info to validate
+ # connections.
+ def ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY)
+ unless @ssl_store
+ @ssl_store = OpenSSL::X509::Store.new
+ @ssl_store.purpose = purpose
+
+ # Use the file path here, because we don't want to cause
+ # a lookup in the middle of setting our ssl connection.
+ @ssl_store.add_file(Puppet[:localcacert])
+
+ # If there's a CRL, add it to our store.
+ if crl = Puppet::SSL::CertificateRevocationList.find(CA_NAME)
+ @ssl_store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
+ @ssl_store.add_crl(crl.content)
+ end
+ return @ssl_store
end
-
- def certificate
- unless @certificate
- generate_key unless key
-
- # get the CA cert first, since it's required for the normal cert
- # to be of any use.
- return nil unless Certificate.find("ca") unless ca?
- return nil unless @certificate = Certificate.find(name)
-
- unless certificate_matches_key?
- raise Puppet::Error, "Retrieved certificate does not match private key; please remove certificate from server and regenerate it with the current key"
- end
- end
- @certificate
- end
-
- def certificate_matches_key?
- return false unless key
- return false unless certificate
-
- certificate.content.check_private_key(key.content)
- end
-
- # Generate all necessary parts of our ssl host.
- def generate
- generate_key unless key
- generate_certificate_request unless certificate_request
-
- # If we can get a CA instance, then we're a valid CA, and we
- # should use it to sign our request; else, just try to read
- # the cert.
- if ! certificate and ca = Puppet::SSL::CertificateAuthority.instance
- ca.sign(self.name)
- end
- end
-
- def initialize(name = nil)
- @name = (name || Puppet[:certname]).downcase
- @key = @certificate = @certificate_request = nil
- @ca = (name == self.class.ca_name)
- end
-
- # Extract the public key from the private key.
- def public_key
- key.content.public_key
+ @ssl_store
+ end
+
+ # Attempt to retrieve a cert, if we don't already have one.
+ def wait_for_cert(time)
+ begin
+ return if certificate
+ generate
+ return if certificate
+ rescue SystemExit,NoMemoryError
+ raise
+ rescue Exception => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err "Could not request certificate: #{detail}"
+ if time < 1
+ puts "Exiting; failed to retrieve certificate and waitforcert is disabled"
+ exit(1)
+ else
+ sleep(time)
+ end
+ retry
end
- # Create/return a store that uses our SSL info to validate
- # connections.
- def ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY)
- unless @ssl_store
- @ssl_store = OpenSSL::X509::Store.new
- @ssl_store.purpose = purpose
-
- # Use the file path here, because we don't want to cause
- # a lookup in the middle of setting our ssl connection.
- @ssl_store.add_file(Puppet[:localcacert])
-
- # If there's a CRL, add it to our store.
- if crl = Puppet::SSL::CertificateRevocationList.find(CA_NAME)
- @ssl_store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
- @ssl_store.add_crl(crl.content)
- end
- return @ssl_store
- end
- @ssl_store
+ if time < 1
+ puts "Exiting; no certificate found and waitforcert is disabled"
+ exit(1)
end
- # Attempt to retrieve a cert, if we don't already have one.
- def wait_for_cert(time)
- begin
- return if certificate
- generate
- return if certificate
- rescue SystemExit,NoMemoryError
- raise
- rescue Exception => detail
- puts detail.backtrace if Puppet[:trace]
- Puppet.err "Could not request certificate: #{detail}"
- if time < 1
- puts "Exiting; failed to retrieve certificate and waitforcert is disabled"
- exit(1)
- else
- sleep(time)
- end
- retry
- end
-
- if time < 1
- puts "Exiting; no certificate found and waitforcert is disabled"
- exit(1)
- end
-
- while true
- sleep time
- begin
- break if certificate
- Puppet.notice "Did not receive certificate"
- rescue StandardError => detail
- puts detail.backtrace if Puppet[:trace]
- Puppet.err "Could not request certificate: #{detail}"
- end
- end
+ while true
+ sleep time
+ begin
+ break if certificate
+ Puppet.notice "Did not receive certificate"
+ rescue StandardError => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err "Could not request certificate: #{detail}"
+ end
end
+ end
end
require 'puppet/ssl/certificate_authority'
diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb
index 6fb2ea8c2..b2b402a53 100644
--- a/lib/puppet/ssl/inventory.rb
+++ b/lib/puppet/ssl/inventory.rb
@@ -3,50 +3,50 @@ require 'puppet/ssl/certificate'
# Keep track of all of our known certificates.
class Puppet::SSL::Inventory
- attr_reader :path
+ attr_reader :path
- # Add a certificate to our inventory.
- def add(cert)
- cert = cert.content if cert.is_a?(Puppet::SSL::Certificate)
+ # Add a certificate to our inventory.
+ def add(cert)
+ cert = cert.content if cert.is_a?(Puppet::SSL::Certificate)
- # Create our file, if one does not already exist.
- rebuild unless FileTest.exist?(@path)
+ # Create our file, if one does not already exist.
+ rebuild unless FileTest.exist?(@path)
- Puppet.settings.write(:cert_inventory, "a") do |f|
- f.print format(cert)
- end
+ Puppet.settings.write(:cert_inventory, "a") do |f|
+ f.print format(cert)
end
+ end
- # Format our certificate for output.
- def format(cert)
- iso = '%Y-%m-%dT%H:%M:%S%Z'
- "0x%04x %s %s %s\n" % [cert.serial, cert.not_before.strftime(iso), cert.not_after.strftime(iso), cert.subject]
- end
-
- def initialize
- @path = Puppet[:cert_inventory]
- end
+ # Format our certificate for output.
+ def format(cert)
+ iso = '%Y-%m-%dT%H:%M:%S%Z'
+ "0x%04x %s %s %s\n" % [cert.serial, cert.not_before.strftime(iso), cert.not_after.strftime(iso), cert.subject]
+ end
- # Rebuild the inventory from scratch. This should happen if
- # the file is entirely missing or if it's somehow corrupted.
- def rebuild
- Puppet.notice "Rebuilding inventory file"
+ def initialize
+ @path = Puppet[:cert_inventory]
+ end
- Puppet.settings.write(:cert_inventory) do |f|
- f.print "# Inventory of signed certificates\n# SERIAL NOT_BEFORE NOT_AFTER SUBJECT\n"
- end
+ # Rebuild the inventory from scratch. This should happen if
+ # the file is entirely missing or if it's somehow corrupted.
+ def rebuild
+ Puppet.notice "Rebuilding inventory file"
- Puppet::SSL::Certificate.search("*").each { |cert| add(cert) }
+ Puppet.settings.write(:cert_inventory) do |f|
+ f.print "# Inventory of signed certificates\n# SERIAL NOT_BEFORE NOT_AFTER SUBJECT\n"
end
- # Find the serial number for a given certificate.
- def serial(name)
- return nil unless FileTest.exist?(@path)
+ 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}$/
+ File.readlines(@path).each do |line|
+ next unless line =~ /^(\S+).+\/CN=#{name}$/
- return Integer($1)
- end
+ return Integer($1)
end
+ end
end
diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb
index cb03729c1..0ddc9623c 100644
--- a/lib/puppet/ssl/key.rb
+++ b/lib/puppet/ssl/key.rb
@@ -3,54 +3,54 @@ require 'puppet/indirector'
# Manage private and public keys as a pair.
class Puppet::SSL::Key < Puppet::SSL::Base
- wraps OpenSSL::PKey::RSA
+ wraps OpenSSL::PKey::RSA
- extend Puppet::Indirector
- indirects :key, :terminus_class => :file
+ extend Puppet::Indirector
+ indirects :key, :terminus_class => :file
- # Because of how the format handler class is included, this
- # can't be in the base class.
- def self.supported_formats
- [:s]
- end
+ # Because of how the format handler class is included, this
+ # can't be in the base class.
+ def self.supported_formats
+ [:s]
+ end
- attr_accessor :password_file
+ attr_accessor :password_file
- # Knows how to create keys with our system defaults.
- def generate
- Puppet.info "Creating a new SSL key for #{name}"
- @content = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
- end
+ # Knows how to create keys with our system defaults.
+ def generate
+ Puppet.info "Creating a new SSL key for #{name}"
+ @content = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
+ end
- def initialize(name)
- super
+ def initialize(name)
+ super
- if ca?
- @password_file = Puppet[:capass]
- else
- @password_file = Puppet[:passfile]
- end
+ if ca?
+ @password_file = Puppet[:capass]
+ else
+ @password_file = Puppet[:passfile]
end
+ end
- def password
- return nil unless password_file and FileTest.exist?(password_file)
+ def password
+ return nil unless password_file and FileTest.exist?(password_file)
- ::File.read(password_file)
- end
+ ::File.read(password_file)
+ end
- # Optionally support specifying a password file.
- def read(path)
- return super unless password_file
+ # Optionally support specifying a password file.
+ def read(path)
+ return super unless password_file
- #@content = wrapped_class.new(::File.read(path), password)
- @content = wrapped_class.new(::File.read(path), password)
- end
+ #@content = wrapped_class.new(::File.read(path), password)
+ @content = wrapped_class.new(::File.read(path), password)
+ end
- def to_s
- if pass = password
- @content.export(OpenSSL::Cipher::DES.new(:EDE3, :CBC), pass)
- else
- return super
- end
+ def to_s
+ if pass = password
+ @content.export(OpenSSL::Cipher::DES.new(:EDE3, :CBC), pass)
+ else
+ return super
end
+ end
end