[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, upstream, updated. 0.25.5-639-g8f94f35
James Turnbull
james at lovedthanlost.net
Wed Jul 14 10:29:45 UTC 2010
The following commit has been merged in the upstream branch:
commit a9fb82b0026e75a670fec553b17de3b0f091c2a5
Author: Brice Figureau <brice-puppet at daysofwonder.com>
Date: Mon Dec 28 19:41:24 2009 +0100
Feature #2839 - fingerprint certificate
This patch adds two things:
* certificate fingerprinting in --list mode
* a puppetca action called "--fingerprint" to display fingerprints
of given certificates
It is also possible to use --digest to specify a specific digest
algorithm.
Signed-off-by: Brice Figureau <brice-puppet at daysofwonder.com>
diff --git a/lib/puppet/application/puppetca.rb b/lib/puppet/application/puppetca.rb
index adc1a6f..6ef8677 100644
--- a/lib/puppet/application/puppetca.rb
+++ b/lib/puppet/application/puppetca.rb
@@ -6,7 +6,7 @@ Puppet::Application.new(:puppetca) do
should_parse_config
- attr_accessor :mode, :all, :ca
+ attr_accessor :mode, :all, :ca, :digest
def find_mode(opt)
modes = Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS
@@ -22,6 +22,10 @@ Puppet::Application.new(:puppetca) do
@all = true
end
+ option("--digest DIGEST", "-d") do |arg|
+ @digest = arg
+ end
+
option("--debug", "-d") do |arg|
Puppet::Util::Log.level = :debug
end
@@ -44,7 +48,7 @@ Puppet::Application.new(:puppetca) do
end
begin
@ca.apply(:revoke, :to => hosts) if @mode == :destroy
- @ca.apply(@mode, :to => hosts)
+ @ca.apply(@mode, :to => hosts, :digest => @digest)
rescue => detail
puts detail.backtrace if Puppet[:trace]
puts detail.to_s
diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb
index 8e4fd7a..f221370 100644
--- a/lib/puppet/ssl/certificate_authority.rb
+++ b/lib/puppet/ssl/certificate_authority.rb
@@ -53,7 +53,7 @@ class Puppet::SSL::CertificateAuthority
unless options[:to]
raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all"
end
- applier = Interface.new(method, options[:to])
+ applier = Interface.new(method, options)
applier.apply(self)
end
@@ -291,6 +291,27 @@ class Puppet::SSL::CertificateAuthority
end
end
+ def fingerprint(name, md = :MD5)
+ unless cert = Puppet::SSL::Certificate.find(name)
+ raise ArgumentError, "Could not find a certificate for %s" % name
+ end
+
+ 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.hexdigest(md, cert.content.to_der).scan(/../).join(':').upcase
+ end
+
# List the waiting certificate requests.
def waiting?
Puppet::SSL::CertificateRequest.search("*").collect { |r| r.name }
diff --git a/lib/puppet/ssl/certificate_authority/interface.rb b/lib/puppet/ssl/certificate_authority/interface.rb
index 3f91434..0023808 100644
--- a/lib/puppet/ssl/certificate_authority/interface.rb
+++ b/lib/puppet/ssl/certificate_authority/interface.rb
@@ -2,11 +2,11 @@
# 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.
class Puppet::SSL::CertificateAuthority::Interface
- INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify]
+ INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify, :fingerprint]
class InterfaceError < ArgumentError; end
- attr_reader :method, :subjects
+ attr_reader :method, :subjects, :digest
# Actually perform the work.
def apply(ca)
@@ -38,9 +38,10 @@ class Puppet::SSL::CertificateAuthority::Interface
end
end
- def initialize(method, subjects)
+ def initialize(method, options)
self.method = method
- self.subjects = subjects
+ self.subjects = options[:to]
+ @digest = options[:digest] || :MD5
end
# List the hosts.
@@ -67,9 +68,9 @@ class Puppet::SSL::CertificateAuthority::Interface
invalid = details.to_s
end
if not invalid and signed.include?(host)
- puts "+ " + host
+ puts "+ #{host} (#{ca.fingerprint(host, @digest)})"
elsif invalid
- puts "- " + host + " (" + invalid + ")"
+ puts "- #{host} (#{ca.fingerprint(host, @digest)}) (#{invalid})"
else
puts host
end
@@ -93,6 +94,17 @@ class Puppet::SSL::CertificateAuthority::Interface
end
end
+ # Print certificate information.
+ def fingerprint(ca)
+ (subjects == :all ? ca.list : subjects).each do |host|
+ if value = ca.fingerprint(host, @digest)
+ puts "#{host} #{value}"
+ else
+ Puppet.err "Could not find certificate for %s" % host
+ end
+ end
+ end
+
# Sign a given certificate.
def sign(ca)
list = subjects == :all ? ca.waiting? : subjects
diff --git a/sbin/puppetca b/sbin/puppetca
index 27ba916..eab594b 100755
--- a/sbin/puppetca
+++ b/sbin/puppetca
@@ -10,7 +10,8 @@
#
# puppetca [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
# [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke]
-# [-p|--print] [-c|--clean] [--verify] [host]
+# [-p|--print] [-c|--clean] [--verify] [--digest DIGEST]
+# [--fingerprint] [host]
#
# = Description
#
@@ -35,6 +36,11 @@
# Operate on all items. Currently only makes sense with '--sign',
# '--clean', or '--list'.
#
+# digest::
+# Set the digest for fingerprinting (defaults to md5). Valid values depends
+# on your openssl and openssl ruby extension version, but should contain at
+# least md5, sha1, md2, sha256.
+#
# clean::
# Remove all files related to a host from puppetca's storage. This is
# useful when rebuilding hosts, since new certificate signing requests
@@ -62,6 +68,9 @@
# print::
# Print the full-text version of a host's certificate.
#
+# fingerprint::
+# Print the DIGEST (defaults to md5) fingerprint of a host's certificate.
+#
# revoke::
# Revoke the certificate of a client. The certificate can be specified
# either by its serial number, given as a decimal number or a hexadecimal
diff --git a/spec/unit/application/puppetca.rb b/spec/unit/application/puppetca.rb
index 3a535f3..132a03c 100644
--- a/spec/unit/application/puppetca.rb
+++ b/spec/unit/application/puppetca.rb
@@ -39,6 +39,12 @@ describe "PuppetCA" do
@puppetca.handle_debug(0)
end
+ it "should set the fingerprint digest with the --digest option" do
+ @puppetca.handle_digest(:digest)
+
+ @puppetca.digest.should == :digest
+ end
+
it "should set mode to :destroy for --clean" do
@puppetca.handle_clean(0)
@puppetca.mode.should == :destroy
@@ -129,6 +135,15 @@ describe "PuppetCA" do
@puppetca.main
end
+ it "should send the currently set digest" do
+ ARGV.stubs(:collect).returns(["host"])
+ @puppetca.handle_digest(:digest)
+
+ @ca.expects(:apply).with { |mode,to| to[:digest] == :digest}
+
+ @puppetca.main
+ end
+
it "should delegate to ca.apply with current set mode" do
@puppetca.mode = "currentmode"
ARGV.stubs(:collect).returns(["host"])
diff --git a/spec/unit/ssl/certificate_authority.rb b/spec/unit/ssl/certificate_authority.rb
index 8011430..6c917ff 100755
--- a/spec/unit/ssl/certificate_authority.rb
+++ b/spec/unit/ssl/certificate_authority.rb
@@ -532,9 +532,9 @@ describe Puppet::SSL::CertificateAuthority do
lambda { @ca.apply(:generate) }.should raise_error(ArgumentError)
end
- it "should create an Interface instance with the specified method and the subjects" do
- Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :hosts).returns(stub('applier', :apply => nil))
- @ca.apply(:generate, :to => :hosts)
+ it "should create an Interface instance with the specified method and the options" do
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil))
+ @ca.apply(:generate, :to => :host)
end
it "should apply the Interface with itself as the argument" do
@@ -583,6 +583,38 @@ describe Puppet::SSL::CertificateAuthority do
end
end
+ describe "and fingerprinting certificates" do
+ before :each do
+ @der = stub 'der', :to_der => "DER"
+ @cert = stub 'cert', :name => "cert", :content => @der
+ Puppet::SSL::Certificate.stubs(:find).with("myhost").returns @cert
+ OpenSSL::Digest.stubs(:constants).returns ["MD5", "DIGEST"]
+ end
+
+ it "should raise an error if the certificate cannot be found" do
+ Puppet::SSL::Certificate.expects(:find).with("myhost").returns nil
+ lambda { @ca.fingerprint("myhost") }.should raise_error
+ end
+
+ it "should digest the certificate DER value and return a ':' seperated nibblet string" do
+ OpenSSL::Digest.expects(:hexdigest).with("MD5", "DER").returns "digest"
+
+ @ca.fingerprint("myhost").should == "DI:GE:ST"
+ end
+
+ it "should raise an error if the digest algorithm is not defined" do
+ OpenSSL::Digest.expects(:constants).returns []
+
+ lambda { @ca.fingerprint("myhost") }.should raise_error
+ end
+
+ it "should use the given digest algorithm" do
+ OpenSSL::Digest.expects(:hexdigest).with("DIGEST", "DER").returns "digest"
+
+ @ca.fingerprint("myhost", :digest).should == "DI:GE:ST"
+ end
+ end
+
describe "and verifying certificates" do
before do
@store = stub 'store', :verify => true, :add_file => nil, :purpose= => nil, :add_crl => true, :flags= => nil
diff --git a/spec/unit/ssl/certificate_authority/interface.rb b/spec/unit/ssl/certificate_authority/interface.rb
index d741ec4..4b0a4ef 100755
--- a/spec/unit/ssl/certificate_authority/interface.rb
+++ b/spec/unit/ssl/certificate_authority/interface.rb
@@ -9,7 +9,7 @@ describe "a normal interface method", :shared => true do
@ca.expects(@method).with("host1")
@ca.expects(@method).with("host2")
- @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, %w{host1 host2})
+ @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 host2})
@applier.apply(@ca)
end
@@ -20,7 +20,7 @@ describe "a normal interface method", :shared => true do
@ca.expects(@method).with("host1")
@ca.expects(@method).with("host2")
- @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :all)
+ @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all)
@applier.apply(@ca)
end
@@ -33,30 +33,40 @@ describe Puppet::SSL::CertificateAuthority::Interface do
describe "when initializing" do
it "should set its method using its settor" do
@class.any_instance.expects(:method=).with(:generate)
- @class.new(:generate, :all)
+ @class.new(:generate, :to => :all)
end
it "should set its subjects using the settor" do
@class.any_instance.expects(:subjects=).with(:all)
- @class.new(:generate, :all)
+ @class.new(:generate, :to => :all)
+ end
+
+ it "should set the digest if given" do
+ interface = @class.new(:generate, :to => :all, :digest => :digest)
+ interface.digest.should == :digest
+ end
+
+ it "should set the digest to md5 if none given" do
+ interface = @class.new(:generate, :to => :all)
+ interface.digest.should == :MD5
end
end
describe "when setting the method" do
it "should set the method" do
- @class.new(:generate, :all).method.should == :generate
+ @class.new(:generate, :to => :all).method.should == :generate
end
it "should fail if the method isn't a member of the INTERFACE_METHODS array" do
Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.expects(:include?).with(:thing).returns false
- lambda { @class.new(:thing, :all) }.should raise_error(ArgumentError)
+ lambda { @class.new(:thing, :to => :all) }.should raise_error(ArgumentError)
end
end
describe "when setting the subjects" do
it "should set the subjects" do
- @class.new(:generate, :all).subjects.should == :all
+ @class.new(:generate, :to => :all).subjects.should == :all
end
it "should fail if the subjects setting isn't :all or an array" do
@@ -65,7 +75,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
end
it "should have a method for triggering the application" do
- @class.new(:generate, :all).should respond_to(:apply)
+ @class.new(:generate, :to => :all).should respond_to(:apply)
end
describe "when applying" do
@@ -75,7 +85,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
end
it "should raise InterfaceErrors" do
- @applier = @class.new(:revoke, :all)
+ @applier = @class.new(:revoke, :to => :all)
@ca.expects(:list).raises Puppet::SSL::CertificateAuthority::Interface::InterfaceError
@@ -83,7 +93,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
end
it "should log non-Interface failures rather than failing" do
- @applier = @class.new(:revoke, :all)
+ @applier = @class.new(:revoke, :to => :all)
@ca.expects(:list).raises ArgumentError
@@ -94,19 +104,19 @@ describe Puppet::SSL::CertificateAuthority::Interface do
describe "with an empty array specified and the method is not list" do
it "should fail" do
- @applier = @class.new(:sign, [])
+ @applier = @class.new(:sign, :to => [])
lambda { @applier.apply(@ca) }.should raise_error(ArgumentError)
end
end
describe ":generate" do
it "should fail if :all was specified" do
- @applier = @class.new(:generate, :all)
+ @applier = @class.new(:generate, :to => :all)
lambda { @applier.apply(@ca) }.should raise_error(ArgumentError)
end
it "should call :generate on the CA for each host specified" do
- @applier = @class.new(:generate, %w{host1 host2})
+ @applier = @class.new(:generate, :to => %w{host1 host2})
@ca.expects(:generate).with("host1")
@ca.expects(:generate).with("host2")
@@ -141,7 +151,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
describe ":sign" do
describe "and an array of names was provided" do
before do
- @applier = @class.new(:sign, %w{host1 host2})
+ @applier = @class.new(:sign, :to => %w{host1 host2})
end
it "should sign the specified waiting certificate requests" do
@@ -159,14 +169,14 @@ describe Puppet::SSL::CertificateAuthority::Interface do
@ca.expects(:sign).with("cert1")
@ca.expects(:sign).with("cert2")
- @applier = @class.new(:sign, :all)
+ @applier = @class.new(:sign, :to => :all)
@applier.apply(@ca)
end
it "should fail if there are no waiting certificate requests" do
@ca.stubs(:waiting?).returns([])
- @applier = @class.new(:sign, :all)
+ @applier = @class.new(:sign, :to => :all)
lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError)
end
end
@@ -178,7 +188,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
@ca.expects(:waiting?).returns %w{host1 host2}
@ca.stubs(:verify)
- @applier = @class.new(:list, [])
+ @applier = @class.new(:list, :to => [])
@applier.expects(:puts).with "host1\nhost2"
@@ -191,14 +201,15 @@ describe Puppet::SSL::CertificateAuthority::Interface do
@ca.expects(:waiting?).returns %w{host1 host2}
@ca.expects(:list).returns %w{host3 host4}
@ca.stubs(:verify)
+ @ca.stubs(:fingerprint).returns "fingerprint"
@ca.expects(:verify).with("host3").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked")
- @applier = @class.new(:list, :all)
+ @applier = @class.new(:list, :to => :all)
@applier.expects(:puts).with "host1"
@applier.expects(:puts).with "host2"
- @applier.expects(:puts).with "- host3 (certificate revoked)"
- @applier.expects(:puts).with "+ host4"
+ @applier.expects(:puts).with "- host3 (fingerprint) (certificate revoked)"
+ @applier.expects(:puts).with "+ host4 (fingerprint)"
@applier.apply(@ca)
end
@@ -208,14 +219,15 @@ describe Puppet::SSL::CertificateAuthority::Interface do
it "should print a string of all named hosts that have a waiting request" do
@ca.expects(:waiting?).returns %w{host1 host2}
@ca.expects(:list).returns %w{host3 host4}
+ @ca.stubs(:fingerprint).returns "fingerprint"
@ca.stubs(:verify)
- @applier = @class.new(:list, %w{host1 host2 host3 host4})
+ @applier = @class.new(:list, :to => %w{host1 host2 host3 host4})
@applier.expects(:puts).with "host1"
@applier.expects(:puts).with "host2"
- @applier.expects(:puts).with "+ host3"
- @applier.expects(:puts).with "+ host4"
+ @applier.expects(:puts).with "+ host3 (fingerprint)"
+ @applier.expects(:puts).with "+ host4 (fingerprint)"
@applier.apply(@ca)
end
@@ -227,7 +239,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
it "should print all certificates" do
@ca.expects(:list).returns %w{host1 host2}
- @applier = @class.new(:print, :all)
+ @applier = @class.new(:print, :to => :all)
@ca.expects(:print).with("host1").returns "h1"
@applier.expects(:puts).with "h1"
@@ -241,7 +253,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
describe "and an array of names was provided" do
it "should print each named certificate if found" do
- @applier = @class.new(:print, %w{host1 host2})
+ @applier = @class.new(:print, :to => %w{host1 host2})
@ca.expects(:print).with("host1").returns "h1"
@applier.expects(:puts).with "h1"
@@ -253,7 +265,7 @@ describe Puppet::SSL::CertificateAuthority::Interface do
end
it "should log any named but not found certificates" do
- @applier = @class.new(:print, %w{host1 host2})
+ @applier = @class.new(:print, :to => %w{host1 host2})
@ca.expects(:print).with("host1").returns "h1"
@applier.expects(:puts).with "h1"
@@ -265,5 +277,46 @@ describe Puppet::SSL::CertificateAuthority::Interface do
end
end
end
+
+ describe ":fingerprint" do
+ it "should fingerprint with the set digest algorithm" do
+ @applier = @class.new(:fingerprint, :to => %w{host1}, :digest => :digest)
+
+ @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @applier.apply(@ca)
+ end
+
+ describe "and :all was provided" do
+ it "should fingerprint all certificates" do
+ @ca.expects(:list).returns %w{host1 host2}
+
+ @applier = @class.new(:fingerprint, :to => :all)
+
+ @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2"
+ @applier.expects(:puts).with "host2 fingerprint2"
+
+ @applier.apply(@ca)
+ end
+ end
+
+ describe "and an array of names was provided" do
+ it "should print each named certificate if found" do
+ @applier = @class.new(:fingerprint, :to => %w{host1 host2})
+
+ @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1"
+ @applier.expects(:puts).with "host1 fingerprint1"
+
+ @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2"
+ @applier.expects(:puts).with "host2 fingerprint2"
+
+ @applier.apply(@ca)
+ end
+ end
+ end
end
end
--
Puppet packaging for Debian
More information about the Pkg-puppet-devel
mailing list