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.
- data/conf/auth.conf +3 -3
- data/lib/puppet/indirector/catalog/compiler.rb +13 -2
- data/lib/puppet/indirector/certificate_status/file.rb +5 -0
- 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 +2 -1
- data/lib/puppet/indirector/resource/active_record.rb +2 -0
- data/lib/puppet/indirector/resource/ral.rb +3 -0
- data/lib/puppet/indirector/resource/store_configs.rb +3 -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/authconfig.rb +1 -1
- data/lib/puppet/network/formats.rb +3 -3
- data/lib/puppet/network/http/handler.rb +7 -2
- data/lib/puppet/network/http/rack/rest.rb +7 -2
- data/lib/puppet/network/http/webrick.rb +1 -0
- data/lib/puppet/parser/templatewrapper.rb +16 -16
- data/lib/puppet/util/monkey_patches.rb +38 -0
- data/lib/puppet/version.rb +1 -1
- data/spec/integration/file_serving/terminus_helper_spec.rb +1 -1
- data/spec/integration/indirector/catalog/queue_spec.rb +1 -1
- data/spec/integration/resource/catalog_spec.rb +1 -0
- data/spec/unit/indirector/catalog/compiler_spec.rb +29 -2
- data/spec/unit/indirector/indirection_spec.rb +17 -1
- data/spec/unit/indirector/terminus_spec.rb +45 -0
- data/spec/unit/network/authconfig_spec.rb +12 -0
- data/spec/unit/network/authorization_spec.rb +4 -0
- data/spec/unit/network/formats_spec.rb +6 -6
- data/spec/unit/network/http/connection_spec.rb +0 -1
- data/spec/unit/network/http/handler_spec.rb +25 -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/parser/functions/inline_template_spec.rb +15 -44
- data/spec/unit/parser/functions/template_spec.rb +43 -56
- data/spec/unit/parser/templatewrapper_spec.rb +68 -91
- data/spec/unit/ssl/certificate_request_spec.rb +2 -0
- data/spec/unit/ssl/host_spec.rb +1 -0
- data/spec/unit/util/monkey_patches_spec.rb +20 -0
- metadata +2190 -2188
data/conf/auth.conf
CHANGED
@@ -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 /
|
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
|
-
|
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
|
-
|
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
|
@@ -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,
|
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,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
|
@@ -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 => "
|
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.
|
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.
|
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.
|
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.
|
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 =~ /#{
|
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 =>
|
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}'"
|
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 @
|
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(
|
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
|
-
|
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
|
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(
|
110
|
-
template.filename =
|
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[#{(
|
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
|