Skip to content

Commit 566bf78

Browse files
committed
Fixing #1729 - puppetmasterd can now read certs at startup
The main aspect of this solution is to create a site-wide Puppet::SSL::Host instance to cache ssl key and certificate, so that by the time we've switched UIDs, we've got the key and cert in memory. Then webrick just uses that, rather than creating a new Host instance. Signed-off-by: Luke Kanies <[email protected]>
1 parent 0cf9dec commit 566bf78

File tree

8 files changed

+165
-126
lines changed

8 files changed

+165
-126
lines changed

bin/puppetmasterd

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ if Puppet.settings.print_configs?
155155
exit(Puppet.settings.print_configs ? 0 : 1)
156156
end
157157

158+
Puppet.settings.use :main, :puppetmasterd, :ssl
159+
158160
# A temporary solution, to at least make the master work for now.
159161
Puppet::Node::Facts.terminus_class = :yaml
160162

@@ -164,7 +166,7 @@ Puppet::Node.cache_class = :yaml
164166
# Configure all of the SSL stuff.
165167
if Puppet::SSL::CertificateAuthority.ca?
166168
Puppet::SSL::Host.ca_location = :local
167-
Puppet.settings.use :main, :ssl, :ca
169+
Puppet.settings.use :ca
168170
Puppet::SSL::CertificateAuthority.instance
169171
else
170172
Puppet::SSL::Host.ca_location = :none
@@ -197,6 +199,16 @@ end
197199

198200
server = Puppet::Network::Server.new(:handlers => rest_handlers, :xmlrpc_handlers => xmlrpc_handlers)
199201

202+
# Make sure we've got a localhost ssl cert
203+
Puppet::SSL::Host.localhost
204+
205+
# And now configure our server to *only* hit the CA for data, because that's
206+
# all it will have write access to.
207+
if Puppet::SSL::CertificateAuthority.ca?
208+
Puppet::SSL::Host.ca_location = :only
209+
Puppet::SSL::Host.ca_location = :none
210+
end
211+
200212
if Process.uid == 0
201213
begin
202214
Puppet::Util.chuser

lib/puppet/defaults.rb

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,12 @@ module Puppet
148148
"The HTTP proxy port to use for outgoing connections"],
149149
:http_enable_post_connection_check => [true,
150150
"Boolean; wheter or not puppetd should validate the server
151-
SSL certificate against the request hostname."]
151+
SSL certificate against the request hostname."],
152+
:filetimeout => [ 15,
153+
"The minimum time to wait (in seconds) between checking for updates in
154+
configuration files. This timeout determines how quickly Puppet checks whether
155+
a file (such as manifests or templates) has changed on disk."
156+
]
152157
)
153158

