diff --git a/bin/puppet b/bin/puppet
index c03070291..c80174f60 100755
--- a/bin/puppet
+++ b/bin/puppet
@@ -1,4 +1,8 @@
#!/usr/bin/env ruby
+# For security reasons, ensure that '.' is not on the load path
+# This is primarily for 1.8.7 since 1.9.2+ doesn't put '.' on the load path
+$LOAD_PATH.delete '.'
+
require 'puppet/util/command_line'
Puppet::Util::CommandLine.new.execute
diff --git a/ext/debian/puppetmaster-passenger.postinst b/ext/debian/puppetmaster-passenger.postinst
index 2c9f20c3f..02c71c349 100644
--- a/ext/debian/puppetmaster-passenger.postinst
+++ b/ext/debian/puppetmaster-passenger.postinst
@@ -1,112 +1,162 @@
#!/bin/sh
set -e
sitename="puppetmaster"
+apache2_version="$(dpkg-query --showformat='${Version}\n' --show apache2)"
# The debian provided a2* utils in Apache 2.4 uses "site name" as
# argument, while the version in Apache 2.2 uses "file name".
#
# For added fun, the Apache 2.4 version requires files to have a
# ".conf" suffix, but this must be stripped when using it as argument
# for the a2* utilities.
#
# This will end in tears…
# Can be removed when we only support apache >= 2.4
apache2_puppetmaster_sitename() {
- apache2_version="$(dpkg-query --showformat='${Version}\n' --show apache2)"
if dpkg --compare-versions "$apache2_version" gt "2.4~"; then
echo "${sitename}.conf"
else
echo "${sitename}"
fi
}
# Can be removed when we only support apache >= 2.4
restart_apache2() {
if [ -x "/etc/init.d/apache2" ]; then
# Seems that a restart is needed. reload breaks ssl apparently.
if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
invoke-rc.d apache2 restart || exit $?
else
/etc/init.d/apache2 restart || exit $?
fi
fi
}
# We may need to update the passenger directives in the apache vhost because
# RailsAutoDetect and RackAutoDetect were removed in passenger 4.0.0
# see http://www.modrails.com/documentation/Users%20guide%20Apache.html#_railsautodetect_rackautodetect_and_wsgiautodetect
update_vhost_for_passenger4() {
# Get passenger version from dpkg.
# This will end in tears…
passenger_version="$(dpkg-query --showformat='${Version}\n' --show libapache2-mod-passenger)"
if dpkg --compare-versions "$passenger_version" gt "4.0~"; then
sed -r -i \
-e "/RailsAutoDetect/d" \
-e "/RackAutoDetect/d" \
$tempfile
fi
}
+# In Apache 2.2, if either the SSLCARevocationFile or SSLCARevocationPath
+# directives were specified then the specified file(s) would be checked when
+# establishing an SSL connection. Apache 2.4+ the SSLCARevocationCheck directive
+# was added to control how CRLs were checked when verifying a connection and had
+# a default value of none. This means that Apache defaults to ignoring CRLs even
+# if paths are specified to CRL files.
+#
+# This function automatically uncomments the SSLCARevocationCheck directive when
+# the currently installed version of Apache is 2.4.
+update_vhost_for_apache24() {
+ if dpkg --compare-versions "$apache2_version" gt "2.4~"; then
+ sed -r -i \
+ -e "/# SSLCARevocationCheck/s/# //" \
+ $tempfile
+ fi
+}
+
+# Update an existing vhost definition with the SSLCARevocationCheck directive
+# on Apache 2.4+. This scans an existing vhost file for the SSLCARevocationCheck
+# directive and adds it to the file after the SSLCARevocationFile directive.
+#
+# See https://tickets.puppetlabs.com/browse/PUP-2533 for more information.
+update_vhost_for_apache24_upgrade() {
+ APACHE2_SITE_FILE="/etc/apache2/sites-available/$(apache2_puppetmaster_sitename)"
+
+ if dpkg --compare-versions "$apache2_version" gt "2.4~"; then
+ if ! grep -q "^[[:space:]]*SSLCARevocationCheck" $APACHE2_SITE_FILE ; then
+ tempfile=$(mktemp)
+ sed -r \
+ -e "/SSLCARevocationFile/a\\ SSLCARevocationCheck chain" \
+ $APACHE2_SITE_FILE > $tempfile
+ mv $tempfile $APACHE2_SITE_FILE
+ fi
+ fi
+}
+
+
+create_initial_puppetmaster_vhost() {
+ # Check that puppet master --configprint works properly
+ # If it doesn't the following steps to update the vhost will produce a very unhelpful and broken vhost
+ if [ $(puppet master --configprint all 2>&1 | grep "Could not parse" | wc -l) != "0" ]; then
+ echo "Puppet config print not working properly, exiting"
+ exit 1
+ fi
+
+ # Initialize puppetmaster CA and generate the master certificate
+ # only if the host doesn't already have any puppet ssl certificate.
+ # The ssl key and cert need to be available (eg generated) before
+ # apache2 is configured and started since apache2 ssl configuration
+ # uses the puppetmaster ssl files.
+ if [ ! -e "$(puppet master --configprint hostcert)" ]; then
+ puppet cert generate $(puppet master --configprint certname)
+ fi
+
+ # Setup apache2 configuration files
+ APACHE2_SITE_FILE="/etc/apache2/sites-available/$(apache2_puppetmaster_sitename)"
+ if [ ! -e "${APACHE2_SITE_FILE}" ]; then
+ tempfile=$(mktemp)
+ sed -r \
+ -e "s|(SSLCertificateFile\s+).+$|\1$(puppet master --configprint hostcert)|" \
+ -e "s|(SSLCertificateKeyFile\s+).+$|\1$(puppet master --configprint hostprivkey)|" \
+ -e "s|(SSLCACertificateFile\s+).+$|\1$(puppet master --configprint localcacert)|" \
+ -e "s|(SSLCertificateChainFile\s+).+$|\1$(puppet master --configprint localcacert)|" \
+ -e "s|(SSLCARevocationFile\s+).+$|\1$(puppet master --configprint cacrl)|" \
+ -e "s|DocumentRoot /etc/puppet/rack/public|DocumentRoot /usr/share/puppet/rack/puppetmasterd/public|" \
+ -e "s|||" \
+ /usr/share/puppetmaster-passenger/apache2.site.conf.tmpl > $tempfile
+ update_vhost_for_passenger4
+ update_vhost_for_apache24
+ mv $tempfile "${APACHE2_SITE_FILE}"
+ fi
+
+ # Enable needed modules
+ a2enmod ssl
+ a2enmod headers
+ a2ensite ${sitename}
+ restart_apache2
+}
+
+update_existing_puppetmaster_vhost() {
+ if dpkg --compare-versions "${1}" lt "3.6.2~"; then
+ update_vhost_for_apache24_upgrade
+ fi
+}
+
if [ "$1" = "configure" ]; then
# Change the owner of the rack config.ru to be the puppet user
# because passenger will suid to that user, see #577366
if ! dpkg-statoverride --list /usr/share/puppet/rack/puppetmasterd/config.ru >/dev/null 2>&1
then
dpkg-statoverride --update --add puppet puppet 0644 /usr/share/puppet/rack/puppetmasterd/config.ru
fi
- # Setup passenger configuration
- if [ "$2" = "" ]; then
- # Check that puppet master --configprint works properly
- # If it doesn't the following steps to update the vhost will produce a very unhelpful and broken vhost
- if [ $(puppet master --configprint all 2>&1 | grep "Could not parse" | wc -l) != "0" ]; then
- echo "Puppet config print not working properly, exiting"
- exit 1
- fi
-
- # Initialize puppetmaster CA and generate the master certificate
- # only if the host doesn't already have any puppet ssl certificate.
- # The ssl key and cert need to be available (eg generated) before
- # apache2 is configured and started since apache2 ssl configuration
- # uses the puppetmaster ssl files.
- if [ ! -e "$(puppet master --configprint hostcert)" ]; then
- puppet cert generate $(puppet master --configprint certname)
- fi
-
- # Setup apache2 configuration files
- APACHE2_SITE_FILE="/etc/apache2/sites-available/$(apache2_puppetmaster_sitename)"
- if [ ! -e "${APACHE2_SITE_FILE}" ]; then
- tempfile=$(mktemp)
- sed -r \
- -e "s|(SSLCertificateFile\s+).+$|\1$(puppet master --configprint hostcert)|" \
- -e "s|(SSLCertificateKeyFile\s+).+$|\1$(puppet master --configprint hostprivkey)|" \
- -e "s|(SSLCACertificateFile\s+).+$|\1$(puppet master --configprint localcacert)|" \
- -e "s|(SSLCertificateChainFile\s+).+$|\1$(puppet master --configprint localcacert)|" \
- -e "s|(SSLCARevocationFile\s+).+$|\1$(puppet master --configprint cacrl)|" \
- -e "s|DocumentRoot /etc/puppet/rack/public|DocumentRoot /usr/share/puppet/rack/puppetmasterd/public|" \
- -e "s|||" \
- /usr/share/puppetmaster-passenger/apache2.site.conf.tmpl > $tempfile
- update_vhost_for_passenger4
- mv $tempfile "${APACHE2_SITE_FILE}"
- fi
-
- # Enable needed modules
- a2enmod ssl
- a2enmod headers
- a2ensite ${sitename}
- restart_apache2
+ # Setup puppetmaster passenger vhost
+ if [ "$2" = "" ]; then
+ create_initial_puppetmaster_vhost
+ else
+ update_existing_puppetmaster_vhost $2
fi
# Fix CRL file on upgrade to use the CA crl file instead of the host crl.
if dpkg --compare-versions "$2" lt-nl "2.6.1-1"; then
if [ -e /etc/apache2/sites-available/puppetmaster ]; then
sed -r -i 's|SSLCARevocationFile[[:space:]]+/var/lib/puppet/ssl/crl.pem$|SSLCARevocationFile /var/lib/puppet/ssl/ca/ca_crl.pem|' /etc/apache2/sites-available/puppetmaster
restart_apache2
fi
fi
fi
#DEBHELPER#
diff --git a/ext/rack/example-passenger-vhost.conf b/ext/rack/example-passenger-vhost.conf
index c14f3cd98..7d40b9498 100644
--- a/ext/rack/example-passenger-vhost.conf
+++ b/ext/rack/example-passenger-vhost.conf
@@ -1,53 +1,57 @@
# This Apache 2 virtual host config shows how to use Puppet as a Rack
# application via Passenger. See
# http://docs.puppetlabs.com/guides/passenger.html for more information.
# You can also use the included config.ru file to run Puppet with other Rack
# servers instead of Passenger.
# you probably want to tune these settings
PassengerHighPerformance on
PassengerMaxPoolSize 12
PassengerPoolIdleTime 1500
# PassengerMaxRequests 1000
PassengerStatThrottleRate 120
RackAutoDetect Off
RailsAutoDetect Off
Listen 8140
SSLEngine on
SSLProtocol ALL -SSLv2
SSLCipherSuite ALL:!aNULL:!eNULL:!DES:!3DES:!IDEA:!SEED:!DSS:!PSK:!RC4:!MD5:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP
SSLHonorCipherOrder on
SSLCertificateFile /etc/puppet/ssl/certs/squigley.namespace.at.pem
SSLCertificateKeyFile /etc/puppet/ssl/private_keys/squigley.namespace.at.pem
SSLCertificateChainFile /etc/puppet/ssl/ca/ca_crt.pem
SSLCACertificateFile /etc/puppet/ssl/ca/ca_crt.pem
# If Apache complains about invalid signatures on the CRL, you can try disabling
# CRL checking by commenting the next line, but this is not recommended.
SSLCARevocationFile /etc/puppet/ssl/ca/ca_crl.pem
+ # Apache 2.4 introduces the SSLCARevocationCheck directive and sets it to none
+ # which effectively disables CRL checking; if you are using Apache 2.4+ you must
+ # specify 'SSLCARevocationCheck chain' to actually use the CRL.
+ # SSLCARevocationCheck chain
SSLVerifyClient optional
SSLVerifyDepth 1
# The `ExportCertData` option is needed for agent certificate expiration warnings
SSLOptions +StdEnvVars +ExportCertData
# This header needs to be set if using a loadbalancer or proxy
RequestHeader unset X-Forwarded-For
RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
DocumentRoot /etc/puppet/rack/public/
RackBaseURI /
Options None
AllowOverride None
Order allow,deny
allow from all
diff --git a/lib/puppet/indirector/facts/facter.rb b/lib/puppet/indirector/facts/facter.rb
index d704f91fd..e22977568 100644
--- a/lib/puppet/indirector/facts/facter.rb
+++ b/lib/puppet/indirector/facts/facter.rb
@@ -1,91 +1,91 @@
require 'puppet/node/facts'
require 'puppet/indirector/code'
class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
desc "Retrieve facts from Facter. This provides a somewhat abstract interface
between Puppet and Facter. It's only `somewhat` abstract because it always
returns the local host's facts, regardless of what you attempt to find."
private
def self.reload_facter
Facter.clear
Facter.loadfacts
end
def self.load_fact_plugins
# Add any per-module fact directories to the factpath
module_fact_dirs = Puppet.lookup(:current_environment).modulepath.collect do |d|
["lib", "plugins"].map do |subdirectory|
Dir.glob("#{d}/*/#{subdirectory}/facter")
end
end.flatten
dirs = module_fact_dirs + Puppet[:factpath].split(File::PATH_SEPARATOR)
dirs.uniq.each do |dir|
load_facts_in_dir(dir)
end
end
def self.setup_external_facts(request)
# Add any per-module fact directories to the factpath
external_facts_dirs = []
request.environment.modules.each do |m|
if m.has_external_facts?
Puppet.info "Loading external facts from #{m.plugin_fact_directory}"
external_facts_dirs << m.plugin_fact_directory
end
end
# Add system external fact directory if it exists
if File.directory?(Puppet[:pluginfactdest])
external_facts_dirs << Puppet[:pluginfactdest]
end
# Add to facter config
Facter.search_external external_facts_dirs
end
def self.load_facts_in_dir(dir)
return unless FileTest.directory?(dir)
Dir.chdir(dir) do
Dir.glob("*.rb").each do |file|
fqfile = ::File.join(dir, file)
begin
Puppet.info "Loading facts in #{fqfile}"
::Timeout::timeout(Puppet[:configtimeout]) do
- load file
+ load File.join('.', file)
end
rescue SystemExit,NoMemoryError
raise
rescue Exception => detail
Puppet.warning "Could not load fact file #{fqfile}: #{detail}"
end
end
end
end
public
def destroy(facts)
raise Puppet::DevError, "You cannot destroy facts in the code store; it is only used for getting facts from Facter"
end
# Look a host's facts up in Facter.
def find(request)
self.class.setup_external_facts(request) if Puppet.features.external_facts?
self.class.reload_facter
self.class.load_fact_plugins
result = Puppet::Node::Facts.new(request.key, Facter.to_hash)
result.add_local_facts
Puppet[:stringify_facts] ? result.stringify : result.sanitize
result
end
def save(facts)
raise Puppet::DevError, "You cannot save facts to the code store; it is only used for getting facts from Facter"
end
end
diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb
index cb90fb7c3..cf6dad908 100755
--- a/spec/unit/indirector/facts/facter_spec.rb
+++ b/spec/unit/indirector/facts/facter_spec.rb
@@ -1,203 +1,203 @@
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/indirector/facts/facter'
module PuppetNodeFactsFacter
describe Puppet::Node::Facts::Facter do
FS = Puppet::FileSystem
it "should be a subclass of the Code terminus" do
Puppet::Node::Facts::Facter.superclass.should equal(Puppet::Indirector::Code)
end
it "should have documentation" do
Puppet::Node::Facts::Facter.doc.should_not be_nil
end
it "should be registered with the configuration store indirection" do
indirection = Puppet::Indirector::Indirection.instance(:facts)
Puppet::Node::Facts::Facter.indirection.should equal(indirection)
end
it "should have its name set to :facter" do
Puppet::Node::Facts::Facter.name.should == :facter
end
describe "when reloading Facter" do
before do
@facter_class = Puppet::Node::Facts::Facter
Facter.stubs(:clear)
Facter.stubs(:load)
Facter.stubs(:loadfacts)
end
it "should clear Facter" do
Facter.expects(:clear)
@facter_class.reload_facter
end
it "should load all Facter facts" do
Facter.expects(:loadfacts)
@facter_class.reload_facter
end
end
end
describe Puppet::Node::Facts::Facter do
before :each do
Puppet::Node::Facts::Facter.stubs(:reload_facter)
@facter = Puppet::Node::Facts::Facter.new
Facter.stubs(:to_hash).returns({})
@name = "me"
@request = stub 'request', :key => @name
@environment = stub 'environment'
@request.stubs(:environment).returns(@environment)
@request.environment.stubs(:modules).returns([])
end
describe Puppet::Node::Facts::Facter, " when finding facts" do
it "should reset and load facts" do
clear = sequence 'clear'
Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear)
Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear)
@facter.find(@request)
end
it "should include external facts when feature is present" do
clear = sequence 'clear'
Puppet.features.stubs(:external_facts?).returns(:true)
Puppet::Node::Facts::Facter.expects(:setup_external_facts).in_sequence(clear)
Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear)
Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear)
@facter.find(@request)
end
it "should return a Facts instance" do
@facter.find(@request).should be_instance_of(Puppet::Node::Facts)
end
it "should return a Facts instance with the provided key as the name" do
@facter.find(@request).name.should == @name
end
it "should return the Facter facts as the values in the Facts instance" do
Facter.expects(:to_hash).returns("one" => "two")
facts = @facter.find(@request)
facts.values["one"].should == "two"
end
it "should add local facts" do
facts = Puppet::Node::Facts.new("foo")
Puppet::Node::Facts.expects(:new).returns facts
facts.expects(:add_local_facts)
@facter.find(@request)
end
it "should convert facts into strings when stringify_facts is true" do
Puppet[:stringify_facts] = true
facts = Puppet::Node::Facts.new("foo")
Puppet::Node::Facts.expects(:new).returns facts
facts.expects(:stringify)
@facter.find(@request)
end
it "should sanitize facts when stringify_facts is false" do
Puppet[:stringify_facts] = false
facts = Puppet::Node::Facts.new("foo")
Puppet::Node::Facts.expects(:new).returns facts
facts.expects(:sanitize)
@facter.find(@request)
end
end
describe Puppet::Node::Facts::Facter, " when saving facts" do
it "should fail" do
proc { @facter.save(@facts) }.should raise_error(Puppet::DevError)
end
end
describe Puppet::Node::Facts::Facter, " when destroying facts" do
it "should fail" do
proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError)
end
end
it "should skip files when asked to load a directory" do
FileTest.expects(:directory?).with("myfile").returns false
Puppet::Node::Facts::Facter.load_facts_in_dir("myfile")
end
it "should load each ruby file when asked to load a directory" do
FileTest.expects(:directory?).with("mydir").returns true
Dir.expects(:chdir).with("mydir").yields
Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb}
- Puppet::Node::Facts::Facter.expects(:load).with("a.rb")
- Puppet::Node::Facts::Facter.expects(:load).with("b.rb")
+ Puppet::Node::Facts::Facter.expects(:load).with File.join('.', 'a.rb')
+ Puppet::Node::Facts::Facter.expects(:load).with File.join('.', 'b.rb')
Puppet::Node::Facts::Facter.load_facts_in_dir("mydir")
end
it "should include pluginfactdest when loading external facts",
:if => (Puppet.features.external_facts? and not Puppet.features.microsoft_windows?) do
Puppet[:pluginfactdest] = "/plugin/dest"
@facter.find(@request)
Facter.search_external_path.include?("/plugin/dest")
end
it "should include pluginfactdest when loading external facts",
:if => (Puppet.features.external_facts? and Puppet.features.microsoft_windows?) do
Puppet[:pluginfactdest] = "/plugin/dest"
@facter.find(@request)
Facter.search_external_path.include?("C:/plugin/dest")
end
describe "when loading fact plugins from disk" do
let(:one) { File.expand_path("one") }
let(:two) { File.expand_path("two") }
it "should load each directory in the Fact path" do
Puppet[:factpath] = [one, two].join(File::PATH_SEPARATOR)
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with(one)
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with(two)
Puppet::Node::Facts::Facter.load_fact_plugins
end
it "should load all facts from the modules" do
Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir)
Dir.stubs(:glob).returns []
Dir.expects(:glob).with("#{one}/*/lib/facter").returns %w{oneA oneB}
Dir.expects(:glob).with("#{two}/*/lib/facter").returns %w{twoA twoB}
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB")
FS.overlay(FS::MemoryFile.a_directory(one), FS::MemoryFile.a_directory(two)) do
Puppet.override(:current_environment => Puppet::Node::Environment.create(:testing, [one, two], "")) do
Puppet::Node::Facts::Facter.load_fact_plugins
end
end
end
it "should include module plugin facts when present", :if => Puppet.features.external_facts? do
mod = Puppet::Module.new("mymodule", "#{one}/mymodule", @request.environment)
@request.environment.stubs(:modules).returns([mod])
@facter.find(@request)
Facter.search_external_path.include?("#{one}/mymodule/facts.d")
end
end
end
end