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
146
147
148
149
150
151
152
153
154
155
|
require 'openssl'
require 'puppet'
require 'puppet/sslcertificates'
require 'xmlrpc/server'
# Much of this was taken from QuickCert:
# http://segment7.net/projects/ruby/QuickCert/
module Puppet
class Server
class CAError < Puppet::Error; end
class CA < Handler
attr_reader :ca
@interface = XMLRPC::Service::Interface.new("puppetca") { |iface|
iface.add_method("array getcert(csr)")
}
def autosign
if defined? @autosign
@autosign
else
Puppet[:autosign]
end
end
# FIXME autosign? should probably accept both hostnames and IP addresses
def autosign?(hostname)
# simple values are easy
if autosign == true or autosign == false
return autosign
end
# we only otherwise know how to handle files
unless autosign =~ /^\//
raise Puppet::Error, "Invalid autosign value %s" %
autosign.inspect
end
unless FileTest.exists?(autosign)
unless defined? @@warnedonautosign
@@warnedonautosign = true
Puppet.info "Autosign is enabled but %s is missing" % autosign
end
return false
end
auth = Puppet::Server::AuthStore.new
File.open(autosign) { |f|
f.each { |line|
next if line =~ /^\s*#/
next if line =~ /^\s*$/
auth.allow(line.chomp)
}
}
# for now, just cheat and pass a fake IP address to allowed?
return auth.allowed?(hostname, "127.1.1.1")
end
def initialize(hash = {})
Puppet.config.use(:puppet, :certificates, :ca)
if hash.include? :autosign
@autosign = hash[:autosign]
end
@ca = Puppet::SSLCertificates::CA.new(hash)
end
# our client sends us a csr, and we either store it for later signing,
# or we sign it right away
def getcert(csrtext, client = nil, clientip = nil)
csr = OpenSSL::X509::Request.new(csrtext)
# Use the hostname from the CSR, not from the network.
subject = csr.subject
nameary = subject.to_a.find { |ary|
ary[0] == "CN"
}
if nameary.nil?
Puppet.err(
"Invalid certificate request: could not retrieve server name"
)
return "invalid"
end
hostname = nameary[1]
unless @ca
Puppet.notice "Host %s asked for signing from non-CA master" % hostname
return ""
end
# We used to save the public key, but it's basically unnecessary
# and it mucks with the permissions requirements.
# save_pk(hostname, csr.public_key)
certfile = File.join(Puppet[:certdir], [hostname, "pem"].join("."))
# first check to see if we already have a signed cert for the host
cert, cacert = ca.getclientcert(hostname)
if cert and cacert
Puppet.info "Retrieving existing certificate for %s" % hostname
#Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class]
return [cert.to_pem, cacert.to_pem]
elsif @ca
if self.autosign?(hostname) or client.nil?
if client.nil?
Puppet.info "Signing certificate for CA server"
end
# okay, we don't have a signed cert
# if we're a CA and autosign is turned on, then go ahead and sign
# the csr and return the results
Puppet.info "Signing certificate for %s" % hostname
cert, cacert = @ca.sign(csr)
#Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class]
return [cert.to_pem, cacert.to_pem]
else # just write out the csr for later signing
if @ca.getclientcsr(hostname)
Puppet.info "Not replacing existing request from %s" % hostname
else
Puppet.notice "Host %s has a waiting certificate request" %
hostname
@ca.storeclientcsr(csr)
end
return ["", ""]
end
else
raise "huh?"
end
end
private
# Save the public key.
def save_pk(hostname, public_key)
pkeyfile = File.join(Puppet[:publickeydir], [hostname, "pem"].join('.'))
if FileTest.exists?(pkeyfile)
currentkey = File.open(pkeyfile) { |k| k.read }
unless currentkey == public_key.to_s
raise Puppet::Error, "public keys for %s differ" % hostname
end
else
File.open(pkeyfile, "w", 0644) { |f|
f.print public_key.to_s
}
end
end
end
end
end
# $Id$
|