1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
require 'puppet/sslcertificates'
# A module to handle reading of certificates.
module Puppet::SSLCertificates::Support
class MissingCertificate < Puppet::Error; end
class InvalidCertificate < Puppet::Error; end
attr_reader :cacert
# Some metaprogramming to create methods for retrieving and creating keys.
# This probably isn't fewer lines than defining each separately...
def self.keytype(name, options, &block)
var = "@%s" % name
maker = "mk_%s" % name
reader = "read_%s" % name
unless param = options[:param]
raise ArgumentError, "You must specify the parameter for the key"
end
unless klass = options[:class]
raise ArgumentError, "You must specify the class for the key"
end
# Define the method that creates it.
define_method(maker, &block)
# Define the reading method.
define_method(reader) do
return nil unless FileTest.exists?(Puppet[param])
begin
instance_variable_set(var,
klass.new(File.read(Puppet[param])))
rescue => detail
raise InvalidCertificate, "Could not read %s: %s" %
[param, detail]
end
end
# Define the overall method, which just calls the reader and maker
# as appropriate.
define_method(name) do
unless instance_variable_get(var)
unless cert = send(reader)
cert = send(maker)
Puppet.settings.write(param) { |f| f.puts cert.to_pem }
end
instance_variable_set(var, cert)
end
instance_variable_get(var)
end
end
# The key pair.
keytype :key, :param => :hostprivkey, :class => OpenSSL::PKey::RSA do
Puppet.info "Creating a new SSL key at %s" % Puppet[:hostprivkey]
key = OpenSSL::PKey::RSA.new(Puppet[:keylength])
# Our key meta programming can only handle one file, so we have
# to separately write out the public key.
Puppet.settings.write(:hostpubkey) do |f|
f.print key.public_key.to_pem
end
return key
end
# Our certificate request
keytype :csr, :param => :hostcsr, :class => OpenSSL::X509::Request do
Puppet.info "Creating a new certificate request for %s" %
Puppet[:certname]
csr = OpenSSL::X509::Request.new
csr.version = 0
csr.subject = OpenSSL::X509::Name.new([["CN", Puppet[:certname]]])
csr.public_key = key.public_key
csr.sign(key, OpenSSL::Digest::MD5.new)
return csr
end
keytype :cert, :param => :hostcert, :class => OpenSSL::X509::Certificate do
raise MissingCertificate, "No host certificate"
end
keytype :ca_cert, :param => :localcacert, :class => OpenSSL::X509::Certificate do
raise MissingCertificate, "No CA certificate"
end
# Request a certificate from the remote system. This does all of the work
# of creating the cert request, contacting the remote system, and
# storing the cert locally.
def requestcert
begin
cert, cacert = caclient.getcert(@csr.to_pem)
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
raise Puppet::Error.new("Certificate retrieval failed: %s" %
detail)
end
if cert.nil? or cert == ""
return nil
end
Puppet.settings.write(:hostcert) do |f| f.print cert end
Puppet.settings.write(:localcacert) do |f| f.print cacert end
#File.open(@certfile, "w", 0644) { |f| f.print cert }
#File.open(@cacertfile, "w", 0644) { |f| f.print cacert }
begin
@cert = OpenSSL::X509::Certificate.new(cert)
@cacert = OpenSSL::X509::Certificate.new(cacert)
retrieved = true
rescue => detail
raise Puppet::Error.new(
"Invalid certificate: %s" % detail
)
end
unless @cert.check_private_key(@key)
raise Puppet::DevError, "Received invalid certificate"
end
return retrieved
end
end
|