puppet 3.1.0 → 3.1.1

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 (42) hide show
  1. data/conf/auth.conf +3 -3
  2. data/lib/puppet/indirector/catalog/compiler.rb +13 -2
  3. data/lib/puppet/indirector/certificate_status/file.rb +5 -0
  4. data/lib/puppet/indirector/errors.rb +5 -0
  5. data/lib/puppet/indirector/file_bucket_file/file.rb +4 -0
  6. data/lib/puppet/indirector/file_bucket_file/selector.rb +4 -0
  7. data/lib/puppet/indirector/indirection.rb +2 -1
  8. data/lib/puppet/indirector/resource/active_record.rb +2 -0
  9. data/lib/puppet/indirector/resource/ral.rb +3 -0
  10. data/lib/puppet/indirector/resource/store_configs.rb +3 -0
  11. data/lib/puppet/indirector/resource/validator.rb +8 -0
  12. data/lib/puppet/indirector/rest.rb +4 -0
  13. data/lib/puppet/indirector/run/local.rb +4 -0
  14. data/lib/puppet/indirector/terminus.rb +20 -0
  15. data/lib/puppet/network/authconfig.rb +1 -1
  16. data/lib/puppet/network/formats.rb +3 -3
  17. data/lib/puppet/network/http/handler.rb +7 -2
  18. data/lib/puppet/network/http/rack/rest.rb +7 -2
  19. data/lib/puppet/network/http/webrick.rb +1 -0
  20. data/lib/puppet/parser/templatewrapper.rb +16 -16
  21. data/lib/puppet/util/monkey_patches.rb +38 -0
  22. data/lib/puppet/version.rb +1 -1
  23. data/spec/integration/file_serving/terminus_helper_spec.rb +1 -1
  24. data/spec/integration/indirector/catalog/queue_spec.rb +1 -1
  25. data/spec/integration/resource/catalog_spec.rb +1 -0
  26. data/spec/unit/indirector/catalog/compiler_spec.rb +29 -2
  27. data/spec/unit/indirector/indirection_spec.rb +17 -1
  28. data/spec/unit/indirector/terminus_spec.rb +45 -0
  29. data/spec/unit/network/authconfig_spec.rb +12 -0
  30. data/spec/unit/network/authorization_spec.rb +4 -0
  31. data/spec/unit/network/formats_spec.rb +6 -6
  32. data/spec/unit/network/http/connection_spec.rb +0 -1
  33. data/spec/unit/network/http/handler_spec.rb +25 -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/parser/functions/inline_template_spec.rb +15 -44
  37. data/spec/unit/parser/functions/template_spec.rb +43 -56
  38. data/spec/unit/parser/templatewrapper_spec.rb +68 -91
  39. data/spec/unit/ssl/certificate_request_spec.rb +2 -0
  40. data/spec/unit/ssl/host_spec.rb +1 -0
  41. data/spec/unit/util/monkey_patches_spec.rb +20 -0
  42. metadata +2190 -2188
@@ -75,10 +75,10 @@ path /certificate_revocation_list/ca
75
75
  method find
76
76
  allow *
77
77
 
78
- # allow all nodes to store their reports
79
- path /report
78
+ # allow all nodes to store their own reports
79
+ path ~ ^/report/([^/]+)$
80
80
  method save
81
- allow *
81
+ allow $1
82
82
 
83
83
  # Allow all nodes to access all file services; this is necessary for
84
84
  # pluginsync, file serving from modules, and file serving from custom
@@ -12,7 +12,9 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
12
12
 
13
13
  def extract_facts_from_request(request)
14
14
  return unless text_facts = request.options[:facts]
15
- raise ArgumentError, "Facts but no fact format provided for #{request.name}" unless format = request.options[:facts_format]
15
+ unless format = request.options[:facts_format]
16
+ raise ArgumentError, "Facts but no fact format provided for #{request.key}"
17
+ end
16
18
 
17
19
  # If the facts were encoded as yaml, then the param reconstitution system
18
20
  # in Network::HTTP::Handler will automagically deserialize the value.
@@ -21,6 +23,11 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
21
23
  else
22
24
  facts = Puppet::Node::Facts.convert_from(format, text_facts)
23
25
  end
26
+
27
+ unless facts.name == request.key
28
+ raise Puppet::Error, "Catalog for #{request.key.inspect} was requested with fact definition for the wrong node (#{facts.name.inspect})."
29
+ end
30
+
24
31
  facts.add_timestamp
