summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorlutter <lutter@980ebf18-57e1-0310-9a29-db15c13687c0>2006-08-21 21:54:13 +0000
committerlutter <lutter@980ebf18-57e1-0310-9a29-db15c13687c0>2006-08-21 21:54:13 +0000
commit7ade561e75853116baef15f3750e3563e6a6faaf (patch)
treea64b6e3f1cb2038386f60fb2774bb5a191ebc24e /lib
parentc6fc6c56cea381c7bdf15e8610a28a4c6924ecf5 (diff)
downloadpuppet-7ade561e75853116baef15f3750e3563e6a6faaf.tar.gz
puppet-7ade561e75853116baef15f3750e3563e6a6faaf.tar.xz
puppet-7ade561e75853116baef15f3750e3563e6a6faaf.zip
Support for certificate revocation and checking connections on the server against the CRL
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1475 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
-rw-r--r--lib/puppet/server.rb17
-rw-r--r--lib/puppet/sslcertificates/ca.rb107
2 files changed, 97 insertions, 27 deletions
diff --git a/lib/puppet/server.rb b/lib/puppet/server.rb
index 107bf83a2..4a949b712 100644
--- a/lib/puppet/server.rb
+++ b/lib/puppet/server.rb
@@ -44,6 +44,22 @@ module Puppet
@authconfig
end
+
+ # Read the CA cert and CRL and populate an OpenSSL::X509::Store
+ # with them, with flags appropriate for checking client
+ # certificates for revocation
+ def x509store
+ unless File.exist?(Puppet[:cacrl])
+ raise Puppet::Error, "Could not find CRL"
+ end
+ crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl]))
+ store = OpenSSL::X509::Store.new
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK
+ store.add_cert(@cacert)
+ store.add_crl(crl)
+ return store
+ end
def initialize(hash = {})
Puppet.info "Starting server for Puppet version %s" % Puppet.version
@@ -97,6 +113,7 @@ module Puppet
end
end
+ hash[:SSLCertificateStore] = x509store
hash[:SSLCertificate] = @cert
hash[:SSLPrivateKey] = @key
hash[:SSLStartImmediately] = true
diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb
index 72b073378..89b7b183c 100644
--- a/lib/puppet/sslcertificates/ca.rb
+++ b/lib/puppet/sslcertificates/ca.rb
@@ -1,6 +1,6 @@
class Puppet::SSLCertificates::CA
Certificate = Puppet::SSLCertificates::Certificate
- attr_accessor :keyfile, :file, :config, :dir, :cert
+ attr_accessor :keyfile, :file, :config, :dir, :cert, :crl
Puppet.setdefaults(:ca,
:cadir => { :default => "$ssldir/ca",
@@ -26,6 +26,12 @@ class Puppet::SSLCertificates::CA
:group => "$group",
:desc => "The CA public key."
},
+ :cacrl => { :default => "$cadir/ca_crl.pem",
+ :owner => "$user",
+ :group => "$group",
+ :mode => 0664,
+ :desc => "The certificate revocation list (CRL) for the CA."
+ },
:caprivatedir => { :default => "$cadir/private",
:owner => "$user",
:group => "$group",
@@ -130,6 +136,7 @@ class Puppet::SSLCertificates::CA
end
self.getcert
+ init_crl
unless FileTest.exists?(@config[:serial])
Puppet.config.write(:serial) do |f|
f << "%04X" % 1
@@ -223,7 +230,6 @@ class Puppet::SSLCertificates::CA
Puppet.config.write(:cacert) do |f|
f.puts @cert.to_pem
end
- @key = cert.key
return cert
end
@@ -278,34 +284,12 @@ class Puppet::SSLCertificates::CA
raise Puppet::Error, "CSR sign verification failed"
end
- # i should probably check key length...
-
- # read the ca cert in
- cacert = OpenSSL::X509::Certificate.new(
- File.read(@config[:cacert])
- )
-
- cakey = nil
- if @config[:password]
- cakey = OpenSSL::PKey::RSA.new(
- File.read(@config[:cakey]), @config[:password]
- )
- else
- cakey = OpenSSL::PKey::RSA.new(
- File.read(@config[:cakey])
- )
- end
-
- unless cacert.check_private_key(cakey)
- raise Puppet::Error, "CA Certificate is invalid"
- end
-
serial = File.read(@config[:serial]).chomp.hex
newcert = Puppet::SSLCertificates.mkcert(
:type => :server,
:name => csr.subject,
:days => @config[:ca_days],
- :issuer => cacert,
+ :issuer => @cert,
:serial => serial,
:publickey => csr.public_key
)
@@ -315,11 +299,11 @@ class Puppet::SSLCertificates::CA
f << "%04X" % (serial + 1)
end
- newcert.sign(cakey, OpenSSL::Digest::SHA1.new)
+ sign_with_key(newcert)
self.storeclientcert(newcert)
- return [newcert, cacert]
+ return [newcert, @cert]
end
# Store the client's CSR for later signing. This is called from
@@ -338,6 +322,20 @@ class Puppet::SSLCertificates::CA
end
end
+ # Revoke the certificate with serial number SERIAL issued by this
+ # CA. The REASON must be one of the OpenSSL::OCSP::REVOKED_* reasons
+ def revoke(serial, reason = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)
+ time = Time.now
+ 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)
+ @crl.add_revoked(revoked)
+ store_crl
+ end
+
# Store the certificate that we generate.
def storeclientcert(cert)
host = thing2name(cert)
@@ -352,6 +350,61 @@ class Puppet::SSLCertificates::CA
f.print cert.to_pem
end
end
+
+ private
+ def init_crl
+ if FileTest.exists?(@config[:cacrl])
+ @crl = OpenSSL::X509::CRL.new(
+ File.read(@config[:cacrl])
+ )
+ else
+ # Create new CRL
+ @crl = OpenSSL::X509::CRL.new
+ @crl.issuer = @cert.subject
+ @crl.version = 1
+ store_crl
+ @crl
+ end
+ end
+
+ def store_crl
+ # Increment the crlNumber
+ e = @crl.extensions.find { |e| e.oid == 'crlNumber' }
+ ext = @crl.extensions.reject { |e| e.oid == 'crlNumber' }
+ crlNum = OpenSSL::ASN1::Integer(e ? e.value.to_i + 1 : 0)
+ ext << OpenSSL::X509::Extension.new("crlNumber", crlNum)
+ @crl.extensions = ext
+
+ # Set last/next update
+ now = Time.now
+ @crl.last_update = now
+ # Keep CRL valid for 5 years
+ @crl.next_update = now + 5 * 365*24*60*60
+
+ sign_with_key(@crl)
+ Puppet.config.write(:cacrl) do |f|
+ f.puts @crl.to_pem
+ end
+ end
+
+ def sign_with_key(signable, digest = OpenSSL::Digest::SHA1.new)
+ cakey = nil
+ if @config[:password]
+ cakey = OpenSSL::PKey::RSA.new(
+ File.read(@config[:cakey]), @config[:password]
+ )
+ else
+ cakey = OpenSSL::PKey::RSA.new(
+ File.read(@config[:cakey])
+ )
+ end
+
+ unless @cert.check_private_key(cakey)
+ raise Puppet::Error, "CA Certificate is invalid"
+ end
+
+ signable.sign(cakey, digest)
+ end
end
# $Id$