puppetlabs-puppet/bin/puppetca

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$