25
32
  Puppet::Node::Facts.indirection.save(facts)
26
33
  end
@@ -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
@@ -83,4 +83,9 @@ class Puppet::Indirector::CertificateStatus::File < Puppet::Indirector::Code
83
83
  nil
84
84
  end
85
85
  end
86
+
87
+ def validate_key(request)
88
+ # We only use desired_state from the instance and use request.key
89
+ # otherwise, so the name does not need to match
90
+ end
86
91
  end
@@ -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
 
@@ -192,7 +192,7 @@ class Puppet::Indirector::Indirection
192
192
  result.expiration ||= self.expiration if result.respond_to?(:expiration)
193
193
  if cache? and request.use_cache?
194
194
  Puppet.info "Caching #{self.name} for #{request.key}"
195
- cache.save request(:save, nil, result, options)
195
+ cache.save request(:save, key, result, options)
196
196
  end
197
197
 
198
198
  return terminus.respond_to?(:filter) ? terminus.filter(result) : result
@@ -303,6 +303,7 @@ class Puppet::Indirector::Indirection
303
303
 
304
304
  dest_terminus = terminus(terminus_name)
305
305
  check_authorization(request, dest_terminus)
306
+ dest_terminus.validate(request)
306
307
 
307
308
  dest_terminus
308
309
  end
@@ -1,6 +1,8 @@
1
1
  require 'puppet/indirector/active_record'
2
+ require 'puppet/indirector/resource/validator'
2
3
 
3
4
  class Puppet::Resource::ActiveRecord < Puppet::Indirector::ActiveRecord
5
+ include Puppet::Resource::Validator
4
6
 
5
7
  desc "A component of ActiveRecord storeconfigs. ActiveRecord-based storeconfigs
6
8
  and inventory are deprecated. See http://links.puppetlabs.com/activerecord-deprecation"
@@ -1,4 +1,7 @@
1
+ require 'puppet/indirector/resource/validator'
2
+
1
3
  class Puppet::Resource::Ral < Puppet::Indirector::Code
4
+ include Puppet::Resource::Validator
2
5
 
3
6
  desc "Manipulate resources with the resource abstraction layer. Only used internally."
4
7
 
@@ -1,5 +1,8 @@
1
1
  require 'puppet/indirector/store_configs'
2
+ require 'puppet/indirector/resource/validator'
3
+
2
4
  class Puppet::Resource::StoreConfigs < Puppet::Indirector::StoreConfigs
5
+ include Puppet::Resource::Validator
3
6
 
4
7
  desc %q{Part of the "storeconfigs" feature. Should not be directly set by end users.}
5
8
 
@@ -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
@@ -177,6 +177,10 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
177
177
  request.do_request(self.class.srv_service, self.class.server, self.class.port) { |request| yield(request) }
178
178
  end
179
179
 
180
+ def validate_key(request)
181
+ # Validation happens on the remote end
182
+ end
183
+
180
184
  private
181
185
 
182
186
  def environment
@@ -8,4 +8,8 @@ class Puppet::Run::Local < Puppet::Indirector::Code
8
8
  def save( request )
9
9
  request.instance.run
10
10
  end
11
+
12
+ def validate_key(request)
13
+ # No key is necessary for kick
14
+ end
11
15
  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
 
@@ -142,4 +143,23 @@ class Puppet::Indirector::Terminus
142
143
  def terminus_type
143
144
  self.class.terminus_type
144
145
  end
146
+
147
+ def validate(request)
148
+ if request.instance
149
+ validate_model(request)
150
+ validate_key(request)
151
+ end
152
+ end
153
+
154
+ def validate_key(request)
155
+ unless request.key == request.instance.name
156
+ raise Puppet::Indirector::ValidationError, "Instance name #{request.instance.name.inspect} does not match requested key #{request.key.inspect}"
157
+ end
158
+ end
159
+
160
+ def validate_model(request)
161
+ unless model === request.instance
162
+ raise Puppet::Indirector::ValidationError, "Invalid instance type #{request.instance.class.inspect}, expected #{model.inspect}"
163
+ end
164
+ end
145
165
  end
@@ -15,7 +15,7 @@ module Puppet
15
15
  # to fileserver.conf
16
16
  { :acl => "/file" },
17
17
  { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true },
18
- { :acl => "/report", :method => :save, :authenticated => true },
18
+ { :acl => "~ ^\/report\/([^\/]+)$", :method => :save, :allow => '$1', :authenticated => true },
19
19
  # These allow `auth any`, because if you can do them anonymously you
