diff options
-rw-r--r-- | lib/puppet.rb | 1 | ||||
-rw-r--r-- | lib/puppet/client.rb | 137 | ||||
-rwxr-xr-x | lib/puppet/daemon.rb | 91 | ||||
-rwxr-xr-x | lib/puppet/sslcertificates.rb | 85 | ||||
-rw-r--r-- | test/client/tc_client.rb | 8 |
5 files changed, 197 insertions, 125 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb index 1a46cba01..a4507e09f 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -76,6 +76,7 @@ module Puppet # then the files}, :manifest => [:puppetconf, "manifest.pp"], + :localconfig => [:puppetconf, "localconfig.ma"], :logfile => [:logdir, "puppet.log"], :httplogfile => [:logdir, "http.log"], :masterlog => [:logdir, "puppetmaster.log"], diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb index efeee68c2..81e64d66f 100644 --- a/lib/puppet/client.rb +++ b/lib/puppet/client.rb @@ -1,7 +1,5 @@ #!/usr/local/bin/ruby -w -# $Id$ - # the available clients require 'puppet' @@ -32,7 +30,7 @@ module Puppet Puppet.err "Could not load client network libs: %s" % $noclientnetworking else class NetworkClient < XMLRPC::Client - include Puppet::Daemon + #include Puppet::Daemon @@methods = [ :getconfig, :getcert ] @@ -40,8 +38,13 @@ module Puppet self.send(:define_method,method) { |*args| begin call("puppetmaster.%s" % method.to_s,*args) + rescue XMLRPC::FaultException => detail + Puppet.err "XML Could not call %s: %s" % + [method, detail.faultString] + raise NetworkClientError.new(detail.to_s) rescue => detail - raise NetworkClientError.new(detail) + Puppet.err "Could not call %s: %s" % [method, detail.inspect] + raise NetworkClientError.new(detail.to_s) end } } @@ -50,15 +53,26 @@ module Puppet hash[:Path] ||= "/RPC2" hash[:Server] ||= "localhost" hash[:Port] ||= Puppet[:masterport] - super(hash[:Server],hash[:Path],hash[:Port]) + super( + hash[:Server], + hash[:Path], + hash[:Port], + nil, # proxy_host + nil, # proxy_port + nil, # user + nil, # password + true) # use_ssl + + # from here, i need to add the key, cert, and ca cert + # and reorgize how i start the client end end end class Client include Puppet + include Puppet::Daemon attr_accessor :local, :secureinit - attr_reader :fqdn def Client.facts facts = {} @@ -103,76 +117,12 @@ module Puppet if hash.include?(:FQDN) @fqdn = hash[:FQDN] else - hostname = Facter["hostname"].value - domain = Facter["domain"].value - @fqdn = [hostname, domain].join(".") + self.fqdn end @secureinit = hash[:NoSecureInit] || true end - def initcerts - return unless @secureinit - # verify we've got all of the certs set up and such - - # we are not going to encrypt our key, but we need at a minimum - # a keyfile and a certfile - certfile = File.join(Puppet[:certdir], [@fqdn, "pem"].join(".")) - keyfile = File.join(Puppet[:privatekeydir], [@fqdn, "pem"].join(".")) - publickeyfile = File.join(Puppet[:publickeydir], [@fqdn, "pem"].join(".")) - - [Puppet[:certdir], Puppet[:privatekeydir], Puppet[:csrdir], - Puppet[:publickeydir]].each { |dir| - unless FileTest.exists?(dir) - Puppet.recmkdir(dir, 0770) - end - } - - inited = false - if File.exists?(keyfile) - # load the key - @key = OpenSSL::PKey::RSA.new(File.read(keyfile)) - else - # create a new one and store it - Puppet.info "Creating a new SSL key at %s" % keyfile - @key = OpenSSL::PKey::RSA.new(Puppet[:keylength]) - File.open(keyfile, "w", 0660) { |f| f.print @key.to_pem } - File.open(publickeyfile, "w", 0660) { |f| - f.print @key.public_key.to_pem - } - end - - unless File.exists?(certfile) - Puppet.info "Creating a new certificate request for %s" % @fqdn - name = OpenSSL::X509::Name.new([["CN", @fqdn]]) - - @csr = OpenSSL::X509::Request.new - @csr.version = 0 - @csr.subject = name - @csr.public_key = @key.public_key - @csr.sign(@key, OpenSSL::Digest::MD5.new) - - Puppet.info "Requesting certificate" - - cert = @driver.getcert(@csr.to_pem) - - if cert.nil? - raise Puppet::Error, "Failed to get certificate" - end - File.open(certfile, "w", 0660) { |f| f.print cert } - begin - @cert = OpenSSL::X509::Certificate.new(cert) - inited = true - rescue => detail - raise Puppet::Error.new( - "Invalid certificate: %s" % detail - ) - end - end - - return inited - end - def getconfig #client.loadproperty('files/sslclient.properties') Puppet.debug("getting config") @@ -190,7 +140,27 @@ module Puppet objects = @driver.getconfig(facts) else textfacts = CGI.escape(Marshal::dump(facts)) - textobjects = CGI.unescape(@driver.getconfig(textfacts)) + textobjects = nil + if textobjects = CGI.unescape(@driver.getconfig(textfacts)) + # we store the config so that if we can't connect next time, we + # can just run against the most recently acquired copy + confdir = File.dirname(Puppet[:localconfig]) + unless FileTest.exists?(confdir) + Puppet.recmkdir(confdir, 0770) + end + File.open(Puppet[:localconfig], "w", 0660) { |f| + f.print textobjects + } + else + if FileTest.exists?(Puppet[:localconfig]) + textobjects = File.read(Puppet[:localconfig]) + else + raise Puppet::Error.new( + "Cannot connect to server and there is no cached configuration" + ) + end + end + begin objects = Marshal::load(textobjects) rescue => detail @@ -257,27 +227,8 @@ module Puppet return transaction #self.shutdown end - - private - - #def on_init - # @default_namespace = 'urn:puppet-client' - # add_method(self, 'config', 'config') - # add_method(self, 'callfunc', 'name', 'arguments') - #end - - def cert(filename) - OpenSSL::X509::Certificate.new(File.open(File.join(@dir, filename)) { |f| - f.read - }) - end - - def key(filename) - OpenSSL::PKey::RSA.new(File.open(File.join(@dir, filename)) { |f| - f.read - }) - end - end #--------------------------------------------------------------- end + +# $Id$ diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb index 76b6cc829..e20cf4aa2 100755 --- a/lib/puppet/daemon.rb +++ b/lib/puppet/daemon.rb @@ -29,6 +29,15 @@ module Puppet end end + def fqdn + unless defined? @fqdn and @fqdn + hostname = Facter["hostname"].value + domain = Facter["domain"].value + @fqdn = [hostname, domain].join(".") + end + return @fqdn + end + def httplog args = [] # yuck; separate http logs @@ -44,6 +53,88 @@ module Puppet return log end + + def initcerts + return unless @secureinit + # verify we've got all of the certs set up and such + + # we are not going to encrypt our key, but we need at a minimum + # a keyfile and a certfile + certfile = File.join(Puppet[:certdir], [@fqdn, "pem"].join(".")) + cacertfile = File.join(Puppet[:certdir], ["ca", "pem"].join(".")) + keyfile = File.join(Puppet[:privatekeydir], [@fqdn, "pem"].join(".")) + publickeyfile = File.join(Puppet[:publickeydir], [@fqdn, "pem"].join(".")) + + [Puppet[:certdir], Puppet[:privatekeydir], Puppet[:csrdir], + Puppet[:publickeydir]].each { |dir| + unless FileTest.exists?(dir) + Puppet.recmkdir(dir, 0770) + end + } + + inited = false + if File.exists?(keyfile) + # load the key + @key = OpenSSL::PKey::RSA.new(File.read(keyfile)) + else + # create a new one and store it + Puppet.info "Creating a new SSL key at %s" % keyfile + @key = OpenSSL::PKey::RSA.new(Puppet[:keylength]) + File.open(keyfile, "w", 0660) { |f| f.print @key.to_pem } + File.open(publickeyfile, "w", 0660) { |f| + f.print @key.public_key.to_pem + } + end + + if File.exists?(certfile) + unless File.exists?(cacertfile) + raise Puppet::Error, "Found cert file with no ca cert file" + end + @cert = OpenSSL::X509::Certificate.new(File.read(certfile)) + inited = true + else + unless defined? @driver + Puppet.err "Cannot request a certificate without a defined target" + return false + end + Puppet.info "Creating a new certificate request for %s" % @fqdn + name = OpenSSL::X509::Name.new([["CN", @fqdn]]) + + @csr = OpenSSL::X509::Request.new + @csr.version = 0 + @csr.subject = name + @csr.public_key = @key.public_key + @csr.sign(@key, OpenSSL::Digest::MD5.new) + + Puppet.info "Requesting certificate" + + begin + cert, cacert = @driver.getcert(@csr.to_pem) + rescue => detail + raise Puppet::Error.new("Certificate retrieval failed: %s" % detail) + end + + if cert.nil? or cert == "" + return nil + end + File.open(certfile, "w", 0660) { |f| f.print cert } + File.open(cacertfile, "w", 0660) { |f| f.print cacert } + begin + @cert = OpenSSL::X509::Certificate.new(cert) + @cacert = OpenSSL::X509::Certificate.new(cacert) + inited = true + rescue => detail + raise Puppet::Error.new( + "Invalid certificate: %s" % detail + ) + end + end + + unless @cert.check_private_key(@key) + raise Puppet::DevError, "Received invalid certificate" + end + return inited + end end end diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb index 13392dedf..1ccade1d8 100755 --- a/lib/puppet/sslcertificates.rb +++ b/lib/puppet/sslcertificates.rb @@ -49,7 +49,7 @@ module SSLCertificates raise ArgumentError, "mkcert called without %s" % param end } - cert = ::OpenSSL::X509::Certificate.new + cert = OpenSSL::X509::Certificate.new from = Time.now cert.subject = hash[:name] @@ -89,13 +89,13 @@ module SSLCertificates key_usage << %w{nonRepudiation digitalSignature keyEncipherment} ext_key_usage << %w{clientAuth emailProtection} else - raise "unknonwn cert type '%s'" % hash[:type] + raise Puppet::Error, "unknown cert type '%s'" % hash[:type] end key_usage.flatten! ext_key_usage.flatten! - ef = ::OpenSSL::X509::ExtensionFactory.new + ef = OpenSSL::X509::ExtensionFactory.new if hash[:issuer] ef.issuer_certificate = hash[:issuer] @@ -108,7 +108,7 @@ module SSLCertificates ex = [] ex << ef.create_extension("basicConstraints", basic_constraint, true) ex << ef.create_extension("nsComment", - "Ruby/OpenSSL Generated Certificate") + "Puppet Ruby/OpenSSL Generated Certificate") ex << ef.create_extension("subjectKeyIdentifier", "hash") #ex << ef.create_extension("nsCertType", "client,email") unless key_usage.empty? then @@ -133,9 +133,6 @@ module SSLCertificates #end cert.extensions = ex - #cmd = "#{ossl} req -nodes -new -x509 -keyout %s -out %s -config %s" % - # [@key, certfile, Puppet::SSLCertificates.config] - # write the cert out #File.open(certfile, "w") { |f| f << cert.to_pem } @@ -193,6 +190,10 @@ module SSLCertificates Puppet.setdefault(param,@@defaults[param]) } + def certfile + @config[:cacert] + end + def host2csrfile(hostname) File.join(Puppet[:csrdir], [hostname, "pem"].join(".")) end @@ -215,7 +216,7 @@ module SSLCertificates self.getcert unless FileTest.exists?(@config[:serial]) File.open(@config[:serial], "w") { |f| - f << "%04X" % 0 + f << "%04X" % 1 } end @@ -258,10 +259,10 @@ module SSLCertificates def getclientcert(host) certfile = host2certfile(host) unless File.exists?(certfile) - return nil + return [nil, nil] end - return OpenSSL::X509::Certificate.new(File.read(certfile)) + return [OpenSSL::X509::Certificate.new(File.read(certfile)), @cert] end def list @@ -341,24 +342,36 @@ module SSLCertificates def sign(csr) unless csr.is_a?(OpenSSL::X509::Request) - raise "CA#sign only accepts OpenSSL::X509::Request objects, not %s" % + raise Puppet::Error, + "CA#sign only accepts OpenSSL::X509::Request objects, not %s" % csr.class end unless csr.verify(csr.public_key) - raise "CSR sign verification failed" + raise Puppet::Error, "CSR sign verification failed" end # i should probably check key length... # read the ca cert in - cacert = ::OpenSSL::X509::Certificate.new( + cacert = OpenSSL::X509::Certificate.new( File.read(@config[:cacert]) ) - ca_keypair = ::OpenSSL::PKey::RSA.new( - File.read(@config[:cakey]), @config[:password] - ) + 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 = SSLCertificates.mkcert( @@ -367,7 +380,7 @@ module SSLCertificates :days => @config[:ca_days], :issuer => cacert, :serial => serial, - :publickey => ca_keypair.public_key + :publickey => csr.public_key ) # increment the serial @@ -375,11 +388,11 @@ module SSLCertificates f << "%04X" % (serial + 1) } - newcert.sign(ca_keypair, ::OpenSSL::Digest::SHA1.new) + newcert.sign(cakey, OpenSSL::Digest::SHA1.new) self.storeclientcert(newcert) - return newcert + return [newcert, cacert] end def storeclientcsr(csr) @@ -425,7 +438,7 @@ module SSLCertificates } def certname - ::OpenSSL::X509::Name.new self.subject + OpenSSL::X509::Name.new self.subject end def delete @@ -451,12 +464,12 @@ module SSLCertificates self.mkkey() end if @password - @key = ::OpenSSL::PKey::RSA.new( + @key = OpenSSL::PKey::RSA.new( File.read(@keyfile), @password ) else - @key = ::OpenSSL::PKey::RSA.new( + @key = OpenSSL::PKey::RSA.new( File.read(@keyfile) ) end @@ -549,18 +562,22 @@ module SSLCertificates self.getkey end - name = ::OpenSSL::X509::Name.new self.subject + name = OpenSSL::X509::Name.new self.subject - @csr = ::OpenSSL::X509::Request.new + @csr = OpenSSL::X509::Request.new @csr.version = 0 @csr.subject = name @csr.public_key = @key.public_key - @csr.sign(@key, ::OpenSSL::Digest::MD5.new) + @csr.sign(@key, OpenSSL::Digest::SHA1.new) #File.open(@csrfile, "w") { |f| # f << @csr.to_pem #} + unless @csr.verify(@key.public_key) + raise Puppet::Error, "CSR sign verification failed" + end + return @csr end @@ -597,12 +614,24 @@ module SSLCertificates def mkkey # @key is the file - @key = ::OpenSSL::PKey::RSA.new 1024 + @key = OpenSSL::PKey::RSA.new(1024) +# { |p,n| +# case p +# when 0; Puppet.info "key info: ." # BN_generate_prime +# when 1; Puppet.info "key info: +" # BN_generate_prime +# when 2; Puppet.info "key info: *" # searching good prime, +# # n = #of try, +# # but also data from BN_generate_prime +# when 3; Puppet.info "key info: \n" # found good prime, n==0 - p, n==1 - q, +# # but also data from BN_generate_prime +# else; Puppet.info "key info: *" # BN_generate_prime +# end +# } if @password #passwdproc = proc { @password } keytext = @key.export( - ::OpenSSL::Cipher::DES.new(:EDE3, :CBC), + OpenSSL::Cipher::DES.new(:EDE3, :CBC), @password ) File.open(@keyfile, "w", 0400) { |f| @@ -635,7 +664,7 @@ module SSLCertificates :publickey => @key.public_key ) - @cert.sign(@key, ::OpenSSL::Digest::SHA1.new) if @selfsign + @cert.sign(@key, OpenSSL::Digest::SHA1.new) if @selfsign end def subject(string = false) diff --git a/test/client/tc_client.rb b/test/client/tc_client.rb index 2ddf53c98..bbb251a6d 100644 --- a/test/client/tc_client.rb +++ b/test/client/tc_client.rb @@ -80,9 +80,8 @@ class TestClient < Test::Unit::TestCase client = Puppet::Client.new(:Server => server) } certfile = File.join(Puppet[:certdir], [client.fqdn, "pem"].join(".")) - assert_raise(Puppet::Error) { - client.initcerts - } + cafile = File.join(Puppet[:certdir], ["ca", "pem"].join(".")) + assert_nil(client.initcerts) assert(! File.exists?(certfile)) ca = nil @@ -100,8 +99,9 @@ class TestClient < Test::Unit::TestCase cert = nil assert_nothing_raised { - cert = ca.sign(csr) + cert, cacert = ca.sign(csr) File.open(certfile, "w") { |f| f.print cert.to_pem } + File.open(cafile, "w") { |f| f.print cacert.to_pem } } # this time it should get the cert correctly |