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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
require 'puppet/ssl/host'
# The class that knows how to sign certificates. It's just a
# special case of the SSL::Host -- it's got a sign method,
# and it reads its info from a different location.
class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host
require 'puppet/ssl/certificate_factory'
# Provide the path to our password, and read our special ca key.
def read_key
return nil unless FileTest.exist?(Puppet[:cakey])
key = Puppet::SSL::Key.new(name)
key.password_file = Puppet[:capass]
key.read(Puppet[:cakey])
return key
end
# Generate and write the key out.
def generate_key
@key = Key.new(name)
@key.password_file = Puppet[:capass]
@key.generate
Puppet.settings.write(:cakey) do |f|
f.print @key.to_s
end
true
end
# Read the special path to our key.
def read_certificate
return nil unless FileTest.exist?(Puppet[:cacert])
cert = Puppet::SSL::Certificate.new(name)
cert.read(Puppet[:cacert])
return cert
end
# The CA creates a self-signed certificate, rather than relying
# on someone else to do the work.
def generate_certificate
request = CertificateRequest.new(name)
request.generate(key)
# Create a self-signed certificate.
@certificate = sign(name, :ca, request)
Puppet.settings.write(:cacert) do |f|
f.print @certificate.to_s
end
return true
end
def initialize
Puppet.settings.use :main, :ssl, :ca
# Always name the ca after the host we're running on.
super(Puppet[:certname])
setup_ca()
end
# Sign a given certificate request.
def sign(host, cert_type = :server, self_signing_csr = nil)
# This is a self-signed certificate
if self_signing_csr
csr = self_signing_csr
issuer = csr.content
else
raise ArgumentError, "Cannot find CA certificate; cannot sign certificate for %s" % host unless certificate
unless csr = Puppet::SSL::CertificateRequest.find(host, :in => :ca_file)
raise ArgumentError, "Could not find certificate request for %s" % host
end
issuer = certificate
end
cert = Puppet::SSL::Certificate.new(host)
cert.content = Puppet::SSL::CertificateFactory.new(cert_type, csr.content, issuer, next_serial).result
cert.content.sign(key, OpenSSL::Digest::SHA1.new)
Puppet.notice "Signed certificate request for %s" % host
# Save the now-signed cert, unless it's a self-signed cert, since we
# assume it goes somewhere else.
cert.save(:in => :ca_file) unless self_signing_csr
return cert
end
# Do all of the initialization necessary to set up our
# ca.
def setup_ca
# Make sure we've got a password protecting our private key.
generate_password unless password?
generate_key unless key
# And then make sure we've got the whole kaboodle. This will
# create a self-signed CA certificate if we don't already have one,
# and it will just read it in if we do.
generate_certificate unless certificate
end
# Generate a new password for the CA.
def generate_password
pass = ""
20.times { pass += (rand(74) + 48).chr }
begin
Puppet.settings.write(:capass) { |f| f.print pass }
rescue Errno::EACCES => detail
raise Puppet::Error, "Could not write CA password: %s" % detail.to_s
end
@password = pass
return pass
end
# Read the next serial from the serial file, and increment the
# file so this one is considered used.
def next_serial
serial = nil
Puppet.settings.readwritelock(:serial) { |f|
if FileTest.exist?(Puppet[:serial])
serial = File.read(Puppet.settings[:serial]).chomp.hex
else
serial = 0x0
end
# We store the next valid serial, not the one we just used.
f << "%04X" % (serial + 1)
}
return serial
end
# Does the password file exist?
def password?
FileTest.exist? Puppet[:capass]
end
end
|