20
20
  # should probably also be able to do them when trusted.
21
21
  { :acl => "/certificate/ca", :method => :find, :authenticated => :any },
@@ -3,12 +3,12 @@ require 'puppet/network/format_handler'
3
3
  Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do
4
4
  # Yaml doesn't need the class name; it's serialized.
5
5
  def intern(klass, text)
6
- YAML.load(text)
6
+ YAML.safely_load(text)
7
7
  end
8
8
 
9
9
  # Yaml doesn't need the class name; it's serialized.
10
10
  def intern_multiple(klass, text)
11
- YAML.load(text)
11
+ YAML.safely_load(text)
12
12
  end
13
13
 
14
14
  def render(instance)
@@ -72,7 +72,7 @@ Puppet::Network::FormatHandler.create_serialized_formats(:b64_zlib_yaml) do
72
72
 
73
73
  def decode(yaml)
74
74
  requiring_zlib do
75
- YAML.load(Zlib::Inflate.inflate(Base64.decode64(yaml)))
75
+ YAML.safely_load(Zlib::Inflate.inflate(Base64.decode64(yaml)))
76
76
  end
77
77
  end
78
78
  end
@@ -73,6 +73,8 @@ module Puppet::Network::HTTP::Handler
73
73
  raise
74
74
  rescue Exception => e
75
75
  return do_exception(response, e)
76
+ ensure
77
+ cleanup(request)
76
78
  end
77
79
 
78
80
  # Set the response up, with the body and status.
@@ -219,11 +221,14 @@ module Puppet::Network::HTTP::Handler
219
221
  raise NotImplementedError
220
222
  end
221
223
 
222
- # Retrieve the client certificate from the request if possible
223
224
  def client_cert(request)
224
225
  raise NotImplementedError
225
226
  end
226
227
 
228
+ def cleanup(request)
229
+ # By default, there is nothing to cleanup.
230
+ end
231
+
227
232
  def decode_params(params)
228
233
  params.inject({}) do |result, ary|
229
234
  param, value = ary
@@ -237,7 +242,7 @@ module Puppet::Network::HTTP::Handler
237
242
  next result if param == :ip
238
243
  value = CGI.unescape(value)
239
244
  if value =~ /^---/
240
- value = YAML.load(value)
245
+ value = YAML.safely_load(value)
241
246
  else
242
247
  value = true if value == "true"
243
248
  value = false if value == "false"
@@ -73,8 +73,6 @@ 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
@@ -89,6 +87,13 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler
89
87
  OpenSSL::X509::Certificate.new(cert)
90
88
  end
91
89
 
90
+ # Passenger freaks out if we finish handling the request without reading any
91
+ # part of the body, so make sure we have.
92
+ def cleanup(request)
93
+ request.body.read(1)
94
+ nil
95
+ end
96
+
92
97
  def extract_client_info(request)
93
98
  result = {}
94
99
  result[:ip] = request.ip
@@ -94,6 +94,7 @@ class Puppet::Network::HTTP::WEBrick
94
94
  results[:SSLCertificate] = host.certificate.content
95
95
  results[:SSLStartImmediately] = true
96
96
  results[:SSLEnable] = true
97
+ results[:SSLOptions] = OpenSSL::SSL::OP_NO_SSLv2
97
98
 
98
99
  raise Puppet::Error, "Could not find CA certificate" unless Puppet::SSL::Certificate.indirection.find(Puppet::SSL::CA_NAME)
99
100
 
@@ -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
@@ -22,7 +24,7 @@ class Puppet::Parser::TemplateWrapper
22
24
  # find which line in the template (if any) we were called from
23
25
  # but defer to when necessary since fetching the caller information on
24
26
  # every variable lookup can be quite time consuming
