puppet 2.6.16 → 2.6.17

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (39) hide show
  1. data/CHANGELOG +13 -0
  2. data/conf/redhat/puppet.spec +4 -1
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application/master.rb +26 -6
  5. data/lib/puppet/file_bucket/file.rb +5 -6
  6. data/lib/puppet/file_serving/configuration.rb +2 -1
  7. data/lib/puppet/file_serving/content.rb +1 -2
  8. data/lib/puppet/file_serving/metadata.rb +1 -2
  9. data/lib/puppet/file_serving/mount/modules.rb +4 -5
  10. data/lib/puppet/file_serving/{indirection_hooks.rb → terminus_selector.rb} +2 -3
  11. data/lib/puppet/indirector/file_bucket_file/selector.rb +49 -0
  12. data/lib/puppet/indirector/file_content/selector.rb +30 -0
  13. data/lib/puppet/indirector/file_metadata/selector.rb +30 -0
  14. data/lib/puppet/reports/store.rb +7 -3
  15. data/lib/puppet/ssl/base.rb +8 -0
  16. data/lib/puppet/ssl/certificate_authority.rb +10 -0
  17. data/lib/puppet/ssl/certificate_authority/interface.rb +4 -2
  18. data/lib/puppet/ssl/host.rb +1 -0
  19. data/spec/integration/file_bucket/file_spec.rb +44 -0
  20. data/spec/integration/file_serving/content_spec.rb +1 -8
  21. data/spec/integration/file_serving/metadata_spec.rb +1 -8
  22. data/spec/integration/indirector/file_content/file_server_spec.rb +2 -2
  23. data/spec/shared_behaviours/file_serving.rb +71 -62
  24. data/spec/shared_behaviours/file_serving_model.rb +73 -0
  25. data/spec/spec_helper.rb +29 -0
  26. data/spec/unit/file_serving/configuration_spec.rb +37 -29
  27. data/spec/unit/file_serving/content_spec.rb +0 -4
  28. data/spec/unit/file_serving/metadata_spec.rb +0 -4
  29. data/spec/unit/file_serving/mount/modules_spec.rb +8 -0
  30. data/spec/unit/file_serving/{indirection_hooks_spec.rb → terminus_selector_spec.rb} +11 -12
  31. data/spec/unit/indirector/file_bucket_file/selector_spec.rb +29 -0
  32. data/spec/unit/indirector/file_content/selector_spec.rb +10 -0
  33. data/spec/unit/indirector/file_metadata/selector_spec.rb +11 -0
  34. data/spec/unit/network/handler/ca_spec.rb +1 -0
  35. data/spec/unit/reports/store_spec.rb +14 -0
  36. data/spec/unit/ssl/certificate_authority/interface_spec.rb +17 -17
  37. data/spec/unit/ssl/certificate_authority_spec.rb +76 -11
  38. metadata +16 -9
  39. data/lib/puppet/file_bucket/file/indirection_hooks.rb +0 -9
data/CHANGELOG CHANGED
@@ -1,3 +1,16 @@
1
+ 2.6.17
2
+ ===
3
+ 554eefc Reject directory traversal in store report processor
4
+ 9607bd7 Use "inspect" when listing certificates
5
+ 0144e68 Don't allow the creation of SSL objects with invalid certnames
6
+ dfedaa5 Validate CSR CN and provided certname before signing
7
+ 8eb0cd8 Add specs for selector terminuses of file_{content,metadata}
8
+ 828c16a Fix whitespace inside parentheses
9
+ e7ef153 Always use the local file_bucket on master
10
+ 29ae87d Fail more gracefully when finding module files if no file is specified
11
+ c872619 Reject file requests containing ..
12
+ c3c7462 Add Selector terminus for file_content/file_metadata
13
+
1
14
  2.6.16
2
15
  ===
3
16
  65446c9 Revert "(#5246) Puppetd does not remove it's pidfile when it exits"
@@ -5,7 +5,7 @@
5
5
  %global confdir conf/redhat
6
6
 
7
7
  Name: puppet
8
- Version: 2.6.13
8
+ Version: 2.6.17
9
9
  Release: 1%{?dist}
10
10
  Summary: A network tool for managing many disparate systems
11
11
  License: GPLv2
