puppet 2.6.17 → 2.6.18

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 (43) hide show
  1. data/CHANGELOG +25 -0
  2. data/conf/auth.conf +3 -3
  3. data/conf/redhat/puppet.spec +5 -2
  4. data/lib/puppet.rb +1 -1
  5. data/lib/puppet/indirector/catalog/compiler.rb +13 -2
  6. data/lib/puppet/indirector/errors.rb +5 -0
  7. data/lib/puppet/indirector/file_bucket_file/file.rb +4 -0
  8. data/lib/puppet/indirector/file_bucket_file/selector.rb +4 -0
  9. data/lib/puppet/indirector/indirection.rb +1 -0
  10. data/lib/puppet/indirector/request.rb +4 -0
  11. data/lib/puppet/indirector/resource/ral.rb +4 -0
  12. data/lib/puppet/indirector/resource/validator.rb +8 -0
  13. data/lib/puppet/indirector/rest.rb +4 -0
  14. data/lib/puppet/indirector/run/local.rb +4 -0
  15. data/lib/puppet/indirector/terminus.rb +20 -0
  16. data/lib/puppet/network/http/handler.rb +9 -0
  17. data/lib/puppet/network/http/rack/rest.rb +7 -2
  18. data/lib/puppet/network/http/webrick.rb +1 -0
  19. data/lib/puppet/network/http_pool.rb +17 -8
  20. data/lib/puppet/network/rest_authconfig.rb +1 -1
  21. data/lib/puppet/parser/templatewrapper.rb +13 -12
  22. data/lib/puppet/util/monkey_patches.rb +43 -0
  23. data/spec/integration/indirector/bucket_file/rest_spec.rb +1 -1
  24. data/spec/integration/indirector/catalog/compiler_spec.rb +1 -0
  25. data/spec/integration/indirector/catalog/queue_spec.rb +1 -1
  26. data/spec/integration/indirector/certificate_request/rest_spec.rb +1 -1
  27. data/spec/integration/indirector/report/rest_spec.rb +1 -1
  28. data/spec/integration/resource/catalog_spec.rb +1 -0
  29. data/spec/unit/indirector/catalog/compiler_spec.rb +30 -3
  30. data/spec/unit/indirector/indirection_spec.rb +18 -1
  31. data/spec/unit/indirector/request_spec.rb +22 -0
  32. data/spec/unit/indirector/terminus_spec.rb +186 -174
  33. data/spec/unit/network/http/handler_spec.rb +1 -0
  34. data/spec/unit/network/http/rack/rest_spec.rb +17 -0
  35. data/spec/unit/network/http/webrick_spec.rb +4 -0
  36. data/spec/unit/network/http_pool_spec.rb +93 -80
  37. data/spec/unit/network/rest_authconfig_spec.rb +16 -3
  38. data/spec/unit/parser/functions/inline_template_spec.rb +14 -1
  39. data/spec/unit/parser/functions/template_spec.rb +18 -1
  40. data/spec/unit/parser/templatewrapper_spec.rb +24 -9
  41. data/spec/unit/util/monkey_patches_spec.rb +11 -1
  42. data/test/language/snippets.rb +1 -1
  43. metadata +7 -5
