275 строки
7.2 KiB
Ruby
Executable File
275 строки
7.2 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
#
|
|
# = Synopsis
|
|
#
|
|
# Stand-alone certificate authority. Capable of generating certificates
|
|
# but mostly meant for signing certificate requests from puppet clients.
|
|
#
|
|
# = Usage
|
|
#
|
|
# puppetca [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
|
|
# [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke]
|
|
# [-c|--clean] [host]
|
|
#
|
|
# = Description
|
|
#
|
|
# Because the puppetmasterd daemon defaults to not signing client certificate
|
|
# requests, this script is available for signing outstanding requests. It
|
|
# can be used to list outstanding requests and then either sign them individually
|
|
# or sign all of them.
|
|
#
|
|
# = Options
|
|
#
|
|
# Note that any configuration parameter that's valid in the configuration file
|
|
# is also a valid long argument. For example, 'ssldir' is a valid configuration
|
|
# parameter, so you can specify '--ssldir <directory>' as an argument.
|
|
#
|
|
# See the configuration file documentation at
|
|
# http://reductivelabs.com/projects/puppet/reference/configref.html for
|
|
# the full list of acceptable parameters. A commented list of all
|
|
# configuration options can also be generated by running puppetca with
|
|
# '--genconfig'.
|
|
#
|
|
# all::
|
|
# Operate on all outstanding requests. Only makes sense with '--sign'.
|
|
#
|
|
# clean::
|
|
# Remove all files related to a host from puppetca's storage. This is
|
|
# useful when rebuilding hosts, since new certificate signing requests
|
|
# will only be honored if puppetca does not have a copy of a signed
|
|
# certificate for that host. The certificate of the host remains valid.
|
|
#
|
|
# debug::
|
|
# Enable full debugging.
|
|
#
|
|
# generate::
|
|
# Generate a certificate for a named client. A certificate/keypair will be
|
|
# generated for each client named on the command line.
|
|
#
|
|
# help::
|
|
# Print this help message
|
|
#
|
|
# list::
|
|
# List outstanding certificate requests.
|
|
#
|
|
# 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
|
|
# number prefixed by '0x', or by its hostname. The certificate is revoked
|
|
# by adding it to the Certificate Revocation List given by the 'cacrl'
|
|
# config parameter. Note that the puppetmasterd needs to be restarted
|
|
# after revoking certificates.
|
|
#
|
|
# sign::
|
|
# Sign an outstanding certificate request. Unless '--all' is specified,
|
|
# hosts must be listed after all flags.
|
|
#
|
|
# verbose::
|
|
# Enable verbosity.
|
|
#
|
|
# = Example
|
|
#
|
|
# $ puppetca -l
|
|
# culain.madstop.com
|
|
# $ puppetca -s culain.madstop.com
|
|
#
|
|
# = Author
|
|
#
|
|
# Luke Kanies
|
|
#
|
|
# = Copyright
|
|
#
|
|
# Copyright (c) 2005 Reductive Labs, LLC
|
|
# Licensed under the GNU Public License
|
|
|
|
require 'puppet'
|
|
require 'puppet/sslcertificates'
|
|
require 'getoptlong'
|
|
|
|
options = [
|
|
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
|
|
[ "--clean", "-c", GetoptLong::NO_ARGUMENT ],
|
|
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
|
|
[ "--generate", "-g", GetoptLong::NO_ARGUMENT ],
|
|
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
|
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
|
|
[ "--revoke", "-r", GetoptLong::NO_ARGUMENT ],
|
|
[ "--sign", "-s", GetoptLong::NO_ARGUMENT ],
|
|
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ]
|
|
]
|
|
|
|
# Add all of the config parameters as valid options.
|
|
Puppet.config.addargs(options)
|
|
|
|
result = GetoptLong.new(*options)
|
|
|
|
mode = nil
|
|
all = false
|
|
generate = nil
|
|
|
|
begin
|
|
result.each { |opt,arg|
|
|
case opt
|
|
when "--all"
|
|
all = true
|
|
when "--clean"
|
|
mode = :clean
|
|
when "--debug"
|
|
Puppet::Util::Log.level = :debug
|
|
when "--generate"
|
|
generate = arg
|
|
mode = :generate
|
|
when "--help"
|
|
if Puppet.features.usage?
|
|
RDoc::usage && exit
|
|
else
|
|
puts "No help available unless you have RDoc::usage installed"
|
|
exit
|
|
end
|
|
when "--list"
|
|
mode = :list
|
|
when "--revoke"
|
|
mode = :revoke
|
|
when "--sign"
|
|
mode = :sign
|
|
when "--verbose"
|
|
Puppet::Util::Log.level = :info
|
|
else
|
|
Puppet.config.handlearg(opt, arg)
|
|
end
|
|
}
|
|
rescue GetoptLong::InvalidOption => detail
|
|
$stderr.puts "Try '#{$0} --help'"
|
|
exit(1)
|
|
end
|
|
|
|
# Now parse the config
|
|
if Puppet[:config] and File.exists? Puppet[:config]
|
|
Puppet.config.parse(Puppet[:config])
|
|
end
|
|
|
|
Puppet.genconfig
|
|
Puppet.genmanifest
|
|
|
|
begin
|
|
ca = Puppet::SSLCertificates::CA.new()
|
|
rescue => detail
|
|
if Puppet[:debug]
|
|
puts detail.backtrace
|
|
end
|
|
puts detail.to_s
|
|
exit(23)
|
|
end
|
|
|
|
unless mode
|
|
$stderr.puts "You must specify --list or --sign"
|
|
exit(12)
|
|
end
|
|
|
|
if [:generate, :clean, :revoke].include?(mode)
|
|
hosts = ARGV.collect { |h| h.downcase }
|
|
else
|
|
waiting = ca.list
|
|
unless waiting.length > 0
|
|
puts "No certificates to sign"
|
|
if ARGV.length > 0
|
|
exit(17)
|
|
else
|
|
exit(0)
|
|
end
|
|
end
|
|
to_sign = ARGV.collect { |h| h.downcase }
|
|
end
|
|
|
|
case mode
|
|
when :list
|
|
puts waiting.join("\n")
|
|
when :clean
|
|
if hosts.empty?
|
|
$stderr.puts "You must specify one or more hosts to clean"
|
|
exit(24)
|
|
end
|
|
hosts.each do |host|
|
|
ca.clean(host)
|
|
end
|
|
when :sign
|
|
unless to_sign.length > 0 or all
|
|
$stderr.puts(
|
|
"You must specify to sign all certificates or you must specify hostnames"
|
|
)
|
|
exit(24)
|
|
end
|
|
|
|
unless all
|
|
to_sign.each { |host|
|
|
unless waiting.include?(host)
|
|
$stderr.puts "No waiting request for %s" % host
|
|
end
|
|
}
|
|
waiting = waiting.find_all { |host|
|
|
to_sign.include?(host)
|
|
}
|
|
end
|
|
|
|
waiting.each { |host|
|
|
begin
|
|
csr = ca.getclientcsr(host)
|
|
rescue => detail
|
|
$stderr.puts "Could not retrieve request for %s: %s" % [host, detail]
|
|
end
|
|
|
|
begin
|
|
ca.sign(csr)
|
|
$stderr.puts "Signed %s" % host
|
|
rescue => detail
|
|
$stderr.puts "Could not sign request for %s: %s" % [host, detail]
|
|
end
|
|
|
|
begin
|
|
ca.removeclientcsr(host)
|
|
rescue => detail
|
|
$stderr.puts "Could not remove request for %s: %s" % [host, detail]
|
|
end
|
|
}
|
|
when :generate
|
|
# we need to generate a certificate for a host
|
|
hosts.each { |host|
|
|
puts "Generating certificate for %s" % host
|
|
cert = Puppet::SSLCertificates::Certificate.new(
|
|
:name => host
|
|
)
|
|
cert.mkcsr
|
|
signedcert, cacert = ca.sign(cert.csr)
|
|
|
|
cert.cert = signedcert
|
|
cert.cacert = cacert
|
|
cert.write
|
|
}
|
|
when :revoke
|
|
hosts.each { |h|
|
|
serial = nil
|
|
if h =~ /^0x[0-9a-f]+$/
|
|
serial = h.to_i(16)
|
|
elsif h =~ /^[0-9]+$/
|
|
serial = h.to_i
|
|
else
|
|
cert = ca.getclientcert(h)[0]
|
|
if cert.nil?
|
|
$stderr.puts "Could not find client certificate for %s" % h
|
|
else
|
|
serial = cert.serial
|
|
end
|
|
end
|
|
unless serial.nil?
|
|
ca.revoke(serial)
|
|
puts "Revoked certificate with serial #{serial}"
|
|
end
|
|
}
|
|
else
|
|
$stderr.puts "Invalid mode %s" % mode
|
|
exit(42)
|
|
end
|
|
|
|
# $Id$
|