@@ -253,6 +253,9 @@ fi
253
253
  rm -rf %{buildroot}
254
254
 
255
255
  %changelog
256
+ * Mon Jul 19 2012 Moses Mendoza <moses@puppetlabs.com> - 2.6.17-1
257
+ - Update for 2.7.17
258
+
256
259
  * Mon Dec 12 2011 Matthaus Litteken <matthaus@puppetlabs.com> - 2.6.13-1
257
260
  - Release of 2.6.13
258
261
 
@@ -24,7 +24,7 @@ require 'puppet/util/run_mode'
24
24
  # it's also a place to find top-level commands like 'debug'
25
25
 
26
26
  module Puppet
27
- PUPPETVERSION = '2.6.16'
27
+ PUPPETVERSION = '2.6.17'
28
28
 
29
29
  def Puppet.version
30
30
  PUPPETVERSION
@@ -75,8 +75,6 @@ class Puppet::Application::Master < Puppet::Application
75
75
 
76
76
  def main
77
77
  require 'etc'
78
- require 'puppet/file_serving/content'
79
- require 'puppet/file_serving/metadata'
80
78
 
81
79
  xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket]
82
80
 
@@ -117,7 +115,7 @@ class Puppet::Application::Master < Puppet::Application
117
115
  end
118
116
  end
119
117
 
120
- def setup
118
+ def setup_logs
121
119
  # Handle the logging settings.
122
120
  if options[:debug] or options[:verbose]
123
121
  if options[:debug]
@@ -133,14 +131,22 @@ class Puppet::Application::Master < Puppet::Application
133
131
  end
134
132
 
135
133
  Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
134
+ end
136
135
 
137
- exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
138
-
139
- Puppet.settings.use :main, :master, :ssl, :metrics
136
+ def setup_terminuses
137
+ require 'puppet/file_serving/content'
138
+ require 'puppet/file_serving/metadata'
140
139
 
141
140
  # Cache our nodes in yaml. Currently not configurable.
142
141
  Puppet::Node.cache_class = :yaml
143
142
 
143
+ Puppet::FileServing::Content.indirection.terminus_class = :file_server
144
+ Puppet::FileServing::Metadata.indirection.terminus_class = :file_server
145
+
146
+ Puppet::FileBucket::File.indirection.terminus_class = :file
147
+ end
148
+
149
+ def setup_ssl
144
150
  # Configure all of the SSL stuff.
145
151
  if Puppet::SSL::CertificateAuthority.ca?
146
152
  Puppet::SSL::Host.ca_location = :local
@@ -150,4 +156,18 @@ class Puppet::Application::Master < Puppet::Application
150
156
  Puppet::SSL::Host.ca_location = :none
151
157
  end
152
158
  end
159
+
160
+ def setup
161
+ raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows?
162
+
163
+ setup_logs
164
+
165
+ exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
166
+
167
+ Puppet.settings.use :main, :master, :ssl, :metrics
168
+
169
+ setup_terminuses
170
+
171
+ setup_ssl
172
+ end
153
173
  end
@@ -8,8 +8,7 @@ class Puppet::FileBucket::File
8
8
  # There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/*
9
9
  # There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper
10
10
  extend Puppet::Indirector
11
- require 'puppet/file_bucket/file/indirection_hooks'
12
- indirects :file_bucket_file, :terminus_class => :file, :extend => Puppet::FileBucket::File::IndirectionHooks
11
+ indirects :file_bucket_file, :terminus_class => :selector
13
12
 
14
13
  attr :contents
15
14
  attr :bucket_path
@@ -42,15 +41,15 @@ class Puppet::FileBucket::File
42
41
  "#{checksum_type}/#{checksum_data}"
43
42
  end
44
43
 
45
- def self.from_s( contents )
46
- self.new( contents )
44
+ def self.from_s(contents)
45
+ self.new(contents)
47
46
  end
48
47
 
49
48
  def to_pson
50
49
  { "contents" => contents }.to_pson
51
50
  end
52
51
 
53
- def self.from_pson( pson )
54
- self.new( pson["contents"] )
52
+ def self.from_pson(pson)
53
+ self.new(pson["contents"])
55
54
  end
56
55
  end
@@ -70,7 +70,8 @@ class Puppet::FileServing::Configuration
70
70
 