data/CHANGELOG CHANGED
@@ -1,3 +1,28 @@
1
+ 2.6.18
2
+ ===
3
+ b166c4f (#19391) Find the catalog for the specified node name
4
+ f256c6d Don't assume master supports SSLv2
5
+ bb288aa Don't require openssl client to return 0 on failure
6
+ a11a690 Display SSL messages so we can match our regex
7
+ f07b761 Don't assume puppetbindir is defined
8
+ e617728 Remove unnecessary rubygems require
9
+ 75a5f7e Run openssl from windows when trying to downgrade master
10
+ e6b6124 Separate tests for same CVEs into separate files
11
+ bdcf029 Fix order-dependent test failure in rest_authconfig_spec
12
+ 66249d4 Always read request body when using Rack
13
+ d5c9a2c (#19392) (CVE-2013-1653) Fix acceptance test to catch unvalidated model on 2.6
14
+ ac44d87 (#19392) (CVE-2013-1653) Validate indirection model in save handler
15
+ b01c728 Acceptance tests for CVEs 2013 (1640, 1652, 1653, 1654, 2274, 2275)
16
+ 16fce8e (#19531) (CVE-2013-2275) Only allow report save from the node matching the certname
17
+ 7648de2 (#19391) Backport Request#remote? method
18
+ 906ab92 (#8858) Explicitly set SSL peer verification mode.
19
+ 31dad7d (#8858) Refactor tests to use real HTTP objects
20
+ 6a7bd25 (#19392) (CVE-2013-1653) Validate instances passed to indirector
21
+ ccf2e4c (#19391) (CVE-2013-1652) Disallow use_node compiler parameter for remote requests
22
+ add9998 (#19151) Reject SSLv2 SSL handshakes and ciphers
23
+ d9ad70a (#14093) Restore access to the filename in the template
24
+ f45cd4b (#14093) Remove unsafe attributes from TemplateWrapper
25
+
1
26
  2.6.17
2
27
  ===
3
28
  554eefc Reject directory traversal in store report processor
@@ -58,10 +58,10 @@ path /certificate_revocation_list/ca
58
58
  method find
59
59
  allow *
60
60
 
61
- # allow all nodes to store their reports
62
- path /report
61
+ # allow all nodes to store their own reports
62
+ path ~ ^/report/([^/]+)$
63
63
  method save
64
- allow *
64
+ allow $1
65
65
 
66
66
  # inconditionnally allow access to all files services
67
67
  # which means in practice that fileserver.conf will
@@ -5,7 +5,7 @@
5
5
  %global confdir conf/redhat
6
6
 
7
7
  Name: puppet
8
- Version: 2.6.17
8
+ Version: 2.6.18
9
9
  Release: 1%{?dist}
10
10
  Summary: A network tool for managing many disparate systems
11
11
  License: GPLv2
@@ -253,8 +253,11 @@ fi
253
253
  rm -rf %{buildroot}
254
254
 
255
255
  %changelog
256
+ * Sun Mar 10 2013 Matthaus Owens <matthaus@puppetlabs.com> - 2.6.18-1
257
+ - Update for 2.6.18 (CVE-2013-1640, CVE-2013-1652, CVE-2013-1654, CVE-2013-2274, CVE-2013-2275)
258
+
256
259
  * Mon Jul 19 2012 Moses Mendoza <moses@puppetlabs.com> - 2.6.17-1
257
- - Update for 2.7.17
260
+ - Update for 2.6.17
258
261
 
259
262
  * Mon Dec 12 2011 Matthaus Litteken <matthaus@puppetlabs.com> - 2.6.13-1
260
263
  - Release of 2.6.13
@@ -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.17'
27
+ PUPPETVERSION = '2.6.18'
28
28
 
29
29
  def Puppet.version
30
30
  PUPPETVERSION
@@ -13,7 +13,9 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
13
13
 
14
14
  def extract_facts_from_request(request)
15
15
  return unless text_facts = request.options[:facts]
16
- raise ArgumentError, "Facts but no fact format provided for #{request.name}" unless format = request.options[:facts_format]
16
+ unless format = request.options[:facts_format]
17
+ raise ArgumentError, "Facts but no fact format provided for #{request.key}"
18
+ end
17
19
 
18
20
  # If the facts were encoded as yaml, then the param reconstitution system
19
21
  # in Network::HTTP::Handler will automagically deserialize the value.
@@ -22,6 +24,11 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
22
24
  else
23
25
  facts = Puppet::Node::Facts.convert_from(format, text_facts)
24
26
  end
27
+
28
+ unless facts.name == request.key
29
+ raise Puppet::Error, "Catalog for #{request.key.inspect} was requested with fact definition for the wrong node (#{facts.name.inspect})."
30
+ end
31
+
25
32
  facts.save
26
33
  end
27
34
 
@@ -104,7 +111,11 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
104
111
  # to find the node.
105
112
  def node_from_request(request)
106
113
  if node = request.options[:use_node]
107
- return node
114
+ if request.remote?
115
+ raise Puppet::Error, "Invalid option use_node for a remote request"
116
+ else
117
+ return node
118
+ end
108
119
  end
109
120
 
110
121
  # We rely on our authorization system to determine whether the connected
@@ -0,0 +1,5 @@
1
+ require 'puppet/error'
2
+
3
+ module Puppet::Indirector
4
+ class ValidationError < Puppet::Error; end
5
+ end
@@ -48,6 +48,10 @@ module Puppet::FileBucketFile
48
48
  instance.to_s
49
49
  end
50
50
 
51
+ def validate_key(request)
52
+ # There are no ACLs on filebucket files so validating key is not important
53
+ end
54
+
51
55
  private
52
56
 
53
57
  def path_match(dir_path, files_original_path)
@@ -44,6 +44,10 @@ module Puppet::FileBucketFile
44
44
  true
45
45
  end
46
46
  end
47
+
48
+ def validate_key(request)
49
+ get_terminus(request).validate(request)
50
+ end
47
51
  end
48
52
  end
49
53
 
@@ -301,6 +301,7 @@ class Puppet::Indirector::Indirection
301
301
 
302
302
  dest_terminus = terminus(terminus_name)
303
303
  check_authorization(request, dest_terminus)
304
+ dest_terminus.validate(request)
304
305
 
305
306
  dest_terminus
306
307
  end
@@ -149,6 +149,10 @@ class Puppet::Indirector::Request
149
149
  return(uri ? uri : "/#{indirection_name}/#{key}")
150
150
  end
151
151
 
152
+ def remote?
153
+ self.node or self.ip
154
+ end
155
+
152
156
  private
153
157
 
154
158
  def set_attributes(options)
@@ -1,4 +1,8 @@
1
+ require 'puppet/indirector/resource/validator'
2
+
1
3
  class Puppet::Resource::Ral < Puppet::Indirector::Code
4
+ include Puppet::Resource::Validator
5
+
2
6
  def find( request )
3
7
  # find by name
4
8
  res = type(request).instances.find { |o| o.name == resource_name(request) }
@@ -0,0 +1,8 @@
1
+ module Puppet::Resource::Validator
2
+ def validate_key(request)
3
+ type, title = request.key.split('/', 2)
4
+ unless type.downcase == request.instance.type.downcase and title == request.instance.title
5
+ raise Puppet::Indirector::ValidationError, "Resource instance does not match request key"
6
+ end
7
+ end
8
+ end
@@ -107,6 +107,10 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
107
107
  deserialize network(request).put(indirection2uri(request), request.instance.render, headers.merge({ "Content-Type" => request.instance.mime }))
108
108
  end
109
109
 
110
+ def validate_key(request)
111
+ # Validation happens on the remote end
112
+ end
113
+
110
114
  private
111
115
 
112
116
  def environment
@@ -5,4 +5,8 @@ class Puppet::Run::Local < Puppet::Indirector::Code
5
5
  def save( request )
6
6
  request.instance.run
7
7
  end
8
+
9
+ def validate_key(request)
10
+ # No key is necessary for kick
11
+ end
8
12
  end
@@ -1,4 +1,5 @@
1
1
  require 'puppet/indirector'
2
+ require 'puppet/indirector/errors'
2
3
  require 'puppet/indirector/indirection'
3
4
  require 'puppet/util/instance_loader'
4
5
 
@@ -145,4 +146,23 @@ class Puppet::Indirector::Terminus
145
146
  def terminus_type
146
147
  self.class.terminus_type
147
148
  end
149
+
150
+ def validate(request)
151
+ if request.instance
152
+ validate_model(request)
153
+ validate_key(request)
154
+ end
155
+ end
156
+
157
+ def validate_key(request)
158
+ unless request.key == request.instance.name
159
+ raise Puppet::Indirector::ValidationError, "Instance name #{request.instance.name.inspect} does not match requested key #{request.key.inspect}"
160
+ end
161
+ end
162
+
163
+ def validate_model(request)
164
+ unless model === request.instance
165
+ raise Puppet::Indirector::ValidationError, "Invalid instance type #{request.instance.class.inspect}, expected #{model.inspect}"
166
+ end
167
+ end
148
168
  end
@@ -70,6 +70,8 @@ module Puppet::Network::HTTP::Handler
70
70
  raise
71
71
  rescue Exception => e
72
72
  return do_exception(response, e)
73
+ ensure
74
+ cleanup(request)
73
75
  end
74
76
 
75
77
  # Set the response up, with the body and status.
@@ -155,6 +157,9 @@ module Puppet::Network::HTTP::Handler
155
157
 
156
158
  format = request_format(request)
157
159
  obj = indirection_request.model.convert_from(format, data)
160
+ unless indirection_request.model === obj
161
+ raise ArgumentError, "Request data must be of type #{indirection_request.model.inspect}"
162
+ end
158
163
  result = save_object(indirection_request, obj)
159
164
  return_yaml_response(response, result)
160
165
  end
@@ -217,6 +222,10 @@ module Puppet::Network::HTTP::Handler
217
222
  raise NotImplementedError
218
223
  end
219
224
 
225
+ def cleanup(request)
226
+ # By default, there is nothing to cleanup.
227
+ end
228
+
220
229
  def decode_params(params)
221
230
  params.inject({}) do |result, ary|
222
231
  param, value = ary
@@ -73,12 +73,17 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler
73
73
  end
74
74
 
75
75
  # return the request body
76
- # request.body has some limitiations, so we need to concat it back
77
- # into a regular string, which is something puppet can use.
78
76
  def body(request)
79
77
  request.body.read
80
78
  end
81
79
 
80
+ # Passenger freaks out if we finish handling the request without reading any
81
+ # part of the body, so make sure we have.
82
+ def cleanup(request)
83
+ request.body.read(1)
84
+ nil
85
+ end
86
+
82
87
  def extract_client_info(request)
83
88
  result = {}
84
89
  result[:ip] = request.ip
@@ -104,6 +104,7 @@ class Puppet::Network::HTTP::WEBrick
104
104
  results[:SSLCertificate] = host.certificate.content
105
105
  results[:SSLStartImmediately] = true
106
106
  results[:SSLEnable] = true
107
+ results[:SSLOptions] = OpenSSL::SSL::OP_NO_SSLv2
107
108
 
108
109
  raise Puppet::Error, "Could not find CA certificate" unless Puppet::SSL::Certificate.find(Puppet::SSL::CA_NAME)
109
110
 
@@ -50,14 +50,23 @@ module Puppet::Network::HttpPool
50
50
 
51
51
  # Use cert information from a Puppet client to set up the http object.
52
52
  def self.cert_setup(http)
53
- # Just no-op if we don't have certs.
54
- return false unless FileTest.exist?(Puppet[:hostcert]) and FileTest.exist?(Puppet[:localcacert])
55
-
56
- http.cert_store = ssl_host.ssl_store
57
- http.ca_file = Puppet[:localcacert]
58
- http.cert = ssl_host.certificate.content
59
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
60
- http.key = ssl_host.key.content
53
+ if FileTest.exist?(Puppet[:hostcert]) and FileTest.exist?(Puppet[:localcacert])
54
+ http.cert_store = ssl_host.ssl_store
55
+ http.ca_file = Puppet[:localcacert]
56
+ http.cert = ssl_host.certificate.content
57
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
58
+ http.key = ssl_host.key.content
59
+ else
60
+ # We don't have the local certificates, so we don't do any verification
61
+ # or setup at this early stage. REVISIT: Shouldn't we supply the local
62
+ # certificate details if we have them? The original code didn't.
63
+ # --daniel 2012-06-03
64
+
65
+ # Ruby 1.8 defaulted to this, but 1.9 defaults to peer verify, and we
66
+ # almost always talk to a dedicated, not-standard CA that isn't trusted
67
+ # out of the box. This forces the expected state.
68
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
69
+ end
61
70
  end
62
71
 
63
72
  # Retrieve a cached http instance if caching is enabled, else return
@@ -12,7 +12,7 @@ module Puppet
12
12
  # to fileserver.conf
13
13
  { :acl => "/file" },
14
14
  { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true },
15
- { :acl => "/report", :method => :save, :authenticated => true },
15
+ { :acl => "~ ^\/report\/([^\/]+)$", :method => :save, :allow => '$1', :authenticated => true },
16
16
  { :acl => "/certificate/ca", :method => :find, :authenticated => false },
17
17
  { :acl => "/certificate/", :method => :find, :authenticated => false },
18
18
  { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false },
@@ -5,8 +5,6 @@ require 'erb'
5
5
 
6
6
  class Puppet::Parser::TemplateWrapper
7
7
  attr_writer :scope
8
- attr_reader :file
9
- attr_accessor :string
10
8
  include Puppet::Util
11
9
  Puppet::Util.logmethods(self)
12
10
 
@@ -14,6 +12,10 @@ class Puppet::Parser::TemplateWrapper
14
12
  @__scope__ = scope
15
13
  end
16
14
 
15
+ def file
16
+ @__file__
17
+ end
18
+
17
19
  def scope
18
20
  @__scope__
19
21
  end
@@ -68,41 +70,40 @@ class Puppet::Parser::TemplateWrapper
68
70
  end
69
71
 
70
72
  def file=(filename)
71
- unless @file = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s)
73
+ unless @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s)
72
74
  raise Puppet::ParseError, "Could not find template '#{filename}'"
73
75
  end
74
76
 
75
77
  # We'll only ever not have a parser in testing, but, eh.
76
- scope.known_resource_types.watch_file(file)
77
-
78
- @string = File.read(file)
78
+ scope.known_resource_types.watch_file(@__file__)
79
79
  end
80
80
 
81
81
  def result(string = nil)
82
82
  if string
83
- self.string = string
84
83
  template_source = "inline template"
85
84
  else
86
- template_source = file
85
+ string = File.read(@__file__)
86
+ template_source = @__file__
87
87
  end
88
88
 
89
89
  # Expose all the variables in our scope as instance variables of the
90
90
  # current object, making it possible to access them without conflict
91
91
  # to the regular methods.
92
92
  benchmark(:debug, "Bound template variables for #{template_source}") do
93
- scope.to_hash.each { |name, value|
93
+ scope.to_hash.each do |name, value|
94
94
  if name.kind_of?(String)
95
95
  realname = name.gsub(/[^\w]/, "_")
96
96
  else
97
97
  realname = name
98
98
  end
99
99
  instance_variable_set("@#{realname}", value)
100
- }
100
+ end
101
101
  end
102
102
 
103
103
  result = nil
104
104
  benchmark(:debug, "Interpolated template #{template_source}") do
105
- template = ERB.new(self.string, 0, "-")
105
+ template = ERB.new(string, 0, "-")
106
+ template.filename = @__file__
106
107
  result = template.result(binding)
107
108
  end
108
109
 
@@ -110,6 +111,6 @@ class Puppet::Parser::TemplateWrapper
110
111
  end
111
112
 
112
113
  def to_s
113
- "template[#{(file ? file : "inline")}]"
114
+ "template[#{(@__file__ ? @__file__ : "inline")}]"
114
115
  end
115
116
  end
@@ -138,3 +138,46 @@ class IO
138
138
  lines
139
139
  end
140
140
  end
141
+
142
+ # (#19151) Reject all SSLv2 ciphers and handshakes
143
+ require 'openssl'
144
+ class OpenSSL::SSL::SSLContext
145
+ if match = /^1\.8\.(\d+)/.match(RUBY_VERSION)
146
+ older_than_187 = match[1].to_i < 7
147
+ else
148
+ older_than_187 = false
149
+ end
150
+
151
+ alias __original_initialize initialize
152
+ private :__original_initialize
153
+
154
+ if older_than_187
155
+ def initialize(*args)
156
+ __original_initialize(*args)
157
+ if bitmask = self.options
158
+ self.options = bitmask | OpenSSL::SSL::OP_NO_SSLv2
159
+ else
160
+ self.options = OpenSSL::SSL::OP_NO_SSLv2
161
+ end
162
+ # These are the default ciphers in recent MRI versions. See
163
+ # https://github.com/ruby/ruby/blob/v1_9_3_392/ext/openssl/lib/openssl/ssl-internal.rb#L26
164
+ self.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW"
165
+ end
166
+ else
167
+ if DEFAULT_PARAMS[:options]
168
+ DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv2
169
+ else
170
+ DEFAULT_PARAMS[:options] = OpenSSL::SSL::OP_NO_SSLv2
171
+ end
172
+ DEFAULT_PARAMS[:ciphers] << ':!SSLv2'
173
+
174
+ def initialize(*args)
175
+ __original_initialize(*args)
176
+ params = {
177
+ :options => DEFAULT_PARAMS[:options],
178
+ :ciphers => DEFAULT_PARAMS[:ciphers],
179
+ }
180
+ set_params(params)
181
+ end
182
+ end
183
+ end