File puppet-July-2012-CVE-fixes.patch of Package puppet.openSUSE_12.1_Update
From 1219f9a09feac3e364d4ac1eada6aad180f62d2b Mon Sep 17 00:00:00 2001
From: Nick Lewis <nick@puppetlabs.com>
Date: Tue, 26 Jun 2012 16:46:45 -0700
Subject: [PATCH] Deprecate IP-based authentication
This will need to be made explicit in a future version, so it's good to
get people to stop using it if possible.
Add Selector terminus for file_content/file_metadata
This terminus is now the default, and encapsulates the behavior that was
previously in the IndirectionHooks. That is, we dynamically select the
terminus to use for a file request based on the key. However, for the
puppet master, we instead explicitly always use the FileServer terminus,
so that *all* requests for files from the master will go through
the file server. This ensures that we will never accidentally
serve local files on the puppet master.
Reject file requests containing ..
Fail more gracefully when finding module files if no file is specified
Previously this would just complain about trying to call `nil.split`.
Always use the local file_bucket on master
This fixes an issue where the master could be made to issue arbitrary
HTTPS requests through carefully constructed URLs. Now the master will
always use the file_bucket, whereas other applications retain their
behavior of dynamically selecting the source (rest or file) based on the
particular request.
Use head method to determine if file is in file bucket
This call was using find, but wasn't doing anything with the result,
only checking whether the file existed. Using head here is more
semantic and saves the trouble of reading the file if it does
exist.
Fix whitespace inside parentheses
Add specs for selector terminuses of file_{content,metadata}
Validate CSR CN and provided certname before signing
This adds a few new checks when signing CSRs, to validate the CN. First,
it must conform to a small set of characters, which are the printable
ASCII characters, except for '/' (because we store these in files). This
prevents attacks such as a CN "foo^H^H^Hbar", which appears as "bar" to
"puppet cert list".
The other check is that the certname for the SSL::Host that we think
we're signing must match the CN. This prevents a CSR with the CN "foo"
from being submitted as a CSR for "bar", which would cause it to appear
as "bar" to "puppet cert list", but to issue a certificate for "foo".
Don't allow the creation of SSL objects with invalid certnames
This ensures we catch invalid certnames earlier in the process, such as
when an agent is starting or making a certificate request, rather than
at the very last step of signing the request. This should make for
better error messages to the user.
Use "inspect" when listing certificates
This fixes an issue of `puppet cert list` potentially not accurately
depicting the actual certname of the certificate/request. For instance,
"foo^H^H^Hbar" would simply appear as "bar".
Reject directory traversal in store report processor
---
lib/puppet/application/master.rb | 34 ++++--
lib/puppet/face/file/download.rb | 2 +-
lib/puppet/file_bucket/file.rb | 13 +-
lib/puppet/file_bucket/file/indirection_hooks.rb | 9 --
lib/puppet/file_serving/configuration.rb | 3 +-
lib/puppet/file_serving/content.rb | 3 +-
lib/puppet/file_serving/indirection_hooks.rb | 31 -----
lib/puppet/file_serving/metadata.rb | 3 +-
lib/puppet/file_serving/mount/modules.rb | 9 +-
lib/puppet/file_serving/terminus_selector.rb | 28 +++++
lib/puppet/indirector/file_bucket_file/selector.rb | 49 ++++++++
lib/puppet/indirector/file_content/selector.rb | 30 +++++
lib/puppet/indirector/file_metadata/selector.rb | 30 +++++
lib/puppet/network/authstore.rb | 12 +-
lib/puppet/reports/store.rb | 10 +-
lib/puppet/ssl/base.rb | 8 ++
lib/puppet/ssl/certificate_authority.rb | 10 ++
lib/puppet/ssl/certificate_authority/interface.rb | 6 +-
lib/puppet/ssl/host.rb | 1 +
spec/integration/file_bucket/file_spec.rb | 44 +++++++
spec/integration/file_serving/content_spec.rb | 11 +-
spec/integration/file_serving/metadata_spec.rb | 13 +-
spec/integration/network/rest_authconfig_spec.rb | 27 ++++-
spec/shared_behaviours/file_serving.rb | 128 +++++++++++---------
spec/shared_behaviours/file_serving_model.rb | 73 +++++++++++
spec/unit/file_serving/configuration_spec.rb | 66 +++++-----
spec/unit/file_serving/content_spec.rb | 4 -
spec/unit/file_serving/indirection_hooks_spec.rb | 59 ---------
spec/unit/file_serving/metadata_spec.rb | 4 -
spec/unit/file_serving/mount/modules_spec.rb | 8 ++
spec/unit/file_serving/terminus_selector_spec.rb | 58 +++++++++
.../indirector/file_bucket_file/selector_spec.rb | 29 +++++
spec/unit/indirector/file_content/selector_spec.rb | 10 ++
.../unit/indirector/file_metadata/selector_spec.rb | 11 ++
spec/unit/network/handler/ca_spec.rb | 1 +
spec/unit/reports/store_spec.rb | 14 +++
.../ssl/certificate_authority/interface_spec.rb | 34 +++---
spec/unit/ssl/certificate_authority_spec.rb | 87 +++++++++++--
38 files changed, 696 insertions(+), 276 deletions(-)
delete mode 100644 lib/puppet/file_bucket/file/indirection_hooks.rb
delete mode 100644 lib/puppet/file_serving/indirection_hooks.rb
create mode 100644 lib/puppet/file_serving/terminus_selector.rb
create mode 100644 lib/puppet/indirector/file_bucket_file/selector.rb
create mode 100644 lib/puppet/indirector/file_content/selector.rb
create mode 100644 lib/puppet/indirector/file_metadata/selector.rb
create mode 100644 spec/integration/file_bucket/file_spec.rb
mode change 100755 => 100644 spec/integration/file_serving/content_spec.rb
mode change 100755 => 100644 spec/integration/file_serving/metadata_spec.rb
create mode 100644 spec/shared_behaviours/file_serving_model.rb
delete mode 100755 spec/unit/file_serving/indirection_hooks_spec.rb
create mode 100755 spec/unit/file_serving/terminus_selector_spec.rb
create mode 100644 spec/unit/indirector/file_bucket_file/selector_spec.rb
create mode 100644 spec/unit/indirector/file_content/selector_spec.rb
create mode 100644 spec/unit/indirector/file_metadata/selector_spec.rb
Index: puppet-2.7.6/lib/puppet/application/master.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/application/master.rb
+++ puppet-2.7.6/lib/puppet/application/master.rb
@@ -163,8 +163,6 @@ Copyright (c) 2011 Puppet Labs, LLC Lice
def main
require 'etc'
- require 'puppet/file_serving/content'
- require 'puppet/file_serving/metadata'
xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket]
@@ -205,9 +203,7 @@ Copyright (c) 2011 Puppet Labs, LLC Lice
end
end
- def setup
- raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows?
-
+ def setup_logs
# Handle the logging settings.
if options[:debug] or options[:verbose]
if options[:debug]
@@ -223,14 +219,22 @@ Copyright (c) 2011 Puppet Labs, LLC Lice
end
Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
+ end
- exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
-
- Puppet.settings.use :main, :master, :ssl, :metrics
+ def setup_terminuses
+ require 'puppet/file_serving/content'
+ require 'puppet/file_serving/metadata'
# Cache our nodes in yaml. Currently not configurable.
Puppet::Node.indirection.cache_class = :yaml
+ Puppet::FileServing::Content.indirection.terminus_class = :file_server
+ Puppet::FileServing::Metadata.indirection.terminus_class = :file_server
+
+ Puppet::FileBucket::File.indirection.terminus_class = :file
+ end
+
+ def setup_ssl
# Configure all of the SSL stuff.
if Puppet::SSL::CertificateAuthority.ca?
Puppet::SSL::Host.ca_location = :local
@@ -240,4 +244,18 @@ Copyright (c) 2011 Puppet Labs, LLC Lice
Puppet::SSL::Host.ca_location = :none
end
end
+
+ def setup
+ raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows?
+
+ setup_logs
+
+ exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
+
+ Puppet.settings.use :main, :master, :ssl, :metrics
+
+ setup_terminuses
+
+ setup_ssl
+ end
end
Index: puppet-2.7.6/lib/puppet/face/file/download.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/face/file/download.rb
+++ puppet-2.7.6/lib/puppet/face/file/download.rb
@@ -35,7 +35,7 @@ Puppet::Face.define(:file, '0.0.1') do
key = "#{type}/#{sumdata}"
Puppet::FileBucket::File.indirection.terminus_class = :file
- if Puppet::FileBucket::File.indirection.find(key)
+ if Puppet::FileBucket::File.indirection.head(key)
Puppet.info "Content for '#{sum}' already exists"
return
end
Index: puppet-2.7.6/lib/puppet/file_bucket/file.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_bucket/file.rb
+++ puppet-2.7.6/lib/puppet/file_bucket/file.rb
@@ -8,13 +8,12 @@ class Puppet::FileBucket::File
# There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/*
# There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper
extend Puppet::Indirector
- require 'puppet/file_bucket/file/indirection_hooks'
- indirects :file_bucket_file, :terminus_class => :file, :extend => Puppet::FileBucket::File::IndirectionHooks
+ indirects :file_bucket_file, :terminus_class => :selector
attr :contents
attr :bucket_path
- def initialize( contents, options = {} )
+ def initialize(contents, options = {})
raise ArgumentError.new("contents must be a String, got a #{contents.class}") unless contents.is_a?(String)
@contents = contents
@@ -42,15 +41,15 @@ class Puppet::FileBucket::File
"#{checksum_type}/#{checksum_data}"
end
- def self.from_s( contents )
- self.new( contents )
+ def self.from_s(contents)
+ self.new(contents)
end
def to_pson
{ "contents" => contents }.to_pson
end
- def self.from_pson( pson )
- self.new( pson["contents"] )
+ def self.from_pson(pson)
+ self.new(pson["contents"])
end
end
Index: puppet-2.7.6/lib/puppet/file_bucket/file/indirection_hooks.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_bucket/file/indirection_hooks.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'puppet/file_bucket/file'
-
-# This module is used to pick the appropriate terminus
-# in filebucket indirections.
-module Puppet::FileBucket::File::IndirectionHooks
- def select_terminus(request)
- return(request.protocol == 'https' ? :rest : Puppet::FileBucket::File.indirection.terminus_class)
- end
-end
Index: puppet-2.7.6/lib/puppet/file_serving/configuration.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_serving/configuration.rb
+++ puppet-2.7.6/lib/puppet/file_serving/configuration.rb
@@ -64,7 +64,8 @@ class Puppet::FileServing::Configuration
mount_name, path = request.key.split(File::Separator, 2)
- raise(ArgumentError, "Cannot find file: Invalid path '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
+ raise(ArgumentError, "Cannot find file: Invalid mount '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
+ raise(ArgumentError, "Cannot find file: Invalid relative path '#{path}'") if path and path.split('/').include?('..')
return nil unless mount = find_mount(mount_name, request.environment)
if mount.name == "modules" and mount_name != "modules"
Index: puppet-2.7.6/lib/puppet/file_serving/content.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_serving/content.rb
+++ puppet-2.7.6/lib/puppet/file_serving/content.rb
@@ -1,14 +1,13 @@
require 'puppet/indirector'
require 'puppet/file_serving'
require 'puppet/file_serving/base'
-require 'puppet/file_serving/indirection_hooks'
# A class that handles retrieving file contents.
# It only reads the file when its content is specifically
# asked for.
class Puppet::FileServing::Content < Puppet::FileServing::Base
extend Puppet::Indirector
- indirects :file_content, :extend => Puppet::FileServing::IndirectionHooks
+ indirects :file_content, :terminus_class => :selector
attr_writer :content
Index: puppet-2.7.6/lib/puppet/file_serving/indirection_hooks.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_serving/indirection_hooks.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'uri'
-require 'puppet/file_serving'
-require 'puppet/util'
-
-# This module is used to pick the appropriate terminus
-# in file-serving indirections. This is necessary because
-# the terminus varies based on the URI asked for.
-module Puppet::FileServing::IndirectionHooks
- PROTOCOL_MAP = {"puppet" => :rest, "file" => :file}
-
- # Pick an appropriate terminus based on the protocol.
- def select_terminus(request)
- # We rely on the request's parsing of the URI.
-
- # Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol.
- return PROTOCOL_MAP["file"] if Puppet::Util.absolute_path?(request.key)
- return PROTOCOL_MAP["file"] if request.protocol == "file"
-
- # We're heading over the wire the protocol is 'puppet' and we've got a server name or we're not named 'apply' or 'puppet'
- if request.protocol == "puppet" and (request.server or !["puppet","apply"].include?(Puppet.settings[:name]))
- return PROTOCOL_MAP["puppet"]
- end
-
- if request.protocol and PROTOCOL_MAP[request.protocol].nil?
- raise(ArgumentError, "URI protocol '#{request.protocol}' is not currently supported for file serving")
- end
-
- # If we're still here, we're using the file_server or modules.
- :file_server
- end
-end
Index: puppet-2.7.6/lib/puppet/file_serving/metadata.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_serving/metadata.rb
+++ puppet-2.7.6/lib/puppet/file_serving/metadata.rb
@@ -3,7 +3,6 @@ require 'puppet/indirector'
require 'puppet/file_serving'
require 'puppet/file_serving/base'
require 'puppet/util/checksums'
-require 'puppet/file_serving/indirection_hooks'
# A class that handles retrieving file metadata.
class Puppet::FileServing::Metadata < Puppet::FileServing::Base
@@ -11,7 +10,7 @@ class Puppet::FileServing::Metadata < Pu
include Puppet::Util::Checksums
extend Puppet::Indirector
- indirects :file_metadata, :extend => Puppet::FileServing::IndirectionHooks
+ indirects :file_metadata, :terminus_class => :selector
attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination
Index: puppet-2.7.6/lib/puppet/file_serving/mount/modules.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/file_serving/mount/modules.rb
+++ puppet-2.7.6/lib/puppet/file_serving/mount/modules.rb
@@ -5,6 +5,7 @@ require 'puppet/file_serving/mount'
class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount
# Return an instance of the appropriate class.
def find(path, request)
+ raise "No module specified" if path.to_s.empty?
module_name, relative_path = path.split("/", 2)
return nil unless mod = request.environment.module(module_name)
@@ -12,11 +13,9 @@ class Puppet::FileServing::Mount::Module
end
def search(path, request)
- module_name, relative_path = path.split("/", 2)
- return nil unless mod = request.environment.module(module_name)
-
- return nil unless path = mod.file(relative_path)
- [path]
+ if result = find(path, request)
+ [result]
+ end
end
def valid?
Index: puppet-2.7.6/lib/puppet/file_serving/terminus_selector.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/lib/puppet/file_serving/terminus_selector.rb
@@ -0,0 +1,28 @@
+require 'puppet/file_serving'
+
+# This module is used to pick the appropriate terminus
+# in file-serving indirections. This is necessary because
+# the terminus varies based on the URI asked for.
+module Puppet::FileServing::TerminusSelector
+ PROTOCOL_MAP = {"puppet" => :rest, "file" => :file}
+
+ def select(request)
+ # We rely on the request's parsing of the URI.
+
+ # Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol.
+ return PROTOCOL_MAP["file"] if Puppet::Util.absolute_path?(request.key)
+ return PROTOCOL_MAP["file"] if request.protocol == "file"
+
+ # We're heading over the wire the protocol is 'puppet' and we've got a server name or we're not named 'apply' or 'puppet'
+ if request.protocol == "puppet" and (request.server or !["puppet","apply"].include?(Puppet.settings[:name]))
+ return PROTOCOL_MAP["puppet"]
+ end
+
+ if request.protocol and PROTOCOL_MAP[request.protocol].nil?
+ raise(ArgumentError, "URI protocol '#{request.protocol}' is not currently supported for file serving")
+ end
+
+ # If we're still here, we're using the file_server or modules.
+ :file_server
+ end
+end
Index: puppet-2.7.6/lib/puppet/indirector/file_bucket_file/selector.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/lib/puppet/indirector/file_bucket_file/selector.rb
@@ -0,0 +1,49 @@
+require 'puppet/indirector/code'
+
+module Puppet::FileBucketFile
+ class Selector < Puppet::Indirector::Code
+ desc "Select the terminus based on the request"
+
+ def select(request)
+ if request.protocol == 'https'
+ :rest
+ else
+ :file
+ end
+ end
+
+ def get_terminus(request)
+ indirection.terminus(select(request))
+ end
+
+ def head(request)
+ get_terminus(request).head(request)
+ end
+
+ def find(request)
+ get_terminus(request).find(request)
+ end
+
+ def save(request)
+ get_terminus(request).save(request)
+ end
+
+ def search(request)
+ get_terminus(request).search(request)
+ end
+
+ def destroy(request)
+ get_terminus(request).destroy(request)
+ end
+
+ def authorized?(request)
+ terminus = get_terminus(request)
+ if terminus.respond_to?(:authorized?)
+ terminus.authorized?(request)
+ else
+ true
+ end
+ end
+ end
+end
+
Index: puppet-2.7.6/lib/puppet/indirector/file_content/selector.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/lib/puppet/indirector/file_content/selector.rb
@@ -0,0 +1,30 @@
+require 'puppet/file_serving/content'
+require 'puppet/indirector/file_content'
+require 'puppet/indirector/code'
+require 'puppet/file_serving/terminus_selector'
+
+class Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code
+ desc "Select the terminus based on the request"
+ include Puppet::FileServing::TerminusSelector
+
+ def get_terminus(request)
+ indirection.terminus(select(request))
+ end
+
+ def find(request)
+ get_terminus(request).find(request)
+ end
+
+ def search(request)
+ get_terminus(request).search(request)
+ end
+
+ def authorized?(request)
+ terminus = get_terminus(request)
+ if terminus.respond_to?(:authorized?)
+ terminus.authorized?(request)
+ else
+ true
+ end
+ end
+end
Index: puppet-2.7.6/lib/puppet/indirector/file_metadata/selector.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/lib/puppet/indirector/file_metadata/selector.rb
@@ -0,0 +1,30 @@
+require 'puppet/file_serving/metadata'
+require 'puppet/indirector/file_metadata'
+require 'puppet/indirector/code'
+require 'puppet/file_serving/terminus_selector'
+
+class Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code
+ desc "Select the terminus based on the request"
+ include Puppet::FileServing::TerminusSelector
+
+ def get_terminus(request)
+ indirection.terminus(select(request))
+ end
+
+ def find(request)
+ get_terminus(request).find(request)
+ end
+
+ def search(request)
+ get_terminus(request).search(request)
+ end
+
+ def authorized?(request)
+ terminus = get_terminus(request)
+ if terminus.respond_to?(:authorized?)
+ terminus.authorized?(request)
+ else
+ true
+ end
+ end
+end
Index: puppet-2.7.6/lib/puppet/network/authstore.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/network/authstore.rb
+++ puppet-2.7.6/lib/puppet/network/authstore.rb
@@ -150,7 +150,16 @@ module Puppet
# Does this declaration match the name/ip combo?
def match?(name, ip)
- ip? ? pattern.include?(IPAddr.new(ip)) : matchname?(name)
+ if ip?
+ if pattern.include?(IPAddr.new(ip))
+ Puppet.deprecation_warning "Authentication based on IP address is deprecated; please use certname-based rules instead"
+ true
+ else
+ false
+ end
+ else
+ matchname?(name)
+ end
end
# Set the pattern appropriately. Also sets the name and length.
@@ -212,7 +221,6 @@ module Puppet
# Convert the name to a common pattern.
def munge_name(name)
- # LAK:NOTE http://snurl.com/21zf8 [groups_google_com]
# Change to name.downcase.split(".",-1).reverse for FQDN support
name.downcase.split(".").reverse
end
Index: puppet-2.7.6/lib/puppet/reports/store.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/reports/store.rb
+++ puppet-2.7.6/lib/puppet/reports/store.rb
@@ -1,5 +1,7 @@
require 'puppet'
+SEPARATOR = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join
+
Puppet::Reports.register_report(:store) do
desc "Store the yaml report on disk. Each host sends its report as a YAML dump
and this just stores the file on disk, in the `reportdir` directory.
@@ -11,9 +13,11 @@ Puppet::Reports.register_report(:store)
def process
# We don't want any tracking back in the fs. Unlikely, but there
# you go.
- client = self.host.gsub("..",".")
+ if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/)
+ raise ArgumentError, "Invalid node name #{host.inspect}"
+ end
- dir = File.join(Puppet[:reportdir], client)
+ dir = File.join(Puppet[:reportdir], host)
if ! FileTest.exists?(dir)
FileUtils.mkdir_p(dir)
@@ -35,7 +39,7 @@ Puppet::Reports.register_report(:store)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
- Puppet.warning "Could not write report for #{client} at #{file}: #{detail}"
+ Puppet.warning "Could not write report for #{host} at #{file}: #{detail}"
end
# Only testing cares about the return value
Index: puppet-2.7.6/lib/puppet/ssl/base.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/ssl/base.rb
+++ puppet-2.7.6/lib/puppet/ssl/base.rb
@@ -5,6 +5,9 @@ class Puppet::SSL::Base
# For now, use the YAML separator.
SEPARATOR = "\n---\n"
+ # Only allow printing ascii characters, excluding /
+ VALID_CERTNAME = /\A[ -.0-~]+\Z/
+
def self.from_multiple_s(text)
text.split(SEPARATOR).collect { |inst| from_s(inst) }
end
@@ -22,6 +25,10 @@ class Puppet::SSL::Base
@wrapped_class
end
+ def self.validate_certname(name)
+ raise "Certname #{name.inspect} must not contain unprintable or non-ASCII characters" unless name =~ VALID_CERTNAME
+ end
+
attr_accessor :name, :content
# Is this file for the CA?
@@ -35,6 +42,7 @@ class Puppet::SSL::Base
def initialize(name)
@name = name.to_s.downcase
+ self.class.validate_certname(@name)
end
# Read content from disk appropriately.
Index: puppet-2.7.6/lib/puppet/ssl/certificate_authority.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/ssl/certificate_authority.rb
+++ puppet-2.7.6/lib/puppet/ssl/certificate_authority.rb
@@ -303,6 +303,16 @@ class Puppet::SSL::CertificateAuthority
raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
end
+ # Do not sign misleading CSRs
+ cn = csr.content.subject.to_a.assoc("CN")[1]
+ if hostname != cn
+ raise CertificateSigningError.new(hostname), "CSR subject common name #{cn.inspect} does not match expected certname #{hostname.inspect}"
+ end
+
+ if hostname !~ Puppet::SSL::Base::VALID_CERTNAME
+ raise CertificateSigningError.new(hostname), "CSR #{hostname.inspect} subject contains unprintable or non-ASCII characters"
+ end
+
# Wildcards: we don't allow 'em at any point.
#
# The stringification here makes the content visible, and saves us having
Index: puppet-2.7.6/lib/puppet/ssl/certificate_authority/interface.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/ssl/certificate_authority/interface.rb
+++ puppet-2.7.6/lib/puppet/ssl/certificate_authority/interface.rb
@@ -88,6 +88,8 @@ module Puppet
names = certs.values.map(&:keys).flatten
name_width = names.sort_by(&:length).last.length rescue 0
+ # We quote these names, so account for those characters
+ name_width += 2
output = [:request, :signed, :invalid].map do |type|
next if certs[type].empty?
@@ -113,11 +115,11 @@ module Puppet
alt_names.delete(host)
- alt_str = "(alt names: #{alt_names.join(', ')})" unless alt_names.empty?
+ alt_str = "(alt names: #{alt_names.map(&:inspect).join(', ')})" unless alt_names.empty?
glyph = {:signed => '+', :request => ' ', :invalid => '-'}[type]
- name = host.ljust(width)
+ name = host.inspect.ljust(width)
fingerprint = "(#{ca.fingerprint(host, @digest)})"
explanation = "(#{verify_error})" if verify_error
Index: puppet-2.7.6/lib/puppet/ssl/host.rb
===================================================================
--- puppet-2.7.6.orig/lib/puppet/ssl/host.rb
+++ puppet-2.7.6/lib/puppet/ssl/host.rb
@@ -222,6 +222,7 @@ class Puppet::SSL::Host
def initialize(name = nil)
@name = (name || Puppet[:certname]).downcase
+ Puppet::SSL::Base.validate_certname(@name)
@key = @certificate = @certificate_request = nil
@ca = (name == self.class.ca_name)
end
Index: puppet-2.7.6/spec/integration/file_bucket/file_spec.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/integration/file_bucket/file_spec.rb
@@ -0,0 +1,44 @@
+#!/usr/bin/env rspec
+
+require 'spec_helper'
+
+require 'puppet/file_bucket/file'
+
+describe Puppet::FileBucket::File do
+ describe "#indirection" do
+ before :each do
+ # Never connect to the network, no matter what
+ described_class.indirection.terminus(:rest).class.any_instance.stubs(:find)
+ end
+
+ describe "when running the master application" do
+ before :each do
+ Puppet::Application[:master].setup_terminuses
+ end
+
+ {
+ "md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
+ "https://puppetmaster:8140/production/file_bucket_file/md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
+ }.each do |key, terminus|
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
+
+ described_class.indirection.find(key)
+ end
+ end
+ end
+
+ describe "when running another application" do
+ {
+ "md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
+ "https://puppetmaster:8140/production/file_bucket_file/md5/d41d8cd98f00b204e9800998ecf8427e" => :rest,
+ }.each do |key, terminus|
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
+
+ described_class.indirection.find(key)
+ end
+ end
+ end
+ end
+end
Index: puppet-2.7.6/spec/integration/file_serving/content_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/integration/file_serving/content_spec.rb
+++ puppet-2.7.6/spec/integration/file_serving/content_spec.rb
@@ -1,14 +1,9 @@
#!/usr/bin/env rspec
+
require 'spec_helper'
require 'puppet/file_serving/content'
-require 'shared_behaviours/file_serving'
-
-describe Puppet::FileServing::Content, " when finding files" do
- it_should_behave_like "Puppet::FileServing::Files"
- before do
- @test_class = Puppet::FileServing::Content
- @indirection = Puppet::FileServing::Content.indirection
- end
+describe Puppet::FileServing::Content do
+ it_should_behave_like "a file_serving model"
end
Index: puppet-2.7.6/spec/integration/file_serving/metadata_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/integration/file_serving/metadata_spec.rb
+++ puppet-2.7.6/spec/integration/file_serving/metadata_spec.rb
@@ -1,15 +1,10 @@
#!/usr/bin/env rspec
+
require 'spec_helper'
require 'puppet/file_serving/metadata'
-require 'shared_behaviours/file_serving'
-require 'puppet/indirector/file_metadata/file_server'
-
-describe Puppet::FileServing::Metadata, " when finding files" do
- it_should_behave_like "Puppet::FileServing::Files"
- before do
- @test_class = Puppet::FileServing::Metadata
- @indirection = Puppet::FileServing::Metadata.indirection
- end
+describe Puppet::FileServing::Metadata do
+ it_should_behave_like "a file_serving model"
end
+
Index: puppet-2.7.6/spec/integration/network/rest_authconfig_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/integration/network/rest_authconfig_spec.rb
+++ puppet-2.7.6/spec/integration/network/rest_authconfig_spec.rb
@@ -44,12 +44,31 @@ describe Puppet::Network::RestAuthConfig
end
def request(args = {})
- { :ip => '10.1.1.1', :node => 'host.domain.com', :key => 'key', :authenticated => true }.each do |k,v|
- args[k] ||= v
- end
+ args = {
+ :key => 'key',
+ :node => 'host.domain.com',
+ :ip => '10.1.1.1',
+ :authenticated => true
+ }.merge(args)
['test', :find, args[:key], args]
end
+ it "should warn when matching against IP addresses" do
+ add_rule("allow 10.1.1.1")
+
+ @auth.should allow(request)
+
+ @logs.should be_any {|log| log.level == :warning and log.message =~ /Authentication based on IP address is deprecated/}
+ end
+
+ it "should not warn when matches against IP addresses fail" do
+ add_rule("allow 10.1.1.2")
+
+ @auth.should_not allow(request)
+
+ @logs.should_not be_any {|log| log.level == :warning and log.message =~ /Authentication based on IP address is deprecated/}
+ end
+
it "should support IPv4 address" do
add_rule("allow 10.1.1.1")
@@ -142,4 +161,4 @@ describe Puppet::Network::RestAuthConfig
@auth.should_not allow(request)
end
-end
\ No newline at end of file
+end
Index: puppet-2.7.6/spec/shared_behaviours/file_serving.rb
===================================================================
--- puppet-2.7.6.orig/spec/shared_behaviours/file_serving.rb
+++ puppet-2.7.6/spec/shared_behaviours/file_serving.rb
@@ -1,68 +1,80 @@
#!/usr/bin/env rspec
-shared_examples_for "Puppet::FileServing::Files" do
- it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do
- uri = "puppet://myhost/fakemod/my/file"
-
- # It appears that the mocking somehow interferes with the caching subsystem.
- # This mock somehow causes another terminus to get generated.
- term = @indirection.terminus(:rest)
- @indirection.stubs(:terminus).with(:rest).returns term
- term.expects(:find)
- @indirection.find(uri)
- end
- it "should use the rest terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is not 'puppet' or 'apply'" do
- uri = "puppet:///fakemod/my/file"
- Puppet.settings.stubs(:value).returns "foo"
- Puppet.settings.stubs(:value).with(:name).returns("puppetd")
- Puppet.settings.stubs(:value).with(:modulepath).returns("")
- @indirection.terminus(:rest).expects(:find)
- @indirection.find(uri)
- end
+shared_examples_for "Puppet::FileServing::Files" do |indirection|
+ %w[find search].each do |method|
+ let(:request) { Puppet::Indirector::Request.new(indirection, method, 'foo') }
- it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'puppet'" do
- uri = "puppet:///fakemod/my/file"
- Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => []))
- Puppet.settings.stubs(:value).returns ""
- Puppet.settings.stubs(:value).with(:name).returns("puppet")
- Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever")
- @indirection.terminus(:file_server).expects(:find)
- @indirection.terminus(:file_server).stubs(:authorized?).returns(true)
- @indirection.find(uri)
- end
+ before :each do
+ # Stub this so we can set the :name setting
+ Puppet::Util::Settings::ReadOnly.stubs(:include?)
+ end
- it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'apply'" do
- uri = "puppet:///fakemod/my/file"
- Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => []))
- Puppet.settings.stubs(:value).returns ""
- Puppet.settings.stubs(:value).with(:name).returns("apply")
- Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever")
- @indirection.terminus(:file_server).expects(:find)
- @indirection.terminus(:file_server).stubs(:authorized?).returns(true)
- @indirection.find(uri)
- end
+ describe "##{method}" do
+ it "should proxy to file terminus if the path is absolute" do
+ request.key = make_absolute('/tmp/foo')
- it "should use the file terminus when the 'file' URI scheme is used" do
- uri = Puppet::Util.path_to_uri(File.expand_path('/fakemod/my/other file'))
- uri.scheme.should == 'file'
- @indirection.terminus(:file).expects(:find)
- @indirection.find(uri.to_s)
- end
+ described_class.indirection.terminus(:file).class.any_instance.expects(method).with(request)
- it "should use the file terminus when a fully qualified path is provided" do
- uri = File.expand_path("/fakemod/my/file")
- @indirection.terminus(:file).expects(:find)
- @indirection.find(uri)
- end
+ subject.send(method, request)
+ end
+
+ it "should proxy to file terminus if the protocol is file" do
+ request.protocol = 'file'
+
+ described_class.indirection.terminus(:file).class.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+
+ describe "when the protocol is puppet" do
+ before :each do
+ request.protocol = 'puppet'
+ end
+
+ describe "and a server is specified" do
+ before :each do
+ request.server = 'puppet_server'
+ end
+
+ it "should proxy to rest terminus if we're 'apply'" do
+ Puppet[:name] = 'apply'
+
+ described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+
+ it "should proxy to rest terminus if we aren't 'apply'" do
+ Puppet[:name] = 'not_apply'
+
+ described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+ end
+
+ describe "and no server is specified" do
+ before :each do
+ request.server = nil
+ end
+
+ it "should proxy to file_server if we're 'apply'" do
+ Puppet[:name] = 'apply'
+
+ described_class.indirection.terminus(:file_server).class.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+
+ it "should proxy to rest if we're not 'apply'" do
+ Puppet[:name] = 'not_apply'
+
+ described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request)
- it "should use the configuration to test whether the request is allowed" do
- uri = "fakemod/my/file"
- mount = mock 'mount'
- config = stub 'configuration', :split_path => [mount, "eh"]
- @indirection.terminus(:file_server).stubs(:configuration).returns config
-
- @indirection.terminus(:file_server).expects(:find)
- mount.expects(:allowed?).returns(true)
- @indirection.find(uri, :node => "foo", :ip => "bar")
+ subject.send(method, request)
+ end
+ end
+ end
+ end
end
end
Index: puppet-2.7.6/spec/shared_behaviours/file_serving_model.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/shared_behaviours/file_serving_model.rb
@@ -0,0 +1,73 @@
+#!/usr/bin/env rspec
+
+shared_examples_for "a file_serving model" do
+ include PuppetSpec::Files
+
+ describe "#indirection" do
+ before :each do
+ # Never connect to the network, no matter what
+ described_class.indirection.terminus(:rest).class.any_instance.stubs(:find)
+ end
+
+ describe "when running the master application" do
+ before :each do
+ Puppet::Application[:master].setup_terminuses
+ end
+
+ {
+ "/etc/sudoers" => :file_server,
+ "file:///etc/sudoers" => :file_server,
+ "puppet:///modules/foo/bar" => :file_server,
+ "puppet://server/modules/foo/bar" => :file_server,
+ }.each do |key, terminus|
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
+
+ described_class.indirection.find(key)
+ end
+ end
+ end
+
+ describe "when running the apply application" do
+ before :each do
+ # Stub this so we can set the 'name' setting
+ Puppet::Util::Settings::ReadOnly.stubs(:include?)
+ Puppet[:name] = 'apply'
+ end
+
+ {
+ "/etc/sudoers" => :file,
+ "file:///etc/sudoers" => :file,
+ "puppet:///modules/foo/bar" => :file_server,
+ "puppet://server/modules/foo/bar" => :rest,
+ }.each do |key, terminus|
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
+
+ described_class.indirection.find(key)
+ end
+ end
+ end
+
+ describe "when running another application" do
+ before :each do
+ # Stub this so we can set the 'name' setting
+ Puppet::Util::Settings::ReadOnly.stubs(:include?)
+ Puppet[:name] = 'agent'
+ end
+
+ {
+ "/etc/sudoers" => :file,
+ "file:///etc/sudoers" => :file,
+ "puppet:///modules/foo/bar" => :rest,
+ "puppet://server/modules/foo/bar" => :rest,
+ }.each do |key, terminus|
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
+
+ described_class.indirection.find(key)
+ end
+ end
+ end
+ end
+end
Index: puppet-2.7.6/spec/unit/file_serving/configuration_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/file_serving/configuration_spec.rb
+++ puppet-2.7.6/spec/unit/file_serving/configuration_spec.rb
@@ -158,80 +158,88 @@ describe Puppet::FileServing::Configurat
end
end
- describe "when finding the mount name and relative path in a request key" do
- before do
- @config = Puppet::FileServing::Configuration.configuration
- @config.stubs(:find_mount)
+ describe "#split_path" do
+ let(:config) { Puppet::FileServing::Configuration.configuration }
+ let(:request) { stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") }
- @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env")
+ before do
+ config.stubs(:find_mount)
end
it "should reread the configuration" do
- @config.expects(:readconfig)
+ config.expects(:readconfig)
- @config.split_path(@request)
+ config.split_path(request)
end
it "should treat the first field of the URI path as the mount name" do
- @config.expects(:find_mount).with { |name, node| name == "foo" }
+ config.expects(:find_mount).with { |name, node| name == "foo" }
- @config.split_path(@request)
+ config.split_path(request)
end
it "should fail if the mount name is not alpha-numeric" do
- @request.expects(:key).returns "foo&bar/asdf"
+ request.expects(:key).returns "foo&bar/asdf"
- lambda { @config.split_path(@request) }.should raise_error(ArgumentError)
+ lambda { config.split_path(request) }.should raise_error(ArgumentError)
end
it "should support dashes in the mount name" do
- @request.expects(:key).returns "foo-bar/asdf"
+ request.expects(:key).returns "foo-bar/asdf"
- lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError)
+ lambda { config.split_path(request) }.should_not raise_error(ArgumentError)
end
it "should use the mount name and environment to find the mount" do
- @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment }
- @request.stubs(:node).returns("mynode")
+ config.expects(:find_mount).with { |name, env| name == "foo" and env == request.environment }
+ request.stubs(:node).returns("mynode")
- @config.split_path(@request)
+ config.split_path(request)
end
it "should return nil if the mount cannot be found" do
- @config.expects(:find_mount).returns nil
+ config.expects(:find_mount).returns nil
- @config.split_path(@request).should be_nil
+ config.split_path(request).should be_nil
end
it "should return the mount and the relative path if the mount is found" do
mount = stub 'mount', :name => "foo"
- @config.expects(:find_mount).returns mount
+ config.expects(:find_mount).returns mount
- @config.split_path(@request).should == [mount, "bar/baz"]
+ config.split_path(request).should == [mount, "bar/baz"]
end
it "should remove any double slashes" do
- @request.stubs(:key).returns "foo/bar//baz"
+ request.stubs(:key).returns "foo/bar//baz"
mount = stub 'mount', :name => "foo"
- @config.expects(:find_mount).returns mount
+ config.expects(:find_mount).returns mount
+
+ config.split_path(request).should == [mount, "bar/baz"]
+ end
+
+ it "should fail if the path contains .." do
+ request.stubs(:key).returns 'module/foo/../../bar'
- @config.split_path(@request).should == [mount, "bar/baz"]
+ expect do
+ config.split_path(request)
+ end.to raise_error(ArgumentError, /Invalid relative path/)
end
it "should return the relative path as nil if it is an empty string" do
- @request.expects(:key).returns "foo"
+ request.expects(:key).returns "foo"
mount = stub 'mount', :name => "foo"
- @config.expects(:find_mount).returns mount
+ config.expects(:find_mount).returns mount
- @config.split_path(@request).should == [mount, nil]
+ config.split_path(request).should == [mount, nil]
end
it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do
- @request.expects(:key).returns "foo/bar"
+ request.expects(:key).returns "foo/bar"
mount = stub 'mount', :name => "modules"
- @config.expects(:find_mount).returns mount
+ config.expects(:find_mount).returns mount
- @config.split_path(@request).should == [mount, "foo/bar"]
+ config.split_path(request).should == [mount, "foo/bar"]
end
end
end
Index: puppet-2.7.6/spec/unit/file_serving/content_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/file_serving/content_spec.rb
+++ puppet-2.7.6/spec/unit/file_serving/content_spec.rb
@@ -12,10 +12,6 @@ describe Puppet::FileServing::Content do
Puppet::FileServing::Content.indirection.name.should == :file_content
end
- it "should should include the IndirectionHooks module in its indirection" do
- Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks)
- end
-
it "should only support the raw format" do
Puppet::FileServing::Content.supported_formats.should == [:raw]
end
Index: puppet-2.7.6/spec/unit/file_serving/indirection_hooks_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/file_serving/indirection_hooks_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env rspec
-require 'spec_helper'
-
-require 'puppet/file_serving/indirection_hooks'
-
-describe Puppet::FileServing::IndirectionHooks do
- before do
- @object = Object.new
- @object.extend(Puppet::FileServing::IndirectionHooks)
-
- @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil
- end
-
- describe "when being used to select termini" do
- it "should return :file if the request key is fully qualified" do
- @request.expects(:key).returns File.expand_path('/foo')
- @object.select_terminus(@request).should == :file
- end
-
- it "should return :file if the URI protocol is set to 'file'" do
- @request.expects(:protocol).returns "file"
- @object.select_terminus(@request).should == :file
- end
-
- it "should fail when a protocol other than :puppet or :file is used" do
- @request.stubs(:protocol).returns "http"
- proc { @object.select_terminus(@request) }.should raise_error(ArgumentError)
- end
-
- describe "and the protocol is 'puppet'" do
- before do
- @request.stubs(:protocol).returns "puppet"
- end
-
- it "should choose :rest when a server is specified" do
- @request.stubs(:protocol).returns "puppet"
- @request.expects(:server).returns "foo"
- @object.select_terminus(@request).should == :rest
- end
-
- # This is so a given file location works when bootstrapping with no server.
- it "should choose :rest when the Settings name isn't 'puppet'" do
- @request.stubs(:protocol).returns "puppet"
- @request.stubs(:server).returns "foo"
- Puppet.settings.stubs(:value).with(:name).returns "foo"
- @object.select_terminus(@request).should == :rest
- end
-
- it "should choose :file_server when the settings name is 'puppet' and no server is specified" do
- modules = mock 'modules'
-
- @request.expects(:protocol).returns "puppet"
- @request.expects(:server).returns nil
- Puppet.settings.expects(:value).with(:name).returns "puppet"
- @object.select_terminus(@request).should == :file_server
- end
- end
- end
-end
Index: puppet-2.7.6/spec/unit/file_serving/metadata_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/file_serving/metadata_spec.rb
+++ puppet-2.7.6/spec/unit/file_serving/metadata_spec.rb
@@ -12,10 +12,6 @@ describe Puppet::FileServing::Metadata d
Puppet::FileServing::Metadata.indirection.name.should == :file_metadata
end
- it "should should include the IndirectionHooks module in its indirection" do
- Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks)
- end
-
it "should have a method that triggers attribute collection" do
Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:collect)
end
Index: puppet-2.7.6/spec/unit/file_serving/mount/modules_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/file_serving/mount/modules_spec.rb
+++ puppet-2.7.6/spec/unit/file_serving/mount/modules_spec.rb
@@ -11,6 +11,10 @@ describe Puppet::FileServing::Mount::Mod
end
describe "when finding files" do
+ it "should fail if no module is specified" do
+ expect { @mount.find("", @request) }.to raise_error(/No module specified/)
+ end
+
it "should use the provided environment to find the module" do
@environment.expects(:module)
@@ -36,6 +40,10 @@ describe Puppet::FileServing::Mount::Mod
end
describe "when searching for files" do
+ it "should fail if no module is specified" do
+ expect { @mount.find("", @request) }.to raise_error(/No module specified/)
+ end
+
it "should use the node's environment to search the module" do
@environment.expects(:module)
Index: puppet-2.7.6/spec/unit/file_serving/terminus_selector_spec.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/unit/file_serving/terminus_selector_spec.rb
@@ -0,0 +1,58 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+require 'puppet/file_serving/terminus_selector'
+
+describe Puppet::FileServing::TerminusSelector do
+ before do
+ @object = Object.new
+ @object.extend(Puppet::FileServing::TerminusSelector)
+
+ @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil
+ end
+
+ describe "when being used to select termini" do
+ it "should return :file if the request key is fully qualified" do
+ @request.expects(:key).returns File.expand_path('/foo')
+ @object.select(@request).should == :file
+ end
+
+ it "should return :file if the URI protocol is set to 'file'" do
+ @request.expects(:protocol).returns "file"
+ @object.select(@request).should == :file
+ end
+
+ it "should fail when a protocol other than :puppet or :file is used" do
+ @request.stubs(:protocol).returns "http"
+ proc { @object.select(@request) }.should raise_error(ArgumentError)
+ end
+
+ describe "and the protocol is 'puppet'" do
+ before do
+ @request.stubs(:protocol).returns "puppet"
+ end
+
+ it "should choose :rest when a server is specified" do
+ @request.stubs(:protocol).returns "puppet"
+ @request.expects(:server).returns "foo"
+ @object.select(@request).should == :rest
+ end
+
+ # This is so a given file location works when bootstrapping with no server.
+ it "should choose :rest when the Settings name isn't 'puppet'" do
+ @request.stubs(:protocol).returns "puppet"
+ # We have to stub this because we can't set name
+ Puppet.settings.stubs(:value).with(:name).returns "foo"
+ @object.select(@request).should == :rest
+ end
+
+ it "should choose :file_server when the settings name is 'puppet' and no server is specified" do
+ modules = mock 'modules'
+
+ @request.expects(:protocol).returns "puppet"
+ @request.expects(:server).returns nil
+ @object.select(@request).should == :file_server
+ end
+ end
+ end
+end
Index: puppet-2.7.6/spec/unit/indirector/file_bucket_file/selector_spec.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/unit/indirector/file_bucket_file/selector_spec.rb
@@ -0,0 +1,29 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+require 'puppet/indirector/file_bucket_file/selector'
+require 'puppet/indirector/file_bucket_file/file'
+require 'puppet/indirector/file_bucket_file/rest'
+
+describe Puppet::FileBucketFile::Selector do
+ %w[head find save search destroy].each do |method|
+ describe "##{method}" do
+ it "should proxy to rest terminus for https requests" do
+ request = stub 'request', :protocol => 'https'
+
+ Puppet::FileBucketFile::Rest.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+
+ it "should proxy to file terminus for other requests" do
+ request = stub 'request', :protocol => 'file'
+
+ Puppet::FileBucketFile::File.any_instance.expects(method).with(request)
+
+ subject.send(method, request)
+ end
+ end
+ end
+end
+
Index: puppet-2.7.6/spec/unit/indirector/file_content/selector_spec.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/unit/indirector/file_content/selector_spec.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+require 'puppet/indirector/file_content/selector'
+
+describe Puppet::Indirector::FileContent::Selector do
+ include PuppetSpec::Files
+
+ it_should_behave_like "Puppet::FileServing::Files", :file_content
+end
Index: puppet-2.7.6/spec/unit/indirector/file_metadata/selector_spec.rb
===================================================================
--- /dev/null
+++ puppet-2.7.6/spec/unit/indirector/file_metadata/selector_spec.rb
@@ -0,0 +1,11 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+require 'puppet/indirector/file_metadata/selector'
+
+describe Puppet::Indirector::FileMetadata::Selector do
+ include PuppetSpec::Files
+
+ it_should_behave_like "Puppet::FileServing::Files", :file_metadata
+end
+
Index: puppet-2.7.6/spec/unit/network/handler/ca_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/network/handler/ca_spec.rb
+++ puppet-2.7.6/spec/unit/network/handler/ca_spec.rb
@@ -31,6 +31,7 @@ describe Puppet::Network::Handler::CA do
Puppet::SSL::CertificateAuthority.stubs(:ca?).returns false
csr = OpenSSL::X509::Request.new
+ csr.subject = OpenSSL::X509::Name.new([["CN", "anything"]])
subject.getcert(csr.to_pem).should == ''
end
Index: puppet-2.7.6/spec/unit/reports/store_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/reports/store_spec.rb
+++ puppet-2.7.6/spec/unit/reports/store_spec.rb
@@ -26,5 +26,19 @@ describe processor do
File.read(File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")).should == @report.to_yaml
end
+
+ ['..', 'hello/', '/hello', 'he/llo', 'hello/..', '.'].each do |node|
+ it "rejects #{node.inspect}" do
+ @report.host = node
+ expect { @report.process }.to raise_error(ArgumentError, /Invalid node/)
+ end
+ end
+
+ ['.hello', 'hello.', '..hi', 'hi..'].each do |node|
+ it "accepts #{node.inspect}" do
+ @report.host = node
+ @report.process
+ end
+ end
end
end
Index: puppet-2.7.6/spec/unit/ssl/certificate_authority/interface_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/ssl/certificate_authority/interface_spec.rb
+++ puppet-2.7.6/spec/unit/ssl/certificate_authority/interface_spec.rb
@@ -216,9 +216,9 @@ describe Puppet::SSL::CertificateAuthori
applier = @class.new(:list, :to => [])
applier.expects(:puts).with(<<-OUTPUT.chomp)
- host1 (fingerprint)
- host2 (fingerprint)
- host3 (fingerprint)
+ "host1" (fingerprint)
+ "host2" (fingerprint)
+ "host3" (fingerprint)
OUTPUT
applier.apply(@ca)
@@ -232,12 +232,12 @@ describe Puppet::SSL::CertificateAuthori
applier = @class.new(:list, :to => :all)
applier.expects(:puts).with(<<-OUTPUT.chomp)
- host1 (fingerprint)
- host2 (fingerprint)
- host3 (fingerprint)
-+ host5 (fingerprint)
-+ host6 (fingerprint)
-- host4 (fingerprint) (certificate revoked)
+ "host1" (fingerprint)
+ "host2" (fingerprint)
+ "host3" (fingerprint)
++ "host5" (fingerprint)
++ "host6" (fingerprint)
+- "host4" (fingerprint) (certificate revoked)
OUTPUT
applier.apply(@ca)
@@ -249,9 +249,9 @@ describe Puppet::SSL::CertificateAuthori
applier = @class.new(:list, :to => :signed)
applier.expects(:puts).with(<<-OUTPUT.chomp)
-+ host4 (fingerprint)
-+ host5 (fingerprint)
-+ host6 (fingerprint)
++ "host4" (fingerprint)
++ "host5" (fingerprint)
++ "host6" (fingerprint)
OUTPUT
applier.apply(@ca)
@@ -263,7 +263,7 @@ describe Puppet::SSL::CertificateAuthori
applier = @class.new(:list, :to => ['host1'])
applier.expects(:puts).with(<<-OUTPUT.chomp)
- host1 (fingerprint) (alt names: DNS:foo, DNS:bar)
+ "host1" (fingerprint) (alt names: "DNS:foo", "DNS:bar")
OUTPUT
applier.apply(@ca)
@@ -275,10 +275,10 @@ describe Puppet::SSL::CertificateAuthori
applier = @class.new(:list, :to => %w{host1 host2 host4 host5})
applier.expects(:puts).with(<<-OUTPUT.chomp)
- host1 (fingerprint)
- host2 (fingerprint)
-+ host4 (fingerprint)
-+ host5 (fingerprint)
+ "host1" (fingerprint)
+ "host2" (fingerprint)
++ "host4" (fingerprint)
++ "host5" (fingerprint)
OUTPUT
applier.apply(@ca)
Index: puppet-2.7.6/spec/unit/ssl/certificate_authority_spec.rb
===================================================================
--- puppet-2.7.6.orig/spec/unit/ssl/certificate_authority_spec.rb
+++ puppet-2.7.6/spec/unit/ssl/certificate_authority_spec.rb
@@ -246,7 +246,7 @@ describe Puppet::SSL::CertificateAuthori
# Stub out the factory
Puppet::SSL::CertificateFactory.stubs(:build).returns "my real cert"
- @request_content = stub "request content stub", :subject => @name
+ @request_content = stub "request content stub", :subject => OpenSSL::X509::Name.new([['CN', @name]])
@request = stub 'request', :name => @name, :request_extensions => [], :subject_alt_names => [], :content => @request_content
# And the inventory
@@ -303,28 +303,28 @@ describe Puppet::SSL::CertificateAuthori
it "should use a certificate type of :ca" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
- args[0] == :ca
+ args[0].should == :ca
end.returns "my real cert"
@ca.sign(@name, :ca, @request)
end
it "should pass the provided CSR as the CSR" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
- args[1] == @request
+ args[1].should == @request
end.returns "my real cert"
@ca.sign(@name, :ca, @request)
end
it "should use the provided CSR's content as the issuer" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
- args[2].subject == "myhost"
+ args[2].subject.to_s.should == "/CN=myhost"
end.returns "my real cert"
@ca.sign(@name, :ca, @request)
end
it "should pass the next serial as the serial number" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
- args[3] == @serial
+ args[3].should == @serial
end.returns "my real cert"
@ca.sign(@name, :ca, @request)
end
@@ -452,11 +452,76 @@ describe Puppet::SSL::CertificateAuthori
@cert.stubs :save
end
+ it "should reject CSRs whose CN doesn't match the name for which we're signing them" do
+ # Shorten this so the test doesn't take too long
+ Puppet[:keylength] = 1024
+ key = Puppet::SSL::Key.new('the_certname')
+ key.generate
+
+ csr = Puppet::SSL::CertificateRequest.new('the_certname')
+ csr.generate(key)
+
+ expect do
+ @ca.check_internal_signing_policies('not_the_certname', csr, false)
+ end.to raise_error(
+ Puppet::SSL::CertificateAuthority::CertificateSigningError,
+ /common name "the_certname" does not match expected certname "not_the_certname"/
+ )
+ end
+
+ describe "when validating the CN" do
+ before :all do
+ Puppet[:keylength] = 1024
+ @signing_key = Puppet::SSL::Key.new('my_signing_key')
+ @signing_key.generate
+ end
+
+ [
+ 'completely_okay',
+ 'sure, why not? :)',
+ 'so+many(things)-are=allowed.',
+ 'this"is#just&madness%you[see]',
+ 'and even a (an?) \\!',
+ 'waltz, nymph, for quick jigs vex bud.',
+ '{552c04ca-bb1b-11e1-874b-60334b04494e}'
+ ].each do |name|
+ it "should accept #{name.inspect}" do
+ csr = Puppet::SSL::CertificateRequest.new(name)
+ csr.generate(@signing_key)
+
+ @ca.check_internal_signing_policies(name, csr, false)
+ end
+ end
+
+ [
+ 'super/bad',
+ "not\neven\tkind\rof",
+ "ding\adong\a",
+ "hidden\b\b\b\b\b\bmessage",
+ "☃ :("
+ ].each do |name|
+ it "should reject #{name.inspect}" do
+ # We aren't even allowed to make objects with these names, so let's
+ # stub that to simulate an invalid one coming from outside Puppet
+ Puppet::SSL::CertificateRequest.stubs(:validate_certname)
+ csr = Puppet::SSL::CertificateRequest.new(name)
+ csr.generate(@signing_key)
+
+ expect do
+ @ca.check_internal_signing_policies(name, csr, false)
+ end.to raise_error(
+ Puppet::SSL::CertificateAuthority::CertificateSigningError,
+ /subject contains unprintable or non-ASCII characters/
+ )
+ end
+ end
+ end
+
it "should reject a critical extension that isn't on the whitelist" do
@request.stubs(:request_extensions).returns [{ "oid" => "banana",
"value" => "yumm",
"critical" => true }]
- expect { @ca.sign(@name) }.to raise_error(
+ expect { @ca.check_internal_signing_policies(@name, @request, false) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/request extensions that are not permitted/
)
@@ -466,7 +531,7 @@ describe Puppet::SSL::CertificateAuthori
@request.stubs(:request_extensions).returns [{ "oid" => "peach",
"value" => "meh",
"critical" => false }]
- expect { @ca.sign(@name) }.to raise_error(
+ expect { @ca.check_internal_signing_policies(@name, @request, false) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/request extensions that are not permitted/
)
@@ -479,7 +544,7 @@ describe Puppet::SSL::CertificateAuthori
{ "oid" => "subjectAltName",
"value" => "DNS:foo",
"critical" => true }]
- expect { @ca.sign(@name) }.to raise_error(
+ expect { @ca.check_internal_signing_policies(@name, @request, false) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/request extensions that are not permitted/
)
@@ -487,7 +552,7 @@ describe Puppet::SSL::CertificateAuthori
it "should reject a subjectAltName for a non-DNS value" do
@request.stubs(:subject_alt_names).returns ['DNS:foo', 'email:bar@example.com']
- expect { @ca.sign(@name, true) }.to raise_error(
+ expect { @ca.check_internal_signing_policies(@name, @request, true) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/subjectAltName outside the DNS label space/
)
@@ -497,7 +562,7 @@ describe Puppet::SSL::CertificateAuthori
@request.content.stubs(:subject).
returns(OpenSSL::X509::Name.new([["CN", "*.local"]]))
- expect { @ca.sign(@name) }.to raise_error(
+ expect { @ca.check_internal_signing_policies('*.local', @request, false) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/subject contains a wildcard/
)
@@ -505,7 +570,7 @@ describe Puppet::SSL::CertificateAuthori
it "should reject a wildcard subjectAltName" do
@request.stubs(:subject_alt_names).returns ['DNS:foo', 'DNS:*.bar']
- expect { @ca.sign(@name, true) }.to raise_error(
+ expect { @ca.check_internal_signing_policies(@name, @request, true) }.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
/subjectAltName contains a wildcard/
)