71
71
  mount_name, path = request.key.split(File::Separator, 2)
72
72
 
73
- raise(ArgumentError, "Cannot find file: Invalid path '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
73
+ raise(ArgumentError, "Cannot find file: Invalid mount '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
74
+ raise(ArgumentError, "Cannot find file: Invalid relative path '#{path}'") if path and path.split('/').include?('..')
74
75
 
75
76
  return nil unless mount = find_mount(mount_name, request.environment)
76
77
  if mount.name == "modules" and mount_name != "modules"
@@ -5,14 +5,13 @@
5
5
  require 'puppet/indirector'
6
6
  require 'puppet/file_serving'
7
7
  require 'puppet/file_serving/base'
8
- require 'puppet/file_serving/indirection_hooks'
9
8
 
10
9
  # A class that handles retrieving file contents.
11
10
  # It only reads the file when its content is specifically
12
11
  # asked for.
13
12
  class Puppet::FileServing::Content < Puppet::FileServing::Base
14
13
  extend Puppet::Indirector
15
- indirects :file_content, :extend => Puppet::FileServing::IndirectionHooks
14
+ indirects :file_content, :terminus_class => :selector
16
15
 
17
16
  attr_writer :content
18
17
 
@@ -7,7 +7,6 @@ require 'puppet/indirector'
7
7
  require 'puppet/file_serving'
8
8
  require 'puppet/file_serving/base'
9
9
  require 'puppet/util/checksums'
10
- require 'puppet/file_serving/indirection_hooks'
11
10
 
12
11
  # A class that handles retrieving file metadata.
13
12
  class Puppet::FileServing::Metadata < Puppet::FileServing::Base
@@ -15,7 +14,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base
15
14
  include Puppet::Util::Checksums
16
15
 
17
16
  extend Puppet::Indirector
18
- indirects :file_metadata, :extend => Puppet::FileServing::IndirectionHooks
17
+ indirects :file_metadata, :terminus_class => :selector
19
18
 
20
19
  attr_reader :path, :owner, :group, :mode, :checksum_type, :checksum, :ftype, :destination
21
20
 
@@ -5,6 +5,7 @@ require 'puppet/file_serving/mount'
5
5
  class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount
6
6
  # Return an instance of the appropriate class.
7
7
  def find(path, request)
8
+ raise "No module specified" if path.to_s.empty?
8
9
  module_name, relative_path = path.split("/", 2)
9
10
  return nil unless mod = request.environment.module(module_name)
10
11
 
@@ -12,11 +13,9 @@ class Puppet::FileServing::Mount::Modules < Puppet::FileServing::Mount
12
13
  end
13
14
 
14
15
  def search(path, request)
15
- module_name, relative_path = path.split("/", 2)
16
- return nil unless mod = request.environment.module(module_name)
17
-
18
- return nil unless path = mod.file(relative_path)
19
- [path]
16
+ if result = find(path, request)
17
+ [result]
18
+ end
20
19
  end
21
20
 
22
21
  def valid?
@@ -8,11 +8,10 @@ require 'puppet/file_serving'
8
8
  # This module is used to pick the appropriate terminus
9
9
  # in file-serving indirections. This is necessary because
10
10
  # the terminus varies based on the URI asked for.
11
- module Puppet::FileServing::IndirectionHooks
11
+ module Puppet::FileServing::TerminusSelector
12
12
  PROTOCOL_MAP = {"puppet" => :rest, "file" => :file}
13
13
 
14
- # Pick an appropriate terminus based on the protocol.
15
- def select_terminus(request)
14
+ def select(request)
16
15
  # We rely on the request's parsing of the URI.
17
16
 
18
17
  # Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol.
@@ -0,0 +1,49 @@
1
+ require 'puppet/indirector/code'
2
+
3
+ module Puppet::FileBucketFile
4
+ class Selector < Puppet::Indirector::Code
5
+ desc "Select the terminus based on the request"
6
+
7
+ def select(request)
8
+ if request.protocol == 'https'
9
+ :rest
10
+ else
11
+ :file
12
+ end
13
+ end
14
+
15
+ def get_terminus(request)
16
+ indirection.terminus(select(request))
17
+ end
18
+
19
+ def head(request)
20
+ get_terminus(request).head(request)
21
+ end
22
+
23
+ def find(request)
24
+ get_terminus(request).find(request)
25
+ end
26
+
27
+ def save(request)
28
+ get_terminus(request).save(request)
29
+ end
30
+
31
+ def search(request)
32
+ get_terminus(request).search(request)
33
+ end
34
+
35
+ def destroy(request)
36
+ get_terminus(request).destroy(request)
37
+ end
38
+
39
+ def authorized?(request)
40
+ terminus = get_terminus(request)
41
+ if terminus.respond_to?(:authorized?)
42
+ terminus.authorized?(request)
43
+ else
44
+ true
45
+ end
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,30 @@
1
+ require 'puppet/file_serving/content'
2
+ require 'puppet/indirector/file_content'
3
+ require 'puppet/indirector/code'
4
+ require 'puppet/file_serving/terminus_selector'
5
+
6
+ class Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code
7
+ desc "Select the terminus based on the request"
8
+ include Puppet::FileServing::TerminusSelector
9
+
10
+ def get_terminus(request)
11
+ indirection.terminus(select(request))
12
+ end
13
+
14
+ def find(request)
15
+ get_terminus(request).find(request)
16
+ end
17
+
18
+ def search(request)
19
+ get_terminus(request).search(request)
20
+ end
21
+
22
+ def authorized?(request)
23
+ terminus = get_terminus(request)
24
+ if terminus.respond_to?(:authorized?)
25
+ terminus.authorized?(request)
26
+ else
27
+ true
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'puppet/file_serving/metadata'
2
+ require 'puppet/indirector/file_metadata'
3
+ require 'puppet/indirector/code'
4
+ require 'puppet/file_serving/terminus_selector'
5
+
6
+ class Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code
7
+ desc "Select the terminus based on the request"
8
+ include Puppet::FileServing::TerminusSelector
9
+
10
+ def get_terminus(request)
11
+ indirection.terminus(select(request))
12
+ end
13
+
14
+ def find(request)
15
+ get_terminus(request).find(request)
16
+ end
17
+
18
+ def search(request)
19
+ get_terminus(request).search(request)
20
+ end
21
+
22
+ def authorized?(request)
23
+ terminus = get_terminus(request)
24
+ if terminus.respond_to?(:authorized?)
25
+ terminus.authorized?(request)
26
+ else
27
+ true
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,7 @@
1
1
  require 'puppet'
2
2
 
3
+ SEPARATOR = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join
4
+
3
5
  Puppet::Reports.register_report(:store) do
4
6
  desc "Store the yaml report on disk. Each host sends its report as a YAML dump
5
7
  and this just stores the file on disk, in the `reportdir` directory.
@@ -11,9 +13,11 @@ Puppet::Reports.register_report(:store) do
11
13
  def process
12
14
  # We don't want any tracking back in the fs. Unlikely, but there
13
15
  # you go.
14
- client = self.host.gsub("..",".")
16
+ if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/)
17
+ raise ArgumentError, "Invalid node name #{host.inspect}"
18
+ end
15
19
 
16
- dir = File.join(Puppet[:reportdir], client)
20
+ dir = File.join(Puppet[:reportdir], host)
17
21
 
18
22
  if ! FileTest.exists?(dir)
19
23
  FileUtils.mkdir_p(dir)
@@ -35,7 +39,7 @@ Puppet::Reports.register_report(:store) do
35
39
  end
36
40
  rescue => detail
37
41
  puts detail.backtrace if Puppet[:trace]
38
- Puppet.warning "Could not write report for #{client} at #{file}: #{detail}"
42
+ Puppet.warning "Could not write report for #{host} at #{file}: #{detail}"
39
43
  end
40
44
 
41
45
  # Only testing cares about the return value
@@ -5,6 +5,9 @@ class Puppet::SSL::Base
5
5
  # For now, use the YAML separator.
6
6
  SEPARATOR = "\n---\n"
7
7
 
8
+ # Only allow printing ascii characters, excluding /
9
+ VALID_CERTNAME = /\A[ -.0-~]+\Z/
10
+
8
11
  def self.from_multiple_s(text)
9
12
  text.split(SEPARATOR).collect { |inst| from_s(inst) }
10
13
  end
@@ -22,6 +25,10 @@ class Puppet::SSL::Base
22
25
  @wrapped_class
23
26
  end
24
27
 
28
+ def self.validate_certname(name)
29
+ raise "Certname #{name.inspect} must not contain unprintable or non-ASCII characters" unless name =~ VALID_CERTNAME
30
+ end
31
+
25
32
  attr_accessor :name, :content
26
33
 
27
34
  # Is this file for the CA?
@@ -35,6 +42,7 @@ class Puppet::SSL::Base
35
42
 
36
43
  def initialize(name)
37
44
  @name = name.to_s.downcase
45
+ self.class.validate_certname(@name)
38
46
  end
39
47
 
40
48
  # Read content from disk appropriately.
@@ -300,6 +300,16 @@ class Puppet::SSL::CertificateAuthority
300
300
  raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
301
301
  end
302
302
 
303
+ # Do not sign misleading CSRs
304
+ cn = csr.content.subject.to_a.assoc("CN")[1]
305
+ if hostname != cn
306
+ raise CertificateSigningError.new(hostname), "CSR subject common name #{cn.inspect} does not match expected certname #{hostname.inspect}"
307
+ end
308
+
309
+ if hostname !~ Puppet::SSL::Base::VALID_CERTNAME
310
+ raise CertificateSigningError.new(hostname), "CSR #{hostname.inspect} subject contains unprintable or non-ASCII characters"
311
+ end
312
+
303
313
  # Wildcards: we don't allow 'em at any point.
304
314
  #
305
315
  # The stringification here makes the content visible, and saves us having
@@ -88,6 +88,8 @@ module Puppet
88
88
  names = certs.values.map(&:keys).flatten
89
89
 
90
90
  name_width = names.sort_by(&:length).last.length rescue 0
91
+ # We quote these names, so account for those characters
92
+ name_width += 2
91
93
 
92
94
  output = [:request, :signed, :invalid].map do |type|
93
95
  next if certs[type].empty?
@@ -113,11 +115,11 @@ module Puppet
113
115
 
114
116
  alt_names.delete(host)
115
117
 
116
- alt_str = "(alt names: #{alt_names.join(', ')})" unless alt_names.empty?
118
+ alt_str = "(alt names: #{alt_names.map(&:inspect).join(', ')})" unless alt_names.empty?
117
119
 
118
120
  glyph = {:signed => '+', :request => ' ', :invalid => '-'}[type]
119
121
 
120
- name = host.ljust(width)
122
+ name = host.inspect.ljust(width)
121
123
  fingerprint = "(#{ca.fingerprint(host, @digest)})"
122
124
 
123
125
  explanation = "(#{verify_error})" if verify_error
@@ -206,6 +206,7 @@ class Puppet::SSL::Host
206
206
 
207
207
  def initialize(name = nil)
208
208
  @name = (name || Puppet[:certname]).downcase
209
+ Puppet::SSL::Base.validate_certname(@name)
209
210
  @key = @certificate = @certificate_request = nil
210
211
  @ca = (name == self.class.ca_name)
211
212
  end
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'puppet/file_bucket/file'
6
+
7
+ describe Puppet::FileBucket::File do
8
+ describe "#indirection" do
9
+ before :each do
10
+ # Never connect to the network, no matter what
11
+ described_class.indirection.terminus(:rest).class.any_instance.stubs(:find)
12
+ end
13
+
14
+ describe "when running the master application" do
15
+ before :each do
16
+ Puppet::Application[:master].setup_terminuses
17
+ end
18
+
19
+ {
20
+ "md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
21
+ "https://puppetmaster:8140/production/file_bucket_file/md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
22
+ }.each do |key, terminus|
23
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
24
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
25
+
26
+ described_class.indirection.find(key)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "when running another application" do
32
+ {
33
+ "md5/d41d8cd98f00b204e9800998ecf8427e" => :file,
34
+ "https://puppetmaster:8140/production/file_bucket_file/md5/d41d8cd98f00b204e9800998ecf8427e" => :rest,
35
+ }.each do |key, terminus|
36
+ it "should use the #{terminus} terminus when requesting #{key.inspect}" do
37
+ described_class.indirection.terminus(terminus).class.any_instance.expects(:find)
38
+
39
+ described_class.indirection.find(key)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end