154159
hostname = Facter["hostname"].value
@@ -380,7 +385,31 @@ module Puppet
380385
:yamldir => {:default => "$vardir/yaml", :owner => "$user", :group => "$user", :mode => "750",
381386
:desc => "The directory in which YAML data is stored, usually in a subdirectory."},
382387
:clientyamldir => {:default => "$vardir/client_yaml", :mode => "750",
383-
:desc => "The directory in which client-side YAML data is stored."}
388+
:desc => "The directory in which client-side YAML data is stored."},
389+
:reports => ["store",
390+
"The list of reports to generate. All reports are looked for
391+
in puppet/reports/<name>.rb, and multiple report names should be
392+
comma-separated (whitespace is okay)."
393+
],
394+
:reportdir => {:default => "$vardir/reports",
395+
:mode => 0750,
396+
:owner => "$user",
397+
:group => "$group",
398+
:desc => "The directory in which to store reports
399+
received from the client. Each client gets a separate
400+
subdirectory."},
401+
:fileserverconfig => ["$confdir/fileserver.conf",
402+
"Where the fileserver configuration is stored."],
403+
:rrddir => {:default => "$vardir/rrd",
404+
:owner => "$user",
405+
:group => "$group",
406+
:desc => "The directory where RRD database files are stored.
407+
Directories for each reporting host will be created under
408+
this directory."
409+
},
410+
:rrdgraph => [false, "Whether RRD information should be graphed."],
411+
:rrdinterval => ["$runinterval", "How often RRD should expect data.
412+
This should match how often the hosts report back to the server."]
384413
)
385414

386415
self.setdefaults(:puppetd,
@@ -428,35 +457,7 @@ module Puppet
428457
:ca_port => ["$masterport", "The port to use for the certificate authority."],
429458
:catalog_format => ["yaml", "What format to use to dump the catalog. Only supports
430459
'marshal' and 'yaml'. Only matters on the client, since it asks the server
431-
for a specific format."]
432-
)
433-
434-
self.setdefaults(:filebucket,
435-
:clientbucketdir => {
436-
:default => "$vardir/clientbucket",
437-
:mode => 0750,
438-
:desc => "Where FileBucket files are stored locally."
439-
}
440-
)
441-
self.setdefaults(:fileserver,
442-
:fileserverconfig => ["$confdir/fileserver.conf",
443-
"Where the fileserver configuration is stored."]
444-
)
445-
self.setdefaults(:reporting,
446-
:reports => ["store",
447-
"The list of reports to generate. All reports are looked for
448-
in puppet/reports/<name>.rb, and multiple report names should be
449-
comma-separated (whitespace is okay)."
450-
],
451-
:reportdir => {:default => "$vardir/reports",
452-
:mode => 0750,
453-
:owner => "$user",
454-
:group => "$group",
455-
:desc => "The directory in which to store reports
456-
received from the client. Each client gets a separate
457-
subdirectory."}
458-
)
459-
self.setdefaults(:puppetd,
460+
for a specific format."],
460461
:puppetdlockfile => [ "$statedir/puppetdlock",
461462
"A lock file to temporarily stop puppetd from doing anything."],
462463
:usecacheonfailure => [true,
@@ -482,10 +483,12 @@ module Puppet
482483
run interval."],
483484
:splay => [false,
484485
"Whether to sleep for a pseudo-random (but consistent) amount of time before
485-
a run."]
486-
)
487-
488-
self.setdefaults(:puppetd,
486+
a run."],
487+
:clientbucketdir => {
488+
:default => "$vardir/clientbucket",
489+
:mode => 0750,
490+
:desc => "Where FileBucket files are stored locally."
491+
},
489492
:configtimeout => [120,
490493
"How long the client should wait for the configuration to be retrieved
491494
before considering it a failure. This can help reduce flapping if too
@@ -496,7 +499,14 @@ module Puppet
496499
],
497500
:report => [false,
498501
"Whether to send reports after every transaction."
499-
]
502+
],
503+
:graph => [false, "Whether to create dot graph files for the different
504+
configuration graphs. These dot files can be interpreted by tools
505+
like OmniGraffle or dot (which is part of ImageMagick)."],
506+
:graphdir => ["$statedir/graphs", "Where to store dot-outputted graphs."],
507+
:storeconfigs => [false,
508+
"Whether to store each client's configuration. This
509+
requires ActiveRecord from Ruby on Rails."]
500510
)
501511

502512
# Plugin information.
@@ -582,13 +592,6 @@ module Puppet
582592
and other environments normally use ``debug``."]
583593
)
584594

585-
setdefaults(:graphing,
586-
:graph => [false, "Whether to create dot graph files for the different
587-
configuration graphs. These dot files can be interpreted by tools
588-
like OmniGraffle or dot (which is part of ImageMagick)."],
589-
:graphdir => ["$statedir/graphs", "Where to store dot-outputted graphs."]
590-
)
591-
592595
setdefaults(:transaction,
593596
:tags => ["", "Tags to use to find resources. If this is set, then
594597
only resources tagged with the specified tags will be applied.
@@ -665,12 +668,6 @@ module Puppet
665668
branch under your main directory."]
666669
)
667670

668-
setdefaults(:puppetmasterd,
669-
:storeconfigs => [false,
670-
"Whether to store each client's configuration. This
671-
requires ActiveRecord from Ruby on Rails."]
672-
)
673-
674671
# This doesn't actually work right now.
675672
setdefaults(:parser,
676673
:lexical => [false, "Whether to use lexical scoping (vs. dynamic)."],
@@ -679,26 +676,4 @@ module Puppet
679676
directories."
680677
]
681678
)
682-
683-
setdefaults(:main,
684-
:filetimeout => [ 15,
685-
"The minimum time to wait (in seconds) between checking for updates in
686-
configuration files. This timeout determines how quickly Puppet checks whether
687-
a file (such as manifests or templates) has changed on disk."
688-
]
689-
)
690-
691-
setdefaults(:metrics,
692-
:rrddir => {:default => "$vardir/rrd",
693-
:owner => "$user",
694-
:group => "$group",
695-
:desc => "The directory where RRD database files are stored.
696-
Directories for each reporting host will be created under
697-
this directory."
698-
},
699-
:rrdgraph => [false, "Whether RRD information should be graphed."],
700-
:rrdinterval => ["$runinterval", "How often RRD should expect data.
701-
This should match how often the hosts report back to the server."]
702-
)
703679
end
704-

lib/puppet/network/http/webrick.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,8 @@ def setup_logger
8989
def setup_ssl
9090
results = {}
9191

92-
host = Puppet::SSL::Host.new
93-
94-
host.generate unless host.certificate
92+
# Get the cached copy. We know it's been generated, too.
93+
host = Puppet::SSL::Host.localhost
9594

9695
raise Puppet::Error, "Could not retrieve certificate for %s and not running on a valid certificate authority" % host.name unless host.certificate
9796

lib/puppet/network/http_pool.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ module Puppet::Network; end
88
module Puppet::Network::HttpPool
99
class << self
1010
include Puppet::Util::Cacher
11-
cached_attr(:ssl_host) { Puppet::SSL::Host.new }
1211

1312
private
1413

1514
cached_attr(:http_cache) { Hash.new }
1615
end
1716

17+
# Use the global localhost instance.
18+
def self.ssl_host
19+
Puppet::SSL::Host.localhost
20+
end
21+
1822
# 2008/03/23
1923
# LAK:WARNING: Enabling this has a high propability of
2024
# causing corrupt files and who knows what else. See #1010.

lib/puppet/ssl/host.rb

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require 'puppet/ssl/certificate'
44
require 'puppet/ssl/certificate_request'
55
require 'puppet/ssl/certificate_revocation_list'
6-
require 'puppet/util/constant_inflector'
6+
require 'puppet/util/cacher'
77

88
# The class that manages all aspects of our SSL certificates --
99
# private keys, public keys, requests, etc.
@@ -14,15 +14,23 @@ class Puppet::SSL::Host
1414
CertificateRequest = Puppet::SSL::CertificateRequest
1515
CertificateRevocationList = Puppet::SSL::CertificateRevocationList
1616

17-
extend Puppet::Util::ConstantInflector
18-
1917
attr_reader :name
2018
attr_accessor :ca
2119

2220
attr_writer :key, :certificate, :certificate_request
2321

24-
CA_NAME = "ca"
22+
class << self
23+
include Puppet::Util::Cacher
24+
25+
cached_attr(:localhost) do
26+
result = new()
27+
result.generate unless result.certificate
28+
result.key # Make sure it's read in
29+
result
30+
end
31+
end
2532

33+
CA_NAME = "ca"
2634
# This is the constant that people will use to mark that a given host is
2735
# a certificate authority.
2836
def self.ca_name
@@ -40,7 +48,7 @@ def self.configure_indirection(terminus, cache = nil)
4048
CertificateRevocationList.terminus_class = terminus
4149

4250
if cache
43-
# This is weird; we don't actually cache our keys or CRL, we
51+
# This is weird; we don't actually cache our keys, we
4452
# use what would otherwise be the cache as our normal
4553
# terminus.
4654
Key.terminus_class = cache
@@ -55,23 +63,25 @@ def self.configure_indirection(terminus, cache = nil)
5563
end
5664
end
5765

66+
CA_MODES = {
67+
# Our ca is local, so we use it as the ultimate source of information
68+
# And we cache files locally.
69+
:local => [:ca, :file],
70+
# We're a remote CA client.
71+
:remote => [:rest, :file],
72+
# We are the CA, so we don't have read/write access to the normal certificates.
73+
:only => [:ca],
74+
# We have no CA, so we just look in the local file store.
75+
:none => [:file]
76+
}
77+
5878
# Specify how we expect to interact with our certificate authority.
5979
def self.ca_location=(mode)
60-
raise ArgumentError, "CA Mode can only be :local, :remote, or :none" unless [:local, :remote, :none].include?(mode)
61-
62-
@ca_mode = mode
63-
64-
case @ca_mode
65-
when :local:
66-
# Our ca is local, so we use it as the ultimate source of information
67-
# And we cache files locally.
68-
configure_indirection :ca, :file
69-
when :remote:
70-
configure_indirection :rest, :file
71-
when :none:
72-
# We have no CA, so we just look in the local file store.
73-
configure_indirection :file
74-
end
80+
raise ArgumentError, "CA Mode can only be %s" % CA_MODES.collect { |m| m.to_s }.join(", ") unless CA_MODES.include?(mode)
81+
82+
@ca_location = mode
83+
84+
configure_indirection(*CA_MODES[@ca_location])
7585
end
7686

7787
# Remove all traces of a given host

spec/unit/network/http/webrick.rb

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -298,24 +298,16 @@
298298

299299
Puppet::SSL::Certificate.stubs(:find).with('ca').returns @cert
300300

301-
Puppet::SSL::Host.stubs(:new).returns @host
301+
Puppet::SSL::Host.stubs(:localhost).returns @host
302302
end
303303

304-
it "should use the key from an SSL::Host instance created with the default name" do
305-
Puppet::SSL::Host.expects(:new).returns @host
304+
it "should use the key from the localhost SSL::Host instance" do
305+
Puppet::SSL::Host.expects(:localhost).returns @host
306306
@host.expects(:key).returns @key
307307

308308
@server.setup_ssl[:SSLPrivateKey].should == "mykey"
309309
end
310310

311-
it "should generate its files if no certificate can be found" do
312-
@host.expects(:certificate).times(2).returns(nil).then.returns(@cert)
313-
314-
@host.expects(:generate)
315-
316-
@server.setup_ssl
317-
end
318-
319311
it "should configure the certificate" do
320312
@server.setup_ssl[:SSLCertificate].should == "mycert"
321313
end

spec/unit/network/http_pool.rb

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,9 @@
1717
Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false
1818
end
1919

20-
it "should use an SSL::Host instance to get its certificate information" do
20+
it "should use the global SSL::Host instance to get its certificate information" do
2121
host = mock 'host'
22-
Puppet::SSL::Host.expects(:new).with().returns host
23-
Puppet::Network::HttpPool.ssl_host.should equal(host)
24-
end
25-
26-
it "should reuse the same host instance" do
27-
host = mock 'host'
28-
Puppet::SSL::Host.expects(:new).with().once.returns host
29-
Puppet::Network::HttpPool.ssl_host.should equal(host)
22+
Puppet::SSL::Host.expects(:localhost).with().returns host
3023
Puppet::Network::HttpPool.ssl_host.should equal(host)
3124
end
3225

@@ -122,16 +115,6 @@ def stub_settings(settings)
122115
one.expects(:finish).never
123116
Puppet::Network::HttpPool.clear_http_instances
124117
end
125-
126-
it "should reset its ssl host when clearing the cache" do
127-
stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120, :http_enable_post_connection_check => true, :certname => "a"
128-
one = Puppet::Network::HttpPool.http_instance("me", 54321)
129-
one.expects(:started?).returns(false)
130-
one.expects(:finish).never
131-
id = Puppet::Network::HttpPool.ssl_host.object_id
132-
Puppet::Network::HttpPool.clear_http_instances
133-
Puppet::Network::HttpPool.ssl_host.object_id.should_not == id
134-
end
135118
end
136119

137120
describe "and http keep-alive is disabled" do

0 commit comments

Comments
 (0)