summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet.rb31
-rw-r--r--lib/puppet/client.rb88
-rw-r--r--lib/puppet/selector.rb1
-rwxr-xr-xlib/puppet/sslcertificates.rb791
-rw-r--r--lib/puppet/transportable.rb4
-rw-r--r--lib/puppet/type.rb8
-rw-r--r--lib/puppet/type/package.rb1
-rw-r--r--lib/puppet/type/pfile.rb5
-rw-r--r--test/client/tc_client.rb123
-rw-r--r--test/other/tc_selector.rb7
-rwxr-xr-xtest/puppet/tc_defaults.rb6
-rw-r--r--test/types/tc_service.rb5
12 files changed, 557 insertions, 513 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb
index d499e7596..2fb9ffbbe 100644
--- a/lib/puppet.rb
+++ b/lib/puppet.rb
@@ -2,6 +2,8 @@
# $Id$
+$VERBOSE = true
+
require 'singleton'
require 'puppet/log'
@@ -35,9 +37,9 @@ module Puppet
str = @message
end
- if Puppet[:debug] and @stack
- str += @stack
- end
+ #if Puppet[:debug] and @stack
+ # str += @stack.to_s
+ #end
return str
end
@@ -57,6 +59,7 @@ module Puppet
}
# I keep wanting to use Puppet.error
+ # XXX this isn't actually working right now
alias :error :err
@defaults = {
@@ -70,19 +73,23 @@ module Puppet
:logfile => [:logdir, "puppet.log"],
:masterlog => [:logdir, "puppetmaster.log"],
:checksumfile => [:statedir, "checksums"],
- :certdir => [:puppetconf, "certs"],
- :rootcert => [:certdir, "ca.crt"],
- :rootkey => [:certdir, "ca.key"],
- :rootpub => [:certdir, "ca.pub"],
- :localcert => [:certdir, "localhost.crt"],
- :localkey => [:certdir, "localhost.key"],
- :localpub => [:certdir, "localhost.pub"],
+ :ssldir => [:puppetconf, "ssl"],
+# :certdir => [:ssldir, "certs"],
+# :publickeydir => [:ssldir, "public_keys"],
+# :privatekeydir => [:ssldir, "private_keys"],
+# :csrdir => [:ssldir, "requests"],
+# :cadir => [:ssldir, "ca"],
+# :cacert => [:cadir, "ca.crt"],
+# :cakey => [:cadir, "ca.key"],
+# :capub => [:cadir, "ca.pub"],
+# :localcert => [:ssldir, "localhost.crt"],
+# :localkey => [:ssldir, "localhost.key"],
+# :localpub => [:ssldir, "localhost.pub"],
# and finally the simple answers,
:server => "puppet",
:rrdgraph => false,
:noop => false,
- :autosign => false,
:parseonly => false,
:puppetport => 8139,
:masterport => 8140,
@@ -182,6 +189,8 @@ module Puppet
end
end
+ # XXX this should all be done using puppet objects, not using
+ # normal mkdir
def self.recmkdir(dir,mode = 0755)
tmp = dir.sub(/^\//,'')
path = [File::SEPARATOR]
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb
index 288782193..199e59b29 100644
--- a/lib/puppet/client.rb
+++ b/lib/puppet/client.rb
@@ -5,10 +5,10 @@
# the available clients
require 'puppet'
-require 'puppet/function'
+require 'puppet/sslcertificates'
require 'puppet/type'
-#require 'puppet/fact'
require 'facter'
+require 'openssl'
require 'puppet/transaction'
require 'puppet/transportable'
require 'puppet/metric'
@@ -54,7 +54,9 @@ module Puppet
class Client
include Puppet
- attr_accessor :local
+ attr_accessor :local, :secureinit
+ attr_reader :fqdn
+
def Client.facts
facts = {}
Facter.each { |name,fact|
@@ -71,7 +73,7 @@ module Puppet
if hash.include?(:Server)
case hash[:Server]
when String:
- if $nonetworking
+ if $noclientnetworking
raise NetworkClientError.new("Networking not available: %s" %
$nonetworking)
end
@@ -94,6 +96,72 @@ module Puppet
else
raise ClientError.new("Must pass :Server to client")
end
+
+ if hash.include?(:FQDN)
+ @fqdn = hash[:FQDN]
+ else
+ hostname = Facter["hostname"].value
+ domain = Facter["domain"].value
+ @fqdn = [hostname, domain].join(".")
+ 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
+ }
+ 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)
+ rescue => detail
+ raise Puppet::Error.new(
+ "Invalid certificate: %s" % detail
+ )
+ end
+ end
end
def getconfig
@@ -181,18 +249,6 @@ module Puppet
#self.shutdown
end
- #def callfunc(name,args)
- # Puppet.debug("Calling callfunc on %s" % name)
- # if function = Puppet::Function[name]
- # #debug("calling function %s" % function)
- # value = function.call(args)
- # #debug("from %s got %s" % [name,value])
- # return value
- # else
- # raise "Function '%s' not found" % name
- # end
- #end
-
private
#def on_init
diff --git a/lib/puppet/selector.rb b/lib/puppet/selector.rb
index 6bfa1e2dd..50e55fb24 100644
--- a/lib/puppet/selector.rb
+++ b/lib/puppet/selector.rb
@@ -3,7 +3,6 @@
# $Id$
require 'puppet'
-require 'puppet/fact'
module Puppet
#---------------------------------------------------------------
diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb
index 1f1619a67..13392dedf 100755
--- a/lib/puppet/sslcertificates.rb
+++ b/lib/puppet/sslcertificates.rb
@@ -8,31 +8,9 @@
require 'puppet'
require 'openssl'
-require 'getoptlong'
module Puppet
-module OpenSSL
- @@config = "/etc/ssl/openssl.cnf"
-
- def self.config=(config)
- @@config = config
- end
-
- def self.config
- return @@config
- end
-
- def self.exec(cmd)
- output = %x{#{cmd} 2>&1}
-
- #puts "\n\n%s\n\n" % cmd
- unless $? == 0
- puts cmd
- raise output
- end
- return output
- end
-
+module SSLCertificates
def self.mkdir(dir)
# this is all a bunch of stupid hackery
unless FileTest.exists?(dir)
@@ -64,367 +42,309 @@ module OpenSSL
Puppet::Type.allclear
end
-
- class Config
- include Enumerable
- attr_accessor :path
-
- def [](name)
- @sectionhash[name]
- end
-
- def clear
- @sectionhash.clear
- @sectionary.clear
+ #def self.mkcert(type, name, days, issuercert, issuername, serial, publickey)
+ def self.mkcert(hash)
+ [:type, :name, :days, :issuer, :serial, :publickey].each { |param|
+ unless hash.include?(param)
+ raise ArgumentError, "mkcert called without %s" % param
+ end
+ }
+ cert = ::OpenSSL::X509::Certificate.new
+ from = Time.now
+
+ cert.subject = hash[:name]
+ if hash[:issuer]
+ cert.issuer = hash[:issuer].subject
+ else
+ cert.issuer = cert.subject
end
-
- def each
- @sectionary.each { |section|
- yield section
- }
+ cert.not_before = from
+ cert.not_after = from + (hash[:days] * 24 * 60 * 60)
+ cert.version = 2 # X509v3
+
+ cert.public_key = hash[:publickey]
+ cert.serial = hash[:serial]
+
+ basic_constraint = nil
+ key_usage = []
+ ext_key_usage = []
+
+ case hash[:type]
+ when :ca:
+ basic_constraint = "CA:TRUE"
+ key_usage.push %w{cRLSign keyCertSign}
+ when :terminalsubca:
+ basic_constraint = "CA:TRUE,pathlen:0"
+ key_usage %w{cRLSign keyCertSign}
+ when :server:
+ basic_constraint = "CA:FALSE"
+ key_usage << %w{digitalSignature keyEncipherment}
+ ext_key_usage << "serverAuth"
+ when :ocsp:
+ basic_constraint = "CA:FALSE"
+ key_usage << %w{nonRepudiation digitalSignature}
+ ext_key_usage << %w{serverAuth OCSPSigning}
+ when :client:
+ basic_constraint = "CA:FALSE"
+ key_usage << %w{nonRepudiation digitalSignature keyEncipherment}
+ ext_key_usage << %w{clientAuth emailProtection}
+ else
+ raise "unknonwn cert type '%s'" % hash[:type]
end
- def initialize(path)
- @path = path
+ key_usage.flatten!
+ ext_key_usage.flatten!
- @sectionhash = {}
- @sectionary = []
+ ef = ::OpenSSL::X509::ExtensionFactory.new
- # default to reading the config in
- if FileTest.exists?(@path)
- self.read
- end
+ if hash[:issuer]
+ ef.issuer_certificate = hash[:issuer]
+ else
+ ef.issuer_certificate = cert
end
- def newsection(name)
- sect = Section.new(name)
- @sectionhash[sect.name] = sect
- @sectionary.push sect
- return sect
- end
-
- def read
- self.clear
- section = self.newsection(:HEAD)
- comments = ""
- File.open(@path) { |f|
- f.readlines.each { |line|
- case line
- when /^\s*#/: comments += line
- when /^\[ (\w+) \]$/:
- name = $1
- section = self.newsection(name)
- when /^(\w+)\s*=\s*([^#]+)(#.*)*$/
- section.newparam($1, $2, comments)
- comments = ""
- when /^(\d+\.\w+)\s*=\s*([^#]+)(#.*)*$/
- section.newparam($1, $2, comments)
- comments = ""
- when /^\s*$/: # nothing
- comments += line
- else
- puts "Could not match line %s" % line.inspect
- end
- }
- }
- end
+ ef.subject_certificate = cert
- def to_s
- @sectionary.collect { |section|
- section.to_s
- }.join("\n") + "\n"
+ ex = []
+ ex << ef.create_extension("basicConstraints", basic_constraint, true)
+ ex << ef.create_extension("nsComment",
+ "Ruby/OpenSSL Generated Certificate")
+ ex << ef.create_extension("subjectKeyIdentifier", "hash")
+ #ex << ef.create_extension("nsCertType", "client,email")
+ unless key_usage.empty? then
+ ex << ef.create_extension("keyUsage", key_usage.join(","))
end
-
- def write
- File.open(@path, "w") { |f|
- f.print self.to_s
- }
+ #ex << ef.create_extension("authorityKeyIdentifier",
+ # "keyid:always,issuer:always")
+ #ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
+ unless ext_key_usage.empty? then
+ ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
end
- class Section
- include Enumerable
- attr_accessor :name
-
- def [](name)
- @paramhash[name]
- end
-
- def []=(name,value)
- if @paramhash.include?(name)
- @paramhash[name] = value
- else
- self.newparam(name, value)
- end
- end
-
- def each
- @paramary.each { |param|
- yield param
- }
- end
-
- def include?(name)
- @paramhash.include?(name)
- end
-
- def initialize(name)
- @name = name
-
- @paramhash = {}
- @paramary = []
- end
- def newparam(name, value, comments = nil)
- if @paramhash.include?(name)
- raise "%s already has a param %s" % [@name, name]
- end
- obj = Parameter.new(name, value, comments)
- @paramhash[name] = obj
- @paramary.push obj
- end
+ #if @ca_config[:cdp_location] then
+ # ex << ef.create_extension("crlDistributionPoints",
+ # @ca_config[:cdp_location])
+ #end
- def to_s
- str = ""
- unless @name == :HEAD
- str += "[ #{@name} ]\n"
- end
- return str + (@paramary.collect { |param|
- param.to_s
- }.join("\n"))
- end
- end
+ #if @ca_config[:ocsp_location] then
+ # ex << ef.create_extension("authorityInfoAccess",
+ # "OCSP;" << @ca_config[:ocsp_location])
+ #end
+ cert.extensions = ex
- class Parameter
- attr_accessor :name, :value, :comments
+ #cmd = "#{ossl} req -nodes -new -x509 -keyout %s -out %s -config %s" %
+ # [@key, certfile, Puppet::SSLCertificates.config]
- def initialize(name, value, comments)
- @name = name
- @value = value.sub(/\s+$/,'')
- @comments = comments
- end
+ # write the cert out
+ #File.open(certfile, "w") { |f| f << cert.to_pem }
- def to_s
- if comments and comments != ""
- return "%s%s = %s" % [@comments, @name, @value]
- else
- return "%s = %s" % [@name, @value]
- end
- end
- end
+ return cert
end
+
class CA
attr_accessor :keyfile, :file, :config, :dir, :cert
- @@DEFAULTCONF = %{#
-# Default configuration to use when one is not provided on the command line.
-#
-[ ca ]
-default_ca = local_ca
-
-#
-# Default location of directories and files needed to generate
-# certificates.
-#
-[ local_ca ]
-certificate = BASEDIR/cacert.pem
-database = BASEDIR/index.txt
-new_certs_dir = BASEDIR/certs
-private_key = BASEDIR/private/cakey.pem
-serial = BASEDIR/serial
-
-#
-# Default expiration and encryption policies for certificates.
-#
-default_crl_days = 365
-default_days = 1825
-default_md = md5
-
-policy = local_ca_policy
-x509_extensions = local_ca_extensions
-
-#
-# Default policy to use when generating server certificates. The following
-# fields must be defined in the server certificate.
-#
-[ local_ca_policy ]
-commonName = supplied
-stateOrProvinceName = supplied
-countryName = supplied
-emailAddress = supplied
-organizationName = supplied
-organizationalUnitName = supplied
-
-#
-# x509 extensions to use when generating server certificates.
-#
-[ local_ca_extensions ]
-subjectAltName = DNS:altname.somewhere.com
-basicConstraints = CA:false
-nsCertType = server
-
-#
-# The default policy to use when
-# generating the root certificate.
-#
-[ req ]
-default_bits = 2048
-default_keyfile = BASEDIR/private/cakey.pem
-default_md = md5
-
-prompt = no
-distinguished_name = root_ca_distinguished_name
-x509_extensions = root_ca_extensions
+ @@params = [
+ :certdir,
+ :publickeydir,
+ :privatekeydir,
+ :cadir,
+ :cakey,
+ :cacert,
+ :capass,
+ :capub,
+ :csrdir,
+ :signeddir,
+ :serial,
+ :privatedir,
+ :ca_crl_days,
+ :ca_days,
+ :ca_md,
+ :req_bits,
+ :keylength,
+ :autosign
+ ]
+
+ @@defaults = {
+ :certdir => [:ssldir, "certs"],
+ :publickeydir => [:ssldir, "public_keys"],
+ :privatekeydir => [:ssldir, "private_keys"],
+ :cadir => [:ssldir, "ca"],
+ :cacert => [:cadir, "ca_crt.pem"],
+ :cakey => [:cadir, "ca_key.pem"],
+ :capub => [:cadir, "ca_pub.pem"],
+ :csrdir => [:cadir, "requests"],
+ :signeddir => [:cadir, "signed"],
+ :capass => [:cadir, "ca.pass"],
+ :serial => [:cadir, "serial"],
+ :privatedir => [:ssldir, "private"],
+ :passfile => [:privatedir, "password"],
+ :autosign => [:ssldir, "autosign"],
+ :ca_crl_days => 365,
+ :ca_days => 1825,
+ :ca_md => "md5",
+ :req_bits => 2048,
+ :keylength => 1024,
+ }
-#
-# Root Certificate Authority distin- guished name. Change these fields to
-# your local environment.
-#
-[ root_ca_distinguished_name ]
-commonName = Reductive Labs Root Certificate Authority
-stateOrProvinceName = Some State
-countryName = US
-emailAddress = root@somename.somewhere.com
-organizationName = Root Certificate Authority
+ @@params.each { |param|
+ Puppet.setdefault(param,@@defaults[param])
+ }
-[ root_ca_extensions ]
-basicConstraints = CA:true
-}
- def init
- self.mkconfig
- self.mkcert
+ def host2csrfile(hostname)
+ File.join(Puppet[:csrdir], [hostname, "pem"].join("."))
end
- def initialize(hash)
- unless hash.include?(:dir)
- raise "You must specify the base directory for the CA"
- end
- @dir = hash[:dir]
-
- @file = hash[:file] || File.join(@dir, "ca.cnf")
-
- unless FileTest.exists?(@dir)
- Puppet::OpenSSL.mkdir(@dir)
- end
-
- @config = self.getconfig
-
- @certfile = @config["local_ca"]["certificate"].value.chomp
- @keyfile = @config["local_ca"]["private_key"].value.chomp
+ # this stores signed certs in a directory unrelated to
+ # normal client certs
+ def host2certfile(hostname)
+ File.join(Puppet[:signeddir], [hostname, "pem"].join("."))
+ end
- certdir = @config["local_ca"]["new_certs_dir"].value.chomp
- Puppet::OpenSSL.mkdir(certdir)
-
- @serial = @config["local_ca"]["serial"].value.chomp
- unless FileTest.exists?(@serial)
- unless FileTest.exists?(File.dirname(@serial))
- Puppet::OpenSSL.mkdir(File.dirname(@serial))
- end
- File.open(@serial, "w", 0600) { |f| f.puts "01" }
- end
-
- database = @config["local_ca"]["database"].value.chomp
- unless FileTest.exists?(database)
- unless FileTest.exists?(File.dirname(database))
- Puppet::OpenSSL.mkdir(File.dirname(database))
- end
- File.open(database, "w", 0600) { |f| f.print "" }
- end
+ def thing2name(thing)
+ thing.subject.to_a.find { |ary|
+ ary[0] == "CN"
+ }[1]
+ end
- @days = @config["local_ca"]["default_crl_days"].value.to_i || 365
-
- unless @certfile
- raise "could not retrieve cert path"
- end
+ def initialize(hash = {})
+ self.setconfig(hash)
- unless @keyfile
- raise "could not retrieve key file"
+ self.getcert
+ unless FileTest.exists?(@config[:serial])
+ File.open(@config[:serial], "w") { |f|
+ f << "%04X" % 0
+ }
end
- if hash.include?(:password)
- @password = hash[:password]
- else
- @passfile = hash[:passfile] || File.join(@dir, "private", "phrase")
- @password = self.genpass
+ if Puppet[:capass] and ! FileTest.exists?(Puppet[:capass])
+ self.genpass
end
-
- @cert = self.getcert
end
def genpass
pass = ""
20.times { pass += (rand(74) + 48).chr }
- unless @passfile
+ unless @config[:capass]
raise "No passfile"
end
- Puppet::OpenSSL.mkdir(File.dirname(@passfile))
- File.open(@passfile, "w", 0600) { |f| f.print pass }
+ Puppet::SSLCertificates.mkdir(File.dirname(@config[:capass]))
+ File.open(@config[:capass], "w", 0600) { |f| f.print pass }
return pass
end
def getcert
- if FileTest.exists?(@certfile)
- return Puppet::OpenSSL::Certificate.new(
- :name => "CAcert",
- :cert => @certfile,
- :encrypt => @passfile,
- :key => @keyfile
+ if FileTest.exists?(@config[:cacert])
+ @cert = OpenSSL::X509::Certificate.new(
+ File.read(@config[:cacert])
)
else
- return self.mkrootcert
+ self.mkrootcert
end
end
- def getconfig
- if FileTest.exists?(@file)
- return Puppet::OpenSSL::Config.new(@file)
- else
- return self.mkconfig
+ def getclientcsr(host)
+ csrfile = host2csrfile(host)
+ unless File.exists?(csrfile)
+ return nil
end
+
+ return OpenSSL::X509::Request.new(File.read(csrfile))
+ end
+
+ def getclientcert(host)
+ certfile = host2certfile(host)
+ unless File.exists?(certfile)
+ return nil
+ end
+
+ return OpenSSL::X509::Certificate.new(File.read(certfile))
+ end
+
+ def list
+ return Dir.entries(Puppet[:csrdir]).reject { |file|
+ file =~ /^\.+$/
+ }.collect { |file|
+ file.sub(/\.pem$/, '')
+ }
end
def mkrootcert
cert = Certificate.new(
:name => "CAcert",
- :cert => @certfile,
- :encrypt => @passfile,
- :key => @keyfile,
+ :cert => @config[:cacert],
+ :encrypt => @config[:passfile],
+ :key => @config[:cakey],
:selfsign => true,
:length => 1825
)
@cert = cert.mkselfsigned
+ File.open(@config[:cacert], "w", 0660) { |f|
+ f.puts @cert.to_pem
+ }
@key = cert.key
-
return cert
end
- def mkconfig
- File.open(@file, "w") { |f| f.print @@DEFAULTCONF }
+ def removeclientcsr(host)
+ csrfile = host2csrfile(host)
+ unless File.exists?(csrfile)
+ raise Puppet::Error, "No certificate request for %s" % host
+ end
- config = Puppet::OpenSSL::Config.new(@file)
+ File.unlink(csrfile)
+ end
- config.each { |section|
- section.each { |param|
- value = param.value.sub(/BASEDIR/, @dir)
- param.value = value
- }
+ def setconfig(hash)
+ @config = {}
+ @@params.each { |param|
+ if hash.include?(param)
+ begin
+ @config[param] = hash[param]
+ Puppet[param] = hash[param]
+ hash.delete(param)
+ rescue => detail
+ puts detail
+ exit
+ end
+ else
+ begin
+ @config[param] = Puppet[param]
+ rescue => detail
+ puts detail
+ exit
+ end
+ end
}
- config.write
+ if hash.include?(:password)
+ @config[:password] = hash[:password]
+ hash.delete(:password)
+ end
+
+ if hash.length > 0
+ raise ArgumentError, "Unknown parameters %s" % hash.keys.join(",")
+ end
- return config
+ [:cadir, :csrdir, :signeddir].each { |dir|
+ unless @config[dir]
+ raise "%s is undefined" % dir
+ end
+ unless FileTest.exists?(@config[dir])
+ Puppet::SSLCertificates.mkdir(@config[dir])
+ end
+ }
end
- def sign(cert)
- unless cert.is_a?(Puppet::OpenSSL::Certificate)
- raise "CA#sign only accepts Puppet::OpenSSL::Certificate objects"
+ def sign(csr)
+ unless csr.is_a?(OpenSSL::X509::Request)
+ raise "CA#sign only accepts OpenSSL::X509::Request objects, not %s" %
+ csr.class
end
- csr = ::OpenSSL::X509::Request.new(
- File.read(cert.csrfile)
- )
-
unless csr.verify(csr.public_key)
raise "CSR sign verification failed"
end
@@ -432,39 +352,66 @@ basicConstraints = CA:true
# i should probably check key length...
# read the ca cert in
- if cert.exists?
- raise "Cannot sign existing certificates"
- end
-
cacert = ::OpenSSL::X509::Certificate.new(
- File.read(@certfile)
+ File.read(@config[:cacert])
)
ca_keypair = ::OpenSSL::PKey::RSA.new(
- File.read(@keyfile), @password
+ File.read(@config[:cakey]), @config[:password]
)
- serial = File.read(@serial).chomp.hex
- newcert = cert.mkcert(
- cacert, cacert.subject, serial, ca_keypair.public_key
+ serial = File.read(@config[:serial]).chomp.hex
+ newcert = SSLCertificates.mkcert(
+ :type => :server,
+ :name => csr.subject,
+ :days => @config[:ca_days],
+ :issuer => cacert,
+ :serial => serial,
+ :publickey => ca_keypair.public_key
)
# increment the serial
- File.open(@serial, "w") { |f|
+ File.open(@config[:serial], "w") { |f|
f << "%04X" % (serial + 1)
}
newcert.sign(ca_keypair, ::OpenSSL::Digest::SHA1.new)
- File.open(cert.certfile, "w", 0644) { |f|
- f << newcert.to_pem
- }
+ self.storeclientcert(newcert)
+
return newcert
end
+
+ def storeclientcsr(csr)
+ host = thing2name(csr)
+
+ csrfile = host2csrfile(host)
+ if File.exists?(csrfile)
+ raise Puppet::Error, "Certificate request for %s already exists" % host
+ end
+
+ File.open(csrfile, "w", 0660) { |f|
+ f.print csr.to_pem
+ }
+ end
+
+ def storeclientcert(cert)
+ host = thing2name(cert)
+
+ certfile = host2certfile(host)
+ if File.exists?(certfile)
+ Puppet.notice "Overwriting signed certificate for %s" % host
+ end
+
+ File.open(certfile, "w", 0660) { |f|
+ f.print cert.to_pem
+ }
+ end
+
end
class Certificate
- attr_accessor :certfile, :keyfile, :name, :dir, :hash, :csrfile, :type
+ attr_accessor :certfile, :keyfile, :name, :dir, :hash, :type
attr_accessor :key, :cert, :csr
@@params2names = {
@@ -482,13 +429,13 @@ basicConstraints = CA:true
end
def delete
- [@certfile,@keyfile,@csrfile].each { |file|
+ [@certfile,@keyfile].each { |file|
if FileTest.exists?(file)
File.unlink(file)
end
}
- if @hash
+ if defined? @hash and @hash
if FileTest.symlink?(@hash)
File.unlink(@hash)
end
@@ -496,7 +443,7 @@ basicConstraints = CA:true
end
def exists?
- FileTest.exists?(@certfile)
+ return FileTest.exists?(@certfile)
end
def getkey
@@ -521,26 +468,39 @@ basicConstraints = CA:true
end
@name = hash[:name]
+ # init a few variables
+ @cert = @key = @csr = nil
+
if hash.include?(:cert)
@certfile = hash[:cert]
@dir = File.dirname(@certfile)
else
- @dir = hash[:dir]
- unless hash.include?(:dir)
- raise "You must specify the directory in which to store certs"
- end
+ @dir = hash[:dir] || Puppet[:certdir]
@certfile = File.join(@dir, @name)
end
unless FileTest.directory?(@dir)
- Puppet::OpenSSL.mkdir(@dir)
+ Puppet::SSLCertificates.mkdir(@dir)
end
unless @certfile =~ /\.pem$/
@certfile += ".pem"
end
- @keyfile = hash[:key] || File.join(@dir, @name + "_key.pem")
- @csrfile = hash[:csr] || File.join(@dir, @name + "_csr.pem")
+ @keyfile = hash[:key] || File.join(
+ Puppet[:privatekeydir], [@name,"pem"].join(".")
+ )
+ unless FileTest.directory?(@dir)
+ Puppet::SSLCertificates.mkdir(@dir)
+ end
+
+ [@keyfile].each { |file|
+ dir = File.dirname(file)
+
+ unless FileTest.directory?(dir)
+ Puppet::SSLCertificates.mkdir(dir)
+ end
+ }
+
@days = hash[:length] || 365
@selfsign = hash[:selfsign] || false
@encrypt = hash[:encrypt] || false
@@ -583,109 +543,9 @@ basicConstraints = CA:true
end
end
- def mkcert(issuercert, issuername, serial, publickey)
- unless issuercert or @selfsign
- raise "Certs must either have an issuer or must be self-signed"
- end
-
- if self.exists?
- raise "Cannot replace existing certificate"
- end
-
- @cert = ::OpenSSL::X509::Certificate.new
- from = Time.now
-
- @cert.subject = self.certname
- @cert.issuer = issuername
- @cert.not_before = from
- @cert.not_after = from + (@days * 24 * 60 * 60)
- @cert.version = 2 # X509v3
-
- @cert.public_key = publickey
- @cert.serial = serial
-
- basic_constraint = nil
- key_usage = []
- ext_key_usage = []
-
- case @type
- when :ca:
- basic_constraint = "CA:TRUE"
- key_usage.push %w{cRLSign keyCertSign}
- when :terminalsubca:
- basic_constraint = "CA:TRUE,pathlen:0"
- key_usage %w{cRLSign keyCertSign}
- when :server:
- basic_constraint = "CA:FALSE"
- key_usage << %w{digitalSignature keyEncipherment}
- ext_key_usage << "serverAuth"
- when :ocsp:
- basic_constraint = "CA:FALSE"
- key_usage << %w{nonRepudiation digitalSignature}
- ext_key_usage << %w{serverAuth OCSPSigning}
- when :client:
- basic_constraint = "CA:FALSE"
- key_usage << %w{nonRepudiation digitalSignature keyEncipherment}
- ext_key_usage << %w{clientAuth emailProtection}
- else
- raise "unknonwn cert type '%s'" % @type
- end
-
- key_usage.flatten!
- ext_key_usage.flatten!
-
- ef = ::OpenSSL::X509::ExtensionFactory.new
-
- if issuercert
- ef.issuer_certificate = issuercert
- else
- ef.issuer_certificate = @cert
- end
-
- ef.subject_certificate = @cert
-
- ex = []
- ex << ef.create_extension("basicConstraints", basic_constraint, true)
- ex << ef.create_extension("nsComment",
- "Ruby/OpenSSL Generated Certificate")
- ex << ef.create_extension("subjectKeyIdentifier", "hash")
- #ex << ef.create_extension("nsCertType", "client,email")
- unless key_usage.empty? then
- ex << ef.create_extension("keyUsage", key_usage.join(","))
- end
- #ex << ef.create_extension("authorityKeyIdentifier",
- # "keyid:always,issuer:always")
- #ex << ef.create_extension("authorityKeyIdentifier", "keyid:always")
- unless ext_key_usage.empty? then
- ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
- end
-
-
- #if @ca_config[:cdp_location] then
- # ex << ef.create_extension("crlDistributionPoints",
- # @ca_config[:cdp_location])
- #end
-
- #if @ca_config[:ocsp_location] then
- # ex << ef.create_extension("authorityInfoAccess",
- # "OCSP;" << @ca_config[:ocsp_location])
- #end
- @cert.extensions = ex
-
- #cmd = "#{ossl} req -nodes -new -x509 -keyout %s -out %s -config %s" %
- # [@key, @certfile, Puppet::OpenSSL.config]
-
- @cert.sign(@key, ::OpenSSL::Digest::SHA1.new) if @selfsign
-
- # write the cert out
- File.open(@certfile, "w") { |f| f << @cert.to_pem }
-
- return @cert
- end
-
# this only works for servers, not for users
def mkcsr
- unless @key
+ unless defined? @key and @key
self.getkey
end
@@ -697,14 +557,16 @@ basicConstraints = CA:true
@csr.public_key = @key.public_key
@csr.sign(@key, ::OpenSSL::Digest::MD5.new)
- File.open(@csrfile, "w") { |f|
- f << @csr.to_pem
- }
+ #File.open(@csrfile, "w") { |f|
+ # f << @csr.to_pem
+ #}
+ return @csr
end
def mkhash
- hash = Puppet::OpenSSL.exec("openssl x509 -noout -hash -in %s" % @certfile).chomp
+ hash = "%x" % @cert.issuer.hash
+ path = nil
10.times { |i|
path = File.join(@dir, "%s.%s" % [hash, i])
if FileTest.exists?(path)
@@ -728,6 +590,8 @@ basicConstraints = CA:true
@hash = path
break
}
+
+ return path
end
def mkkey
@@ -754,12 +618,24 @@ basicConstraints = CA:true
end
def mkselfsigned
- unless @key
+ unless defined? @key and @key
self.getkey
end
- self.mkcert(nil, self.certname, 0x0, @key.public_key)
-# self.mkhash
+ if defined? @cert and @cert
+ raise Puppet::Error, "Cannot replace existing certificate"
+ end
+
+ @cert = SSLCertificates.mkcert(
+ :type => :server,
+ :name => self.certname,
+ :days => @days,
+ :issuer => nil,
+ :serial => 0x0,
+ :publickey => @key.public_key
+ )
+
+ @cert.sign(@key, ::OpenSSL::Digest::SHA1.new) if @selfsign
end
def subject(string = false)
@@ -777,6 +653,31 @@ basicConstraints = CA:true
return subj
end
end
+
+ def write
+ files = {
+ @certfile => @cert,
+ @keyfile => @key,
+ }
+ #@csrfile => @csr
+
+ files.each { |file,thing|
+ if defined? thing and thing
+ if FileTest.exists?(file)
+ newtext = File.open(file) { |f| f.read }
+ if newtext != thing.to_pem
+ raise "Cannot replace existing %s" % thing.class
+ else
+ next
+ end
+ end
+
+ File.open(file, "w", 0660) { |f| f.print thing.to_pem }
+ end
+ }
+
+ self.mkhash
+ end
end
end
end
diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb
index eab0e9a53..ebabf3a27 100644
--- a/lib/puppet/transportable.rb
+++ b/lib/puppet/transportable.rb
@@ -54,7 +54,7 @@ module Puppet
if type = Puppet::Type.type(self.type)
retobj = type.new(self)
else
- raise "Could not find object type %s" % self.type
+ raise Puppet::Error.new("Could not find object type %s" % self.type)
end
return retobj
@@ -143,6 +143,7 @@ module Puppet
Puppet.err "Failed to create %s %s: %s" %
[child.type,child.name,except.message]
if Puppet[:debug]
+ puts child.inspect
puts except.stack
end
next
@@ -150,6 +151,7 @@ module Puppet
Puppet.err "Failed to create %s %s: %s" %
[child.type,child.name,except.message]
if Puppet[:debug]
+ puts child.inspect
puts caller
end
next
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index c6b9dc5b1..cacc78c28 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -701,7 +701,13 @@ class Type < Puppet::Element
order.flatten.each { |name|
if hash.include?(name)
- self[name] = hash[name]
+ begin
+ self[name] = hash[name]
+ rescue => detail
+ raise Puppet::DevError.new(
+ "Could not set %s on %s" % [name, self.class]
+ )
+ end
hash.delete name
end
}
diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb
index 77c866c08..4b618d020 100644
--- a/lib/puppet/type/package.rb
+++ b/lib/puppet/type/package.rb
@@ -3,7 +3,6 @@
# $Id$
require 'puppet/type/state'
-require 'puppet/fact'
module Puppet
class PackageError < Puppet::Error; end
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index fd5bc74b0..c0e05f5a8 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -444,7 +444,9 @@ module Puppet
if stat = @parent.stat(true)
self.is = stat.mode & 007777
unless defined? @fixed
- self.dirfix
+ if defined? @should and @should
+ self.dirfix
+ end
end
else
self.is = -1
@@ -503,7 +505,6 @@ module Puppet
end
def should=(value)
- require 'puppet/fact'
method = nil
gid = nil
gname = nil
diff --git a/test/client/tc_client.rb b/test/client/tc_client.rb
index 33f40dcf0..2ddf53c98 100644
--- a/test/client/tc_client.rb
+++ b/test/client/tc_client.rb
@@ -7,36 +7,109 @@ end
require 'puppet'
require 'puppet/client'
-#require 'puppet/server'
-require 'puppet/fact'
+require 'puppet/server'
require 'test/unit'
require 'puppettest.rb'
# $Id$
class TestClient < Test::Unit::TestCase
-# def test_local
-# client = nil
-# server = nil
-# assert_nothing_raised() {
-# server = Puppet::Master.new(
-# :File => file,
-# :Local => true
-# )
-# }
-# assert_nothing_raised() {
-# client = Puppet::Client.new(:Server => server)
-# }
-#
-# facts = %w{operatingsystem operatingsystemrelease}
-# facts.each { |fact|
-# assert_equal(
-# Puppet::Fact[fact],
-# client.callfunc("fact",fact)
-# )
-# }
-# end
-
- def test_files
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ @@tmpfiles = []
+ end
+
+ def teardown
+ Puppet::Type.allclear
+ @@tmpfiles.each { |f|
+ if FileTest.exists?(f)
+ system("rm -rf %s" % f)
+ end
+ }
+ end
+
+ def test_sslInitWithAutosigningLocalServer
+ Puppet[:autosign] = true
+ Puppet[:ssldir] = "/tmp/puppetclientcertests"
+ @@tmpfiles.push Puppet[:ssldir]
+
+ file = File.join($puppetbase, "examples", "code", "head")
+
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::Master.new(
+ :File => file,
+ :Local => true,
+ :CA => true
+ )
+ }
+ client = nil
+ assert_nothing_raised {
+ client = Puppet::Client.new(:Server => server)
+ }
+ assert_nothing_raised {
+ client.initcerts
+ }
+
+ certfile = File.join(Puppet[:certdir], [client.fqdn, "pem"].join("."))
+ keyfile = File.join(Puppet[:privatekeydir], [client.fqdn, "pem"].join("."))
+ publickeyfile = File.join(Puppet[:publickeydir], [client.fqdn, "pem"].join("."))
+
+ assert(File.exists?(keyfile))
+ assert(File.exists?(certfile))
+ assert(File.exists?(publickeyfile))
+ end
+
+ def test_sslInitWithNonsigningLocalServer
+ Puppet[:autosign] = false
+ Puppet[:ssldir] = "/tmp/puppetclientcertests"
+ @@tmpfiles.push Puppet[:ssldir]
+
+ file = File.join($puppetbase, "examples", "code", "head")
+
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::Master.new(
+ :File => file,
+ :Local => true,
+ :CA => true
+ )
+ }
+ client = nil
+ assert_nothing_raised {
+ client = Puppet::Client.new(:Server => server)
+ }
+ certfile = File.join(Puppet[:certdir], [client.fqdn, "pem"].join("."))
+ assert_raise(Puppet::Error) {
+ client.initcerts
+ }
+ assert(! File.exists?(certfile))
+
+ ca = nil
+ assert_nothing_raised {
+ ca = Puppet::SSLCertificates::CA.new()
+ }
+
+
+ csr = nil
+ assert_nothing_raised {
+ csr = ca.getclientcsr(client.fqdn)
+ }
+
+ assert(csr)
+
+ cert = nil
+ assert_nothing_raised {
+ cert = ca.sign(csr)
+ File.open(certfile, "w") { |f| f.print cert.to_pem }
+ }
+
+ # this time it should get the cert correctly
+ assert_nothing_raised {
+ client.initcerts
+ }
+
+ # this isn't a very good test, since i just wrote the file out
+ assert(File.exists?(certfile))
end
end
diff --git a/test/other/tc_selector.rb b/test/other/tc_selector.rb
index 7c8ffbd68..47e0d9ff5 100644
--- a/test/other/tc_selector.rb
+++ b/test/other/tc_selector.rb
@@ -5,14 +5,15 @@ if __FILE__ == $0
end
require 'puppet/selector'
+require 'facter'
require 'test/unit'
# $Id$
class TestSelector < Test::Unit::TestCase
def setup
- @os = Puppet::Fact["operatingsystem"]
- @hostname = Puppet::Fact["hostname"]
+ @os = Facter["operatingsystem"].value
+ @hostname = Facter["hostname"].value
Puppet[:loglevel] = :debug if __FILE__ == $0
end
@@ -22,7 +23,7 @@ class TestSelector < Test::Unit::TestCase
assert_nothing_raised() {
selector = Puppet::Selector.new { |select|
select.add("value1") {
- Puppet::Fact["hostname"] == @hostname
+ Facter["hostname"].value == @hostname
}
}
}
diff --git a/test/puppet/tc_defaults.rb b/test/puppet/tc_defaults.rb
index 06edd7873..0f46e6496 100755
--- a/test/puppet/tc_defaults.rb
+++ b/test/puppet/tc_defaults.rb
@@ -10,9 +10,9 @@ require 'test/unit'
# $Id$
class TestPuppetDefaults < Test::Unit::TestCase
- @@dirs = %w{rrddir puppetconf puppetvar logdir statedir certdir bucketdir}
- @@files = %w{logfile checksumfile localcert localkey localpub
- rootcert rootkey rootpub manifest masterlog}
+ @@dirs = %w{rrddir puppetconf puppetvar logdir statedir}
+ @@files = %w{logfile checksumfile
+ manifest masterlog}
@@normals = %w{puppetport masterport server}
@@booleans = %w{rrdgraph noop}
def testStringOrParam
diff --git a/test/types/tc_service.rb b/test/types/tc_service.rb
index a46f158a1..4bbc37594 100644
--- a/test/types/tc_service.rb
+++ b/test/types/tc_service.rb
@@ -33,6 +33,7 @@ class TestService < Test::Unit::TestCase
def teardown
Puppet::Type.allclear
+ Kernel.system("pkill sleeper")
end
def test_process_start
@@ -83,8 +84,4 @@ class TestService < Test::Unit::TestCase
)
}
end
-
- def teardown
- Kernel.system("pkill sleeper")
- end
end