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.
- data/CHANGELOG +25 -0
- data/conf/auth.conf +3 -3
- data/conf/redhat/puppet.spec +5 -2
- data/lib/puppet.rb +1 -1
- data/lib/puppet/indirector/catalog/compiler.rb +13 -2
- data/lib/puppet/indirector/errors.rb +5 -0
- data/lib/puppet/indirector/file_bucket_file/file.rb +4 -0
- data/lib/puppet/indirector/file_bucket_file/selector.rb +4 -0
- data/lib/puppet/indirector/indirection.rb +1 -0
- data/lib/puppet/indirector/request.rb +4 -0
- data/lib/puppet/indirector/resource/ral.rb +4 -0
- data/lib/puppet/indirector/resource/validator.rb +8 -0
- data/lib/puppet/indirector/rest.rb +4 -0
- data/lib/puppet/indirector/run/local.rb +4 -0
- data/lib/puppet/indirector/terminus.rb +20 -0
- data/lib/puppet/network/http/handler.rb +9 -0
- data/lib/puppet/network/http/rack/rest.rb +7 -2
- data/lib/puppet/network/http/webrick.rb +1 -0
- data/lib/puppet/network/http_pool.rb +17 -8
- data/lib/puppet/network/rest_authconfig.rb +1 -1
- data/lib/puppet/parser/templatewrapper.rb +13 -12
- data/lib/puppet/util/monkey_patches.rb +43 -0
- data/spec/integration/indirector/bucket_file/rest_spec.rb +1 -1
- data/spec/integration/indirector/catalog/compiler_spec.rb +1 -0
- data/spec/integration/indirector/catalog/queue_spec.rb +1 -1
- data/spec/integration/indirector/certificate_request/rest_spec.rb +1 -1
- data/spec/integration/indirector/report/rest_spec.rb +1 -1
- data/spec/integration/resource/catalog_spec.rb +1 -0
- data/spec/unit/indirector/catalog/compiler_spec.rb +30 -3
- data/spec/unit/indirector/indirection_spec.rb +18 -1
- data/spec/unit/indirector/request_spec.rb +22 -0
- data/spec/unit/indirector/terminus_spec.rb +186 -174
- data/spec/unit/network/http/handler_spec.rb +1 -0
- data/spec/unit/network/http/rack/rest_spec.rb +17 -0
- data/spec/unit/network/http/webrick_spec.rb +4 -0
- data/spec/unit/network/http_pool_spec.rb +93 -80
- data/spec/unit/network/rest_authconfig_spec.rb +16 -3
- data/spec/unit/parser/functions/inline_template_spec.rb +14 -1
- data/spec/unit/parser/functions/template_spec.rb +18 -1
- data/spec/unit/parser/templatewrapper_spec.rb +24 -9
- data/spec/unit/util/monkey_patches_spec.rb +11 -1
- data/test/language/snippets.rb +1 -1
- 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
|
data/conf/auth.conf
CHANGED
@@ -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 /
|
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
|
data/conf/redhat/puppet.spec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
%global confdir conf/redhat
|
6
6
|
|
7
7
|
Name: puppet
|
8
|
-
Version: 2.6.
|
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.
|
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
|
data/lib/puppet.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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,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
|
@@ -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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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 => "
|
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 @
|
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(
|
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
|
-
|
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
|
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(
|
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[#{(
|
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
|