25
- Proc.new { (caller.find { |l| l =~ /#{file}:/ }||"")[/:(\d+):/,1] }
27
+ Proc.new { (caller.find { |l| l =~ /#{@__file__}:/ }||"")[/:(\d+):/,1] }
26
28
  end
27
29
 
28
30
  def script_line
@@ -63,51 +65,49 @@ class Puppet::Parser::TemplateWrapper
63
65
  # dead.
64
66
  def method_missing(name, *args)
65
67
  if scope.include?(name.to_s)
66
- return scope[name.to_s, {:file => file,:lineproc => script_line_proc}]
68
+ return scope[name.to_s, {:file => @__file__, :lineproc => script_line_proc}]
67
69
  else
68
70
  # Just throw an error immediately, instead of searching for
69
71
  # other missingmethod things or whatever.
70
- raise Puppet::ParseError.new("Could not find value for '#{name}'",@file,script_line)
72
+ raise Puppet::ParseError.new("Could not find value for '#{name}'", @__file__, script_line)
71
73
  end
72
74
  end
73
75
 
74
76
  def file=(filename)
75
- unless @file = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s)
77
+ unless @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s)
76
78
  raise Puppet::ParseError, "Could not find template '#{filename}'"
77
79
  end
78
80
 
79
81
  # We'll only ever not have a parser in testing, but, eh.
80
- scope.known_resource_types.watch_file(file)
81
-
82
- @string = File.read(file)
82
+ scope.known_resource_types.watch_file(@__file__)
83
83
  end
84
84
 
85
85
  def result(string = nil)
86
86
  if string
87
- self.string = string
88
87
  template_source = "inline template"
89
88
  else
90
- template_source = file
89
+ string = File.read(@__file__)
90
+ template_source = @__file__
91
91
  end
92
92
 
93
93
  # Expose all the variables in our scope as instance variables of the
94
94
  # current object, making it possible to access them without conflict
95
95
  # to the regular methods.
96
96
  benchmark(:debug, "Bound template variables for #{template_source}") do
97
- scope.to_hash.each { |name, value|
97
+ scope.to_hash.each do |name, value|
98
98
  if name.kind_of?(String)
99
99
  realname = name.gsub(/[^\w]/, "_")
100
100
  else
101
101
  realname = name
102
102
  end
103
103
  instance_variable_set("@#{realname}", value)
104
- }
104
+ end
105
105
  end
106
106
 
107
107
  result = nil
108
108
  benchmark(:debug, "Interpolated template #{template_source}") do
109
- template = ERB.new(self.string, 0, "-")
110
- template.filename = file
109
+ template = ERB.new(string, 0, "-")
110
+ template.filename = @__file__
111
111
  result = template.result(binding)
112
112
  end
113
113
 
@@ -115,6 +115,6 @@ class Puppet::Parser::TemplateWrapper
115
115
  end
116
116
 
117
117
  def to_s
118
- "template[#{(file ? file : "inline")}]"
118
+ "template[#{(@__file__ ? @__file__ : "inline")}]"
119
119
  end
120
120
  end
@@ -41,6 +41,21 @@ end
41
41
  end
42
42
  }
43
43
 
44
+ if defined?(YAML::ENGINE) and YAML::ENGINE.yamler == 'psych'
45
+ def Psych.safely_load(str)
46
+ result = Psych.parse(str)
47
+ if invalid_node = result.find { |node| node.tag =~ /!map:(.*)/ || node.tag =~ /!ruby\/hash:(.*)/ }
48
+ raise ArgumentError, "Illegal YAML mapping found with tag #{invalid_node.tag}; please use !ruby/object:#{$1} instead"
49
+ else
50
+ result.to_ruby
51
+ end
52
+ end
53
+ else
54
+ def YAML.safely_load(str)
55
+ self.load(str)
56
+ end
57
+ end
58
+
44
59
  def YAML.dump(*args)
45
60
  ZAML.dump(*args)
46
61
  end
@@ -356,3 +371,26 @@ unless Dir.respond_to?(:mktmpdir)
356
371
  end
357
372
  end
358
373
  end
374
+
375
+ # (#19151) Reject all SSLv2 ciphers and handshakes
376
+ require 'openssl'
377
+ class OpenSSL::SSL::SSLContext
378
+ if DEFAULT_PARAMS[:options]
379
+ DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv2
380
+ else
381
+ DEFAULT_PARAMS[:options] = OpenSSL::SSL::OP_NO_SSLv2
382
+ end
383
+ DEFAULT_PARAMS[:ciphers] << ':!SSLv2'
384
+
385
+ alias __original_initialize initialize
386
+ private :__original_initialize
387
+
388
+ def initialize(*args)
389
+ __original_initialize(*args)
390
+ params = {
391
+ :options => DEFAULT_PARAMS[:options],
392
+ :ciphers => DEFAULT_PARAMS[:ciphers],
393
+ }
394
+ set_params(params)
395
+ end
396
+ end
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
  module Puppet
10
- PUPPETVERSION = '3.1.0'
10
+ PUPPETVERSION = '3.1.1'
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and