6
# Stand-alone certificate authority. Capable of generating certificates
7
# but mostly meant for signing certificate requests from puppet clients.
11
# puppetca [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
12
# [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke]
13
# [-p|--print] [-c|--clean] [--verify] [host]
17
# Because the puppetmasterd daemon defaults to not signing client certificate
18
# requests, this script is available for signing outstanding requests. It
19
# can be used to list outstanding requests and then either sign them individually
20
# or sign all of them.
24
# Note that any configuration parameter that's valid in the configuration file
25
# is also a valid long argument. For example, 'ssldir' is a valid configuration
26
# parameter, so you can specify '--ssldir <directory>' as an argument.
28
# See the configuration file documentation at
29
# http://reductivelabs.com/projects/puppet/reference/configref.html for
30
# the full list of acceptable parameters. A commented list of all
31
# configuration options can also be generated by running puppetca with
35
# Operate on all items. Currently only makes sense with '--sign',
36
# '--clean', or '--list'.
39
# Remove all files related to a host from puppetca's storage. This is
40
# useful when rebuilding hosts, since new certificate signing requests
41
# will only be honored if puppetca does not have a copy of a signed
42
# certificate for that host. The certificate of the host remains valid.
43
# If '--all' is specified then all host certificates, both signed and
44
# unsigned, will be removed.
47
# Enable full debugging.
50
# Generate a certificate for a named client. A certificate/keypair will be
51
# generated for each client named on the command line.
54
# Print this help message
57
# List outstanding certificate requests. If '--all' is specified,
58
# signed certificates are also listed, prefixed by '+'.
61
# Print the full-text version of a host's certificate.
64
# Revoke the certificate of a client. The certificate can be specified
65
# either by its serial number, given as a decimal number or a hexadecimal
66
# number prefixed by '0x', or by its hostname. The certificate is revoked
67
# by adding it to the Certificate Revocation List given by the 'cacrl'
68
# config parameter. Note that the puppetmasterd needs to be restarted
69
# after revoking certificates.
72
# Sign an outstanding certificate request. Unless '--all' is specified,
73
# hosts must be listed after all flags.
79
# Print the puppet version number and exit.
82
# Verify the named certificate against the local CA certificate.
88
# $ puppetca -s culain.madstop.com
96
# Copyright (c) 2005 Reductive Labs, LLC
97
# Licensed under the GNU Public License
100
require 'puppet/sslcertificates'
104
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
105
[ "--clean", "-c", GetoptLong::NO_ARGUMENT ],
106
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
107
[ "--generate", "-g", GetoptLong::NO_ARGUMENT ],
108
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
109
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
110
[ "--print", "-p", GetoptLong::NO_ARGUMENT ],
111
[ "--revoke", "-r", GetoptLong::NO_ARGUMENT ],
112
[ "--sign", "-s", GetoptLong::NO_ARGUMENT ],
113
[ "--verify", GetoptLong::NO_ARGUMENT ],
114
[ "--version", "-V", GetoptLong::NO_ARGUMENT ],
115
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ]
118
# Add all of the config parameters as valid options.
119
Puppet.settings.addargs(options)
121
result = GetoptLong.new(*options)
127
modes = [:clean, :list, :revoke, :generate, :sign, :print, :verify]
130
result.each { |opt,arg|
135
Puppet::Util::Log.level = :debug
140
if Puppet.features.usage?
143
puts "No help available unless you have RDoc::usage installed"
153
puts "%s" % Puppet.version
156
Puppet::Util::Log.level = :info
158
tmp = opt.sub("--", '').to_sym
159
if modes.include?(tmp)
162
Puppet.settings.handlearg(opt, arg)
166
rescue GetoptLong::InvalidOption => detail
167
$stderr.puts "Try '#{$0} --help'"
171
# Now parse the config
174
if Puppet.settings.print_configs?
175
exit(Puppet.settings.print_configs ? 0 : 1)
179
ca = Puppet::SSLCertificates::CA.new()
182
puts detail.backtrace
189
$stderr.puts "You must specify a mode; see the output from --help"
193
if [:verify, :print, :generate, :clean, :revoke, :list].include?(mode)
194
hosts = ARGV.collect { |h| h.downcase }
197
if [:sign, :list].include?(mode)
199
unless waiting.length > 0 or (mode == :list and all)
200
puts "No certificates to sign"
212
if waiting.length > 0
213
puts waiting.join("\n")
216
puts ca.list_signed.collect { |cert | cert.sub(/^/,"+ ") }.join("\n")
219
if hosts.empty? and all == false
220
$stderr.puts "You must specify one or more hosts to clean or --all to clean all host certificates"
228
certs |= ca.list_signed
230
$stderr.puts "No certificates to clean"
240
unless cert = ca.getclientcert(host)[0] || ca.getclientcsr(host)
241
$stderr.puts "Could not find client certificate or request for %s" % host
254
to_sign = ARGV.collect { |h| h.downcase }
255
unless to_sign.length > 0 or all
257
"You must specify one or more hosts to sign certificates for or --all to sign all certificates"
263
to_sign.each { |host|
264
unless waiting.include?(host)
265
$stderr.puts "No waiting request for %s" % host
268
waiting = waiting.find_all { |host|
269
to_sign.include?(host)
273
waiting.each { |host|
275
csr = ca.getclientcsr(host)
277
$stderr.puts "Could not retrieve request for %s: %s" % [host, detail]
282
$stderr.puts "Signed %s" % host
284
$stderr.puts "Could not sign request for %s: %s" % [host, detail]
288
ca.removeclientcsr(host)
290
$stderr.puts "Could not remove request for %s: %s" % [host, detail]
294
# we need to generate a certificate for a host
296
puts "Generating certificate for %s" % host
297
cert = Puppet::SSLCertificates::Certificate.new(
301
signedcert, cacert = ca.sign(cert.csr)
303
cert.cert = signedcert
309
cert = ca.getclientcert(h)[0]
315
if h =~ /^0x[0-9a-f]+$/
317
elsif h =~ /^[0-9]+$/
320
cert = ca.getclientcert(h)[0]
322
$stderr.puts "Could not find client certificate for %s" % h
329
puts "Revoked certificate with serial #{serial}"
333
unless ssl = %x{which openssl}.chomp
334
raise "Can't verify certificates without the openssl binary and could not find one"
338
cacert = Puppet[:localcacert]
342
file = ca.host2certfile(host)
343
unless FileTest.exist?(file)
344
puts "no certificate found"
350
command = %{#{ssl} verify -CAfile #{cacert} #{file}}
351
output = %x{#{command}}
360
$stderr.puts "Invalid mode %s" % mode