puppet 2.6.11 → 2.6.12
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +33 -0
- data/conf/redhat/puppet.spec +7 -4
- data/lib/puppet.rb +1 -1
- data/lib/puppet/application/cert.rb +17 -3
- data/lib/puppet/application/kick.rb +0 -2
- data/lib/puppet/defaults.rb +52 -3
- data/lib/puppet/network/handler/ca.rb +16 -106
- data/lib/puppet/network/handler/master.rb +0 -3
- data/lib/puppet/network/handler/runner.rb +1 -0
- data/lib/puppet/ssl/certificate.rb +6 -0
- data/lib/puppet/ssl/certificate_authority.rb +86 -11
- data/lib/puppet/ssl/certificate_authority/interface.rb +64 -19
- data/lib/puppet/ssl/certificate_factory.rb +112 -91
- data/lib/puppet/ssl/certificate_request.rb +88 -1
- data/lib/puppet/ssl/host.rb +16 -3
- data/lib/puppet/type/file.rb +0 -1
- data/lib/puppet/util/command_line/puppetca +23 -2
- data/lib/puppet/util/monkey_patches.rb +69 -0
- data/lib/puppet/util/settings.rb +5 -0
- data/spec/integration/defaults_spec.rb +11 -0
- data/spec/integration/network/handler_spec.rb +1 -1
- data/spec/unit/configurer_spec.rb +2 -2
- data/spec/unit/network/handler/ca_spec.rb +86 -0
- data/spec/unit/ssl/certificate_authority/interface_spec.rb +92 -53
- data/spec/unit/ssl/certificate_authority_spec.rb +133 -23
- data/spec/unit/ssl/certificate_factory_spec.rb +90 -70
- data/spec/unit/ssl/certificate_request_spec.rb +62 -1
- data/spec/unit/ssl/certificate_spec.rb +31 -0
- data/spec/unit/ssl/host_spec.rb +44 -2
- data/spec/unit/util/settings_spec.rb +10 -0
- data/test/language/functions.rb +0 -1
- data/test/language/snippets.rb +0 -9
- data/test/lib/puppettest/exetest.rb +1 -1
- data/test/lib/puppettest/servertest.rb +0 -1
- data/test/rails/rails.rb +0 -1
- data/test/ral/type/filesources.rb +0 -60
- metadata +5 -34
- data/lib/puppet/network/client.rb +0 -179
- data/lib/puppet/network/client/ca.rb +0 -56
- data/lib/puppet/network/client/file.rb +0 -6
- data/lib/puppet/network/client/proxy.rb +0 -27
- data/lib/puppet/network/client/report.rb +0 -26
- data/lib/puppet/network/client/runner.rb +0 -10
- data/lib/puppet/network/client/status.rb +0 -4
- data/lib/puppet/network/http_server.rb +0 -3
- data/lib/puppet/network/http_server/mongrel.rb +0 -150
- data/lib/puppet/network/http_server/webrick.rb +0 -155
- data/lib/puppet/network/xmlrpc/client.rb +0 -211
- data/lib/puppet/sslcertificates.rb +0 -146
- data/lib/puppet/sslcertificates/ca.rb +0 -375
- data/lib/puppet/sslcertificates/certificate.rb +0 -255
- data/lib/puppet/sslcertificates/inventory.rb +0 -38
- data/lib/puppet/sslcertificates/monkey_patch.rb +0 -6
- data/lib/puppet/sslcertificates/support.rb +0 -146
- data/spec/integration/network/client_spec.rb +0 -19
- data/spec/unit/network/client_spec.rb +0 -45
- data/spec/unit/network/xmlrpc/client_spec.rb +0 -172
- data/spec/unit/sslcertificates/ca_spec.rb +0 -110
- data/test/certmgr/certmgr.rb +0 -308
- data/test/certmgr/inventory.rb +0 -69
- data/test/certmgr/support.rb +0 -105
- data/test/network/client/ca.rb +0 -69
- data/test/network/client/dipper.rb +0 -34
- data/test/network/handler/ca.rb +0 -273
- data/test/network/server/mongrel_test.rb +0 -99
- data/test/network/server/webrick.rb +0 -128
- data/test/network/xmlrpc/client.rb +0 -45
data/CHANGELOG
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
2.6.12 (CVE-2011-3872 see http://puppetlabs.com/security/hotfixes/cve-2011-3872/)
|
2
|
+
===
|
3
|
+
3ed6499 Backport Enumerable#count to Rubies < 1.8.7
|
4
|
+
5f44c23 More 1.8.5 compatibility fixes.
|
5
|
+
ef1b960 Better 1.8.5 compatible implementation of `lines`.
|
6
|
+
246e875 (#2848) Config options require '_', not '-'.
|
7
|
+
3bdeb3a Ruby 1.8.5 compatibility changes in tests and code.
|
8
|
+
6866d4b Add `lines` alias for `each_line` in Ruby 1.8.5.
|
9
|
+
2f9ec3c s/not_to/should_not/ for older versions of RSpec 2.
|
10
|
+
56320ea (#2848) Eliminate redundant `master_dns_alt_names`.
|
11
|
+
de19861 (#2848) Remove the legacy SSLCertificates code
|
12
|
+
cf008a6 (#2848) Rework the xmlrpc CA handler to use the modern SSL code
|
13
|
+
32be180 (#2848) Remove unused xmlrpc code
|
14
|
+
5f2a44d (#2848) Consistent return values from `subject_alt_names` accessors.
|
15
|
+
5e507f2 (#2848) Consistently use `subject_alt_names` as accessor name.
|
16
|
+
5ac2417 (#2848) Don't strip the subjectAltName label when listing.
|
17
|
+
44cf3a2 (#2848) Don't enable `emailProtection` for server keys.
|
18
|
+
d66def9 (#2848) Only mark `subjectAltName` critical if `subject` is empty.
|
19
|
+
8174047 (#2848) Migrate `dns-alt-names` back to settings.
|
20
|
+
f18df2b Wire up the `setbycli` slot in Puppet settings.
|
21
|
+
efa61f2 (#2848) rename subject-alt-name option to dns-alt-names
|
22
|
+
f103b20 (#2848) Rename `certdnsnames` to match new behaviour.
|
23
|
+
363b47b (#2848) Use `certdnsnames` when bootstrapping a local master.
|
24
|
+
49334ff (#2848) CSR subjectAltNames handling while signing.
|
25
|
+
5f2af93 (#2848) List subject alt names in output of puppet cert --list
|
26
|
+
bb475ec (#7224) Add a helper to Puppet::SSL::Certificate to retrieve alternate names
|
27
|
+
bab9310 (#2848) Rewrite SSL Certificate Factory, fixing `subjectAltName` leak.
|
28
|
+
fca1ff0 (#2848) Reject unknown (== all) extensions on the CSR.
|
29
|
+
443a756 (#2848) extract the subjectAltName value from the CSR.
|
30
|
+
66101f1 (#2848) Set `certdnsnames` values into the CSR.
|
31
|
+
77b814f (#6928) Don't blow up when the method is undefined...
|
32
|
+
5427f1e (#6928) backport Symbol#to_proc for Ruby < 1.8.7
|
33
|
+
|
1
34
|
2.6.11
|
2
35
|
===
|
3
36
|
e158b26 (#9793) "secure" indirector file backed terminus base class.
|
data/conf/redhat/puppet.spec
CHANGED
@@ -5,13 +5,13 @@
|
|
5
5
|
%global confdir conf/redhat
|
6
6
|
|
7
7
|
Name: puppet
|
8
|
-
Version: 2.6.
|
8
|
+
Version: 2.6.12
|
9
9
|
Release: 1%{?dist}
|
10
10
|
Summary: A network tool for managing many disparate systems
|
11
11
|
License: GPLv2
|
12
12
|
URL: http://puppetlabs.com
|
13
|
-
Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}
|
14
|
-
|
13
|
+
Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz
|
14
|
+
Source1: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz.asc
|
15
15
|
|
16
16
|
Group: System Environment/Base
|
17
17
|
|
@@ -65,7 +65,7 @@ Provides the central puppet server daemon which provides manifests to clients.
|
|
65
65
|
The server can also function as a certificate authority and file server.
|
66
66
|
|
67
67
|
%prep
|
68
|
-
%setup -q -n %{name}-%{version}
|
68
|
+
%setup -q -n %{name}-%{version}
|
69
69
|
patch -s -p1 < conf/redhat/rundir-perms.patch
|
70
70
|
|
71
71
|
|
@@ -253,6 +253,9 @@ fi
|
|
253
253
|
rm -rf %{buildroot}
|
254
254
|
|
255
255
|
%changelog
|
256
|
+
* Fri Oct 21 2011 Michael Stahnke <stahnma@puppetlabs.com> - 2.6.12-1
|
257
|
+
- CVE-2011-3872 fixes
|
258
|
+
|
256
259
|
* Fri Sep 30 2011 Michael Stahnke <stahnma@puppetlabs.com> - 2.6.11-1
|
257
260
|
- CVE-2011-3869, 3870, 3871
|
258
261
|
|
data/lib/puppet.rb
CHANGED
@@ -10,6 +10,7 @@ class Puppet::Application::Cert < Puppet::Application
|
|
10
10
|
def subcommand
|
11
11
|
@subcommand
|
12
12
|
end
|
13
|
+
|
13
14
|
def subcommand=(name)
|
14
15
|
# Handle the nasty, legacy mapping of "clean" to "destroy".
|
15
16
|
sub = name.to_sym
|
@@ -38,11 +39,15 @@ class Puppet::Application::Cert < Puppet::Application
|
|
38
39
|
|
39
40
|
require 'puppet/ssl/certificate_authority/interface'
|
40
41
|
Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject {|m| m == :destroy }.each do |method|
|
41
|
-
option("--#{method}", "-#{method.to_s[0,1]}") do
|
42
|
+
option("--#{method.to_s.gsub('_','-')}", "-#{method.to_s[0,1]}") do
|
42
43
|
self.subcommand = method
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
option("--[no-]allow-dns-alt-names") do |value|
|
48
|
+
options[:allow_dns_alt_names] = value
|
49
|
+
end
|
50
|
+
|
46
51
|
option("--verbose", "-v") do
|
47
52
|
Puppet::Util::Log.level = :info
|
48
53
|
end
|
@@ -56,8 +61,8 @@ class Puppet::Application::Cert < Puppet::Application
|
|
56
61
|
hosts = command_line.args.collect { |h| h.downcase }
|
57
62
|
end
|
58
63
|
begin
|
59
|
-
@ca.apply(:revoke, :to => hosts) if subcommand == :destroy
|
60
|
-
@ca.apply(subcommand, :to => hosts, :digest => @digest)
|
64
|
+
@ca.apply(:revoke, options.merge(:to => hosts)) if subcommand == :destroy
|
65
|
+
@ca.apply(subcommand, options.merge(:to => hosts, :digest => @digest))
|
61
66
|
rescue => detail
|
62
67
|
puts detail.backtrace if Puppet[:trace]
|
63
68
|
puts detail.to_s
|
@@ -77,6 +82,15 @@ class Puppet::Application::Cert < Puppet::Application
|
|
77
82
|
Puppet::SSL::Host.ca_location = :only
|
78
83
|
end
|
79
84
|
|
85
|
+
# If we are generating, and the option came from the CLI, it gets added to
|
86
|
+
# the data. This will do the right thing for non-local certificates, in
|
87
|
+
# that the command line but *NOT* the config file option will apply.
|
88
|
+
if subcommand == :generate
|
89
|
+
if Puppet.settings.setting(:dns_alt_names).setbycli
|
90
|
+
options[:dns_alt_names] = Puppet[:dns_alt_names]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
80
94
|
begin
|
81
95
|
@ca = Puppet::SSL::CertificateAuthority.new
|
82
96
|
rescue => detail
|
@@ -48,8 +48,6 @@ class Puppet::Application::Kick < Puppet::Application
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def main
|
51
|
-
require 'puppet/network/client'
|
52
|
-
|
53
51
|
Puppet.warning "Failed to load ruby LDAP library. LDAP functionality will not be available" unless Puppet.features.ldap?
|
54
52
|
require 'puppet/util/ldap/connection'
|
55
53
|
|
data/lib/puppet/defaults.rb
CHANGED
@@ -205,9 +205,58 @@ module Puppet
|
|
205
205
|
to the fully qualified domain name.",
|
206
206
|
:call_on_define => true, # Call our hook with the default value, so we're always downcased
|
207
207
|
:hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }},
|
208
|
-
:certdnsnames =>
|
209
|
-
|
210
|
-
|
208
|
+
:certdnsnames => {
|
209
|
+
:default => '',
|
210
|
+
:hook => proc do |value|
|
211
|
+
unless value.nil? or value == '' then
|
212
|
+
Puppet.warning <<WARN
|
213
|
+
The `certdnsnames` setting is no longer functional,
|
214
|
+
after CVE-2011-3872. We ignore the value completely.
|
215
|
+
|
216
|
+
For your own certificate request you can set `dns_alt_names` in the
|
217
|
+
configuration and it will apply locally. There is no configuration option to
|
218
|
+
set DNS alt names, or any other `subjectAltName` value, for another nodes
|
219
|
+
certificate.
|
220
|
+
|
221
|
+
Alternately you can use the `--dns_alt_names` command line option to set the
|
222
|
+
labels added while generating your own CSR.
|
223
|
+
WARN
|
224
|
+
end
|
225
|
+
end,
|
226
|
+
:desc => <<EOT
|
227
|
+
The `certdnsnames` setting is no longer functional,
|
228
|
+
after CVE-2011-3872. We ignore the value completely.
|
229
|
+
|
230
|
+
For your own certificate request you can set `dns_alt_names` in the
|
231
|
+
configuration and it will apply locally. There is no configuration option to
|
232
|
+
set DNS alt names, or any other `subjectAltName` value, for another nodes
|
233
|
+
certificate.
|
234
|
+
|
235
|
+
Alternately you can use the `--dns_alt_names` command line option to set the
|
236
|
+
labels added while generating your own CSR.
|
237
|
+
EOT
|
238
|
+
},
|
239
|
+
:dns_alt_names => {
|
240
|
+
:default => '',
|
241
|
+
:desc => <<EOT,
|
242
|
+
The comma-separated list of alternative DNS names to use for the local host.
|
243
|
+
|
244
|
+
When the node generates a CSR for itself, these are added to the request
|
245
|
+
as the desired `subjectAltName` in the certificate: additional DNS labels
|
246
|
+
that the certificate is also valid answering as.
|
247
|
+
|
248
|
+
This is generally required if you use a non-hostname `certname`, or if you
|
249
|
+
want to use `puppet kick` or `puppet resource -H` and the primary certname
|
250
|
+
does not match the DNS name you use to communicate with the host.
|
251
|
+
|
252
|
+
This is unnecessary for agents, unless you intend to use them as a server for
|
253
|
+
`puppet kick` or remote `puppet resource` management.
|
254
|
+
|
255
|
+
It is rarely necessary for servers; it is usually helpful only if you need to
|
256
|
+
have a pool of multiple load balanced masters, or for the same master to
|
257
|
+
respond on two physically separate networks under different names.
|
258
|
+
EOT
|
259
|
+
},
|
211
260
|
:certdir => {
|
212
261
|
:default => "$ssldir/certs",
|
213
262
|
:owner => "service",
|
@@ -1,10 +1,7 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'puppet'
|
3
|
-
require 'puppet/sslcertificates'
|
4
3
|
require 'xmlrpc/server'
|
5
|
-
|
6
|
-
# Much of this was taken from QuickCert:
|
7
|
-
# http://segment7.net/projects/ruby/QuickCert/
|
4
|
+
require 'puppet/network/handler'
|
8
5
|
|
9
6
|
class Puppet::Network::Handler
|
10
7
|
class CA < Handler
|
@@ -18,73 +15,17 @@ class Puppet::Network::Handler
|
|
18
15
|
iface.add_method("array getcert(csr)")
|
19
16
|
}
|
20
17
|
|
21
|
-
def autosign
|
22
|
-
if defined?(@autosign)
|
23
|
-
@autosign
|
24
|
-
else
|
25
|
-
Puppet[:autosign]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# FIXME autosign? should probably accept both hostnames and IP addresses
|
30
|
-
def autosign?(hostname)
|
31
|
-
# simple values are easy
|
32
|
-
if autosign == true or autosign == false
|
33
|
-
return autosign
|
34
|
-
end
|
35
|
-
|
36
|
-
# we only otherwise know how to handle files
|
37
|
-
unless autosign =~ /^\//
|
38
|
-
raise Puppet::Error, "Invalid autosign value #{autosign.inspect}"
|
39
|
-
end
|
40
|
-
|
41
|
-
unless FileTest.exists?(autosign)
|
42
|
-
unless defined?(@@warnedonautosign)
|
43
|
-
@@warnedonautosign = true
|
44
|
-
Puppet.info "Autosign is enabled but #{autosign} is missing"
|
45
|
-
end
|
46
|
-
return false
|
47
|
-
end
|
48
|
-
auth = Puppet::Network::AuthStore.new
|
49
|
-
File.open(autosign) { |f|
|
50
|
-
f.each { |line|
|
51
|
-
next if line =~ /^\s*#/
|
52
|
-
next if line =~ /^\s*$/
|
53
|
-
auth.allow(line.chomp)
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
# for now, just cheat and pass a fake IP address to allowed?
|
58
|
-
auth.allowed?(hostname, "127.1.1.1")
|
59
|
-
end
|
60
|
-
|
61
18
|
def initialize(hash = {})
|
62
19
|
Puppet.settings.use(:main, :ssl, :ca)
|
63
|
-
@autosign = hash[:autosign] if hash.include? :autosign
|
64
20
|
|
65
|
-
@ca = Puppet::
|
21
|
+
@ca = Puppet::SSL::CertificateAuthority.instance
|
66
22
|
end
|
67
23
|
|
68
24
|
# our client sends us a csr, and we either store it for later signing,
|
69
25
|
# or we sign it right away
|
70
26
|
def getcert(csrtext, client = nil, clientip = nil)
|
71
|
-
csr =
|
72
|
-
|
73
|
-
# Use the hostname from the CSR, not from the network.
|
74
|
-
subject = csr.subject
|
75
|
-
|
76
|
-
nameary = subject.to_a.find { |ary|
|
77
|
-
ary[0] == "CN"
|
78
|
-
}
|
79
|
-
|
80
|
-
if nameary.nil?
|
81
|
-
Puppet.err(
|
82
|
-
"Invalid certificate request: could not retrieve server name"
|
83
|
-
)
|
84
|
-
return "invalid"
|
85
|
-
end
|
86
|
-
|
87
|
-
hostname = nameary[1]
|
27
|
+
csr = Puppet::SSL::CertificateRequest.from_s(csrtext)
|
28
|
+
hostname = csr.name
|
88
29
|
|
89
30
|
unless @ca
|
90
31
|
Puppet.notice "Host #{hostname} asked for signing from non-CA master"
|
@@ -93,57 +34,26 @@ class Puppet::Network::Handler
|
|
93
34
|
|
94
35
|
# We used to save the public key, but it's basically unnecessary
|
95
36
|
# and it mucks with the permissions requirements.
|
96
|
-
# save_pk(hostname, csr.public_key)
|
97
|
-
|
98
|
-
certfile = File.join(Puppet[:certdir], [hostname, "pem"].join("."))
|
99
37
|
|
100
38
|
# first check to see if we already have a signed cert for the host
|
101
|
-
cert
|
102
|
-
|
39
|
+
cert = Puppet::SSL::Certificate.find(hostname)
|
40
|
+
cacert = Puppet::SSL::Certificate.find(@ca.host.name)
|
41
|
+
|
42
|
+
if cert
|
103
43
|
Puppet.info "Retrieving existing certificate for #{hostname}"
|
104
|
-
unless csr.public_key.to_s == cert.public_key.to_s
|
44
|
+
unless csr.content.public_key.to_s == cert.content.public_key.to_s
|
105
45
|
raise Puppet::Error, "Certificate request does not match existing certificate; run 'puppetca --clean #{hostname}'."
|
106
46
|
end
|
107
|
-
|
108
|
-
elsif @ca
|
109
|
-
if self.autosign?(hostname) or client.nil?
|
110
|
-
Puppet.info "Signing certificate for CA server" if client.nil?
|
111
|
-
# okay, we don't have a signed cert
|
112
|
-
# if we're a CA and autosign is turned on, then go ahead and sign
|
113
|
-
# the csr and return the results
|
114
|
-
Puppet.info "Signing certificate for #{hostname}"
|
115
|
-
cert, cacert = @ca.sign(csr)
|
116
|
-
#Puppet.info "Cert: #{cert.class}; Cacert: #{cacert.class}"
|
117
|
-
return [cert.to_pem, cacert.to_pem]
|
118
|
-
else # just write out the csr for later signing
|
119
|
-
if @ca.getclientcsr(hostname)
|
120
|
-
Puppet.info "Not replacing existing request from #{hostname}"
|
121
|
-
else
|
122
|
-
Puppet.notice "Host #{hostname} has a waiting certificate request"
|
123
|
-
@ca.storeclientcsr(csr)
|
124
|
-
end
|
125
|
-
return ["", ""]
|
126
|
-
end
|
47
|
+
[cert.to_s, cacert.to_s]
|
127
48
|
else
|
128
|
-
|
129
|
-
end
|
130
|
-
end
|
49
|
+
csr.save
|
131
50
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
if FileTest.exists?(pkeyfile)
|
139
|
-
currentkey = File.open(pkeyfile) { |k| k.read }
|
140
|
-
unless currentkey == public_key.to_s
|
141
|
-
raise Puppet::Error, "public keys for #{hostname} differ"
|
51
|
+
# We determine whether we signed the csr by checking if there's a certificate for it
|
52
|
+
if cert = Puppet::SSL::Certificate.find(hostname)
|
53
|
+
[cert.to_s, cacert.to_s]
|
54
|
+
else
|
55
|
+
nil
|
142
56
|
end
|
143
|
-
else
|
144
|
-
File.open(pkeyfile, "w", 0644) { |f|
|
145
|
-
f.print public_key.to_s
|
146
|
-
}
|
147
57
|
end
|
148
58
|
end
|
149
59
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'puppet'
|
3
|
-
require 'puppet/sslcertificates'
|
4
3
|
require 'xmlrpc/server'
|
5
4
|
require 'yaml'
|
6
5
|
|
@@ -33,8 +32,6 @@ class Puppet::Network::Handler
|
|
33
32
|
|
34
33
|
args[:Local] = true
|
35
34
|
|
36
|
-
@ca = (hash.include?(:CA) and hash[:CA]) ? Puppet::SSLCertificates::CA.new : nil
|
37
|
-
|
38
35
|
# This is only used by the cfengine module, or if --loadclasses was
|
39
36
|
# specified in +puppet+.
|
40
37
|
args[:Classes] = hash[:Classes] if hash.include?(:Classes)
|
@@ -27,6 +27,12 @@ class Puppet::SSL::Certificate < Puppet::SSL::Base
|
|
27
27
|
[:s]
|
28
28
|
end
|
29
29
|
|
30
|
+
def subject_alt_names
|
31
|
+
alts = content.extensions.find{|ext| ext.oid == "subjectAltName"}
|
32
|
+
return [] unless alts
|
33
|
+
alts.value.split(/\s*,\s*/)
|
34
|
+
end
|
35
|
+
|
30
36
|
def expiration
|
31
37
|
return nil unless content
|
32
38
|
content.not_after
|
@@ -11,6 +11,15 @@ require 'puppet/util/cacher'
|
|
11
11
|
# it can also be seen as a general interface into all of the
|
12
12
|
# SSL stuff.
|
13
13
|
class Puppet::SSL::CertificateAuthority
|
14
|
+
# We will only sign extensions on this whitelist, ever. Any CSR with a
|
15
|
+
# requested extension that we don't recognize is rejected, against the risk
|
16
|
+
# that it will introduce some security issue through our ignorance of it.
|
17
|
+
#
|
18
|
+
# Adding an extension to this whitelist simply means we will consider it
|
19
|
+
# further, not that we will always accept a certificate with an extension
|
20
|
+
# requested on this list.
|
21
|
+
RequestExtensionWhitelist = %w{subjectAltName}
|
22
|
+
|
14
23
|
require 'puppet/ssl/certificate_factory'
|
15
24
|
require 'puppet/ssl/inventory'
|
16
25
|
require 'puppet/ssl/certificate_revocation_list'
|
@@ -25,6 +34,14 @@ class Puppet::SSL::CertificateAuthority
|
|
25
34
|
end
|
26
35
|
end
|
27
36
|
|
37
|
+
class CertificateSigningError < RuntimeError
|
38
|
+
attr_accessor :host
|
39
|
+
|
40
|
+
def initialize(host)
|
41
|
+
@host = host
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
28
45
|
class << self
|
29
46
|
include Puppet::Util::Cacher
|
30
47
|
|
@@ -52,7 +69,6 @@ class Puppet::SSL::CertificateAuthority
|
|
52
69
|
def apply(method, options)
|
53
70
|
raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
|
54
71
|
applier = Interface.new(method, options)
|
55
|
-
|
56
72
|
applier.apply(self)
|
57
73
|
end
|
58
74
|
|
@@ -108,13 +124,15 @@ class Puppet::SSL::CertificateAuthority
|
|
108
124
|
end
|
109
125
|
|
110
126
|
# Generate a new certificate.
|
111
|
-
def generate(name)
|
127
|
+
def generate(name, options = {})
|
112
128
|
raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.find(name)
|
113
|
-
host = Puppet::SSL::Host.new(name)
|
114
129
|
|
115
|
-
|
130
|
+
# Pass on any requested subjectAltName field.
|
131
|
+
san = options[:dns_alt_names]
|
116
132
|
|
117
|
-
|
133
|
+
host = Puppet::SSL::Host.new(name)
|
134
|
+
host.generate_certificate_request(:dns_alt_names => san)
|
135
|
+
sign(name, !!san)
|
118
136
|
end
|
119
137
|
|
120
138
|
# Generate our CA certificate.
|
@@ -123,14 +141,16 @@ class Puppet::SSL::CertificateAuthority
|
|
123
141
|
|
124
142
|
host.generate_key unless host.key
|
125
143
|
|
126
|
-
# Create a new cert request. We do this
|
127
|
-
#
|
128
|
-
# save the request anywhere.
|
144
|
+
# Create a new cert request. We do this specially, because we don't want
|
145
|
+
# to actually save the request anywhere.
|
129
146
|
request = Puppet::SSL::CertificateRequest.new(host.name)
|
147
|
+
|
148
|
+
# We deliberately do not put any subjectAltName in here: the CA
|
149
|
+
# certificate absolutely does not need them. --daniel 2011-10-13
|
130
150
|
request.generate(host.key)
|
131
151
|
|
132
152
|
# Create a self-signed certificate.
|
133
|
-
@certificate = sign(host.name,
|
153
|
+
@certificate = sign(host.name, false, request)
|
134
154
|
|
135
155
|
# And make sure we initialize our CRL.
|
136
156
|
crl
|
@@ -223,20 +243,34 @@ class Puppet::SSL::CertificateAuthority
|
|
223
243
|
end
|
224
244
|
|
225
245
|
# Sign a given certificate request.
|
226
|
-
def sign(hostname,
|
246
|
+
def sign(hostname, allow_dns_alt_names = false, self_signing_csr = nil)
|
227
247
|
# This is a self-signed certificate
|
228
248
|
if self_signing_csr
|
249
|
+
# # This is a self-signed certificate, which is for the CA. Since this
|
250
|
+
# # forces the certificate to be self-signed, anyone who manages to trick
|
251
|
+
# # the system into going through this path gets a certificate they could
|
252
|
+
# # generate anyway. There should be no security risk from that.
|
229
253
|
csr = self_signing_csr
|
254
|
+
cert_type = :ca
|
230
255
|
issuer = csr.content
|
231
256
|
else
|
257
|
+
allow_dns_alt_names = true if hostname == Puppet[:certname].downcase
|
232
258
|
unless csr = Puppet::SSL::CertificateRequest.find(hostname)
|
233
259
|
raise ArgumentError, "Could not find certificate request for #{hostname}"
|
234
260
|
end
|
261
|
+
|
262
|
+
cert_type = :server
|
235
263
|
issuer = host.certificate.content
|
264
|
+
|
265
|
+
# Make sure that the CSR conforms to our internal signing policies.
|
266
|
+
# This will raise if the CSR doesn't conform, but just in case...
|
267
|
+
check_internal_signing_policies(hostname, csr, allow_dns_alt_names) or
|
268
|
+
raise CertificateSigningError.new(hostname), "CSR had an unknown failure checking internal signing policies, will not sign!"
|
236
269
|
end
|
237
270
|
|
238
271
|
cert = Puppet::SSL::Certificate.new(hostname)
|
239
|
-
cert.content = Puppet::SSL::CertificateFactory.
|
272
|
+
cert.content = Puppet::SSL::CertificateFactory.
|
273
|
+
build(cert_type, csr, issuer, next_serial)
|
240
274
|
cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new)
|
241
275
|
|
242
276
|
Puppet.notice "Signed certificate request for #{hostname}"
|
@@ -256,6 +290,47 @@ class Puppet::SSL::CertificateAuthority
|
|
256
290
|
cert
|
257
291
|
end
|
258
292
|
|
293
|
+
def check_internal_signing_policies(hostname, csr, allow_dns_alt_names)
|
294
|
+
# Reject unknown request extensions.
|
295
|
+
unknown_req = csr.request_extensions.
|
296
|
+
reject {|x| RequestExtensionWhitelist.include? x["oid"] }
|
297
|
+
|
298
|
+
if unknown_req and not unknown_req.empty?
|
299
|
+
names = unknown_req.map {|x| x["oid"] }.sort.uniq.join(", ")
|
300
|
+
raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
|
301
|
+
end
|
302
|
+
|
303
|
+
# Wildcards: we don't allow 'em at any point.
|
304
|
+
#
|
305
|
+
# The stringification here makes the content visible, and saves us having
|
306
|
+
# to scrobble through the content of the CSR subject field to make sure it
|
307
|
+
# is what we expect where we expect it.
|
308
|
+
if csr.content.subject.to_s.include? '*'
|
309
|
+
raise CertificateSigningError.new(hostname), "CSR subject contains a wildcard, which is not allowed: #{csr.content.subject.to_s}"
|
310
|
+
end
|
311
|
+
|
312
|
+
unless csr.subject_alt_names.empty?
|
313
|
+
# If you alt names are allowed, they are required. Otherwise they are
|
314
|
+
# disallowed. Self-signed certs are implicitly trusted, however.
|
315
|
+
unless allow_dns_alt_names
|
316
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains subject alternative names (#{csr.subject_alt_names.join(', ')}), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{csr.name}` to sign this request."
|
317
|
+
end
|
318
|
+
|
319
|
+
# If subjectAltNames are present, validate that they are only for DNS
|
320
|
+
# labels, not any other kind.
|
321
|
+
unless csr.subject_alt_names.all? {|x| x =~ /^DNS:/ }
|
322
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains a subjectAltName outside the DNS label space: #{csr.subject_alt_names.join(', ')}. To continue, this CSR needs to be cleaned."
|
323
|
+
end
|
324
|
+
|
325
|
+
# Check for wildcards in the subjectAltName fields too.
|
326
|
+
if csr.subject_alt_names.any? {|x| x.include? '*' }
|
327
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' subjectAltName contains a wildcard, which is not allowed: #{csr.subject_alt_names.join(', ')} To continue, this CSR needs to be cleaned."
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
return true # good enough for us!
|
332
|
+
end
|
333
|
+
|
259
334
|
# Verify a given host's certificate.
|
260
335
|
def verify(name)
|
261
336
|
unless cert = Puppet::SSL::Certificate.find(name)
|