puppet 2.7.5 → 2.7.6
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 +121 -0
- data/conf/redhat/puppet.spec +16 -7
- data/lib/puppet.rb +1 -1
- data/lib/puppet/application/cert.rb +17 -3
- data/lib/puppet/application/device.rb +1 -0
- data/lib/puppet/application/kick.rb +0 -2
- data/lib/puppet/application/resource.rb +73 -66
- data/lib/puppet/configurer/plugin_handler.rb +6 -2
- data/lib/puppet/defaults.rb +60 -5
- data/lib/puppet/face/ca.rb +11 -2
- data/lib/puppet/face/certificate.rb +33 -4
- data/lib/puppet/file_serving/fileset.rb +1 -1
- data/lib/puppet/file_serving/indirection_hooks.rb +2 -2
- data/lib/puppet/file_serving/metadata.rb +43 -4
- data/lib/puppet/indirector.rb +0 -1
- data/lib/puppet/indirector/request.rb +3 -4
- data/lib/puppet/indirector/resource/active_record.rb +3 -10
- data/lib/puppet/indirector/resource/ral.rb +2 -2
- data/lib/puppet/indirector/rest.rb +1 -1
- data/lib/puppet/network/handler/ca.rb +16 -106
- data/lib/puppet/network/handler/master.rb +0 -3
- data/lib/puppet/network/handler/runner.rb +1 -0
- data/lib/puppet/parser/scope.rb +10 -0
- data/lib/puppet/provider/file/posix.rb +72 -34
- data/lib/puppet/provider/file/windows.rb +100 -0
- data/lib/puppet/provider/group/windows_adsi.rb +2 -2
- data/lib/puppet/provider/user/windows_adsi.rb +19 -4
- data/lib/puppet/resource.rb +16 -0
- data/lib/puppet/resource/catalog.rb +1 -1
- data/lib/puppet/ssl/certificate.rb +2 -2
- data/lib/puppet/ssl/certificate_authority.rb +86 -10
- data/lib/puppet/ssl/certificate_authority/interface.rb +64 -19
- data/lib/puppet/ssl/certificate_factory.rb +112 -91
- data/lib/puppet/ssl/certificate_request.rb +88 -1
- data/lib/puppet/ssl/host.rb +20 -3
- data/lib/puppet/type/file.rb +15 -34
- data/lib/puppet/type/file/group.rb +11 -91
- data/lib/puppet/type/file/mode.rb +11 -41
- data/lib/puppet/type/file/owner.rb +18 -34
- data/lib/puppet/type/file/source.rb +22 -7
- data/lib/puppet/type/group.rb +4 -3
- data/lib/puppet/type/user.rb +4 -1
- data/lib/puppet/util.rb +59 -6
- data/lib/puppet/util/adsi.rb +11 -0
- data/lib/puppet/util/log.rb +4 -0
- data/lib/puppet/util/log/destinations.rb +7 -1
- data/lib/puppet/util/monkey_patches.rb +19 -0
- data/lib/puppet/util/network_device/config.rb +4 -5
- data/lib/puppet/util/settings.rb +5 -0
- data/lib/puppet/util/suidmanager.rb +0 -1
- data/lib/puppet/util/windows.rb +4 -0
- data/lib/puppet/util/windows/error.rb +16 -0
- data/lib/puppet/util/windows/security.rb +593 -0
- data/spec/integration/defaults_spec.rb +27 -0
- data/spec/integration/network/handler_spec.rb +1 -1
- data/spec/integration/type/file_spec.rb +382 -145
- data/spec/integration/util/windows/security_spec.rb +468 -0
- data/spec/shared_behaviours/file_serving.rb +4 -3
- data/spec/unit/application/agent_spec.rb +1 -0
- data/spec/unit/application/device_spec.rb +5 -0
- data/spec/unit/application/resource_spec.rb +62 -101
- data/spec/unit/configurer/downloader_spec.rb +2 -2
- data/spec/unit/configurer/plugin_handler_spec.rb +15 -8
- data/spec/unit/configurer_spec.rb +2 -2
- data/spec/unit/face/ca_spec.rb +34 -0
- data/spec/unit/face/certificate_spec.rb +168 -1
- data/spec/unit/file_serving/fileset_spec.rb +1 -1
- data/spec/unit/file_serving/indirection_hooks_spec.rb +1 -1
- data/spec/unit/file_serving/metadata_spec.rb +151 -107
- data/spec/unit/indirector/certificate_request/ca_spec.rb +0 -3
- data/spec/unit/indirector/direct_file_server_spec.rb +10 -9
- data/spec/unit/indirector/file_metadata/file_spec.rb +6 -4
- data/spec/unit/indirector/request_spec.rb +13 -3
- data/spec/unit/indirector/resource/active_record_spec.rb +4 -10
- data/spec/unit/indirector/resource/ral_spec.rb +6 -4
- data/spec/unit/indirector/rest_spec.rb +5 -6
- data/spec/unit/network/handler/ca_spec.rb +86 -0
- data/spec/unit/parser/collector_spec.rb +7 -7
- data/spec/unit/parser/scope_spec.rb +20 -0
- data/spec/unit/provider/file/posix_spec.rb +226 -0
- data/spec/unit/provider/file/windows_spec.rb +136 -0
- data/spec/unit/provider/group/windows_adsi_spec.rb +7 -2
- data/spec/unit/provider/user/windows_adsi_spec.rb +36 -3
- data/spec/unit/resource/catalog_spec.rb +20 -10
- data/spec/unit/resource_spec.rb +55 -8
- data/spec/unit/ssl/certificate_authority/interface_spec.rb +97 -54
- data/spec/unit/ssl/certificate_authority_spec.rb +133 -23
- data/spec/unit/ssl/certificate_factory_spec.rb +90 -70
- data/spec/unit/ssl/certificate_request_spec.rb +62 -1
- data/spec/unit/ssl/certificate_spec.rb +20 -14
- data/spec/unit/ssl/host_spec.rb +52 -6
- data/spec/unit/type/file/content_spec.rb +4 -4
- data/spec/unit/type/file/group_spec.rb +34 -96
- data/spec/unit/type/file/mode_spec.rb +88 -0
- data/spec/unit/type/file/owner_spec.rb +32 -123
- data/spec/unit/type/file/source_spec.rb +120 -41
- data/spec/unit/type/file_spec.rb +1033 -753
- data/spec/unit/type_spec.rb +19 -1
- data/spec/unit/util/adsi_spec.rb +19 -0
- data/spec/unit/util/log/destinations_spec.rb +75 -0
- data/spec/unit/util/log_spec.rb +15 -0
- data/spec/unit/util/network_device/config_spec.rb +7 -0
- data/spec/unit/util/settings_spec.rb +10 -0
- data/spec/unit/util_spec.rb +126 -13
- data/test/language/functions.rb +0 -1
- data/test/language/snippets.rb +0 -9
- data/test/lib/puppettest/exetest.rb +1 -1
- data/test/lib/puppettest/servertest.rb +0 -1
- data/test/rails/rails.rb +0 -1
- data/test/ral/type/filesources.rb +0 -60
- metadata +13 -33
- data/lib/puppet/network/client.rb +0 -174
- data/lib/puppet/network/client/ca.rb +0 -56
- data/lib/puppet/network/client/file.rb +0 -6
- data/lib/puppet/network/client/proxy.rb +0 -27
- data/lib/puppet/network/client/report.rb +0 -26
- data/lib/puppet/network/client/runner.rb +0 -10
- data/lib/puppet/network/client/status.rb +0 -4
- data/lib/puppet/network/http_server.rb +0 -3
- data/lib/puppet/network/http_server/mongrel.rb +0 -130
- data/lib/puppet/network/http_server/webrick.rb +0 -155
- data/lib/puppet/network/xmlrpc/client.rb +0 -211
- data/lib/puppet/provider/file/win32.rb +0 -72
- data/lib/puppet/sslcertificates.rb +0 -146
- data/lib/puppet/sslcertificates/ca.rb +0 -375
- data/lib/puppet/sslcertificates/certificate.rb +0 -255
- data/lib/puppet/sslcertificates/inventory.rb +0 -38
- data/lib/puppet/sslcertificates/support.rb +0 -146
- data/spec/integration/network/client_spec.rb +0 -18
- data/spec/unit/network/xmlrpc/client_spec.rb +0 -172
- data/spec/unit/sslcertificates/ca_spec.rb +0 -106
- data/test/certmgr/certmgr.rb +0 -308
- data/test/certmgr/inventory.rb +0 -69
- data/test/certmgr/support.rb +0 -105
- data/test/network/client/ca.rb +0 -69
- data/test/network/client/dipper.rb +0 -34
- data/test/network/handler/ca.rb +0 -273
- data/test/network/server/mongrel_test.rb +0 -99
- data/test/network/server/webrick.rb +0 -111
- data/test/network/xmlrpc/client.rb +0 -45
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'puppet'
|
3
|
-
require 'puppet/sslcertificates'
|
4
3
|
require 'xmlrpc/server'
|
5
4
|
require 'yaml'
|
6
5
|
|
@@ -33,8 +32,6 @@ class Puppet::Network::Handler
|
|
33
32
|
|
34
33
|
args[:Local] = true
|
35
34
|
|
36
|
-
@ca = (hash.include?(:CA) and hash[:CA]) ? Puppet::SSLCertificates::CA.new : nil
|
37
|
-
|
38
35
|
# This is only used by the cfengine module, or if --loadclasses was
|
39
36
|
# specified in +puppet+.
|
40
37
|
args[:Classes] = hash[:Classes] if hash.include?(:Classes)
|
data/lib/puppet/parser/scope.rb
CHANGED
@@ -418,6 +418,16 @@ class Puppet::Parser::Scope
|
|
418
418
|
environment.known_resource_types.find_definition(namespaces, type.to_s.downcase)
|
419
419
|
end
|
420
420
|
|
421
|
+
def method_missing(method, *args, &block)
|
422
|
+
method.to_s =~ /^function_(.*)$/
|
423
|
+
super unless $1
|
424
|
+
super unless Puppet::Parser::Functions.function($1)
|
425
|
+
|
426
|
+
# Calling .function(name) adds "function_#{name}" as a callable method on
|
427
|
+
# self if it's found, so now we can just send it
|
428
|
+
send(method, *args)
|
429
|
+
end
|
430
|
+
|
421
431
|
def resolve_type_and_titles(type, titles)
|
422
432
|
raise ArgumentError, "titles must be an array" unless titles.is_a?(Array)
|
423
433
|
|
@@ -8,15 +8,13 @@ Puppet::Type.type(:file).provide :posix do
|
|
8
8
|
|
9
9
|
require 'etc'
|
10
10
|
|
11
|
-
def
|
12
|
-
return id.to_s if id.is_a?(Symbol)
|
11
|
+
def uid2name(id)
|
12
|
+
return id.to_s if id.is_a?(Symbol) or id.is_a?(String)
|
13
13
|
return nil if id > Puppet[:maximum_uid].to_i
|
14
14
|
|
15
15
|
begin
|
16
16
|
user = Etc.getpwuid(id)
|
17
|
-
rescue TypeError
|
18
|
-
return nil
|
19
|
-
rescue ArgumentError
|
17
|
+
rescue TypeError, ArgumentError
|
20
18
|
return nil
|
21
19
|
end
|
22
20
|
|
@@ -27,33 +25,33 @@ Puppet::Type.type(:file).provide :posix do
|
|
27
25
|
end
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
elsif value.is_a?(String)
|
35
|
-
fail "Could not find user #{value}" unless uid = uid(value)
|
36
|
-
else
|
37
|
-
uid = value
|
38
|
-
end
|
39
|
-
|
40
|
-
return true if uid == current
|
41
|
-
end
|
28
|
+
# Determine if the user is valid, and if so, return the UID
|
29
|
+
def name2uid(value)
|
30
|
+
Integer(value) rescue uid(value) || false
|
31
|
+
end
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
33
|
+
def gid2name(id)
|
34
|
+
return id.to_s if id.is_a?(Symbol) or id.is_a?(String)
|
35
|
+
return nil if id > Puppet[:maximum_uid].to_i
|
36
|
+
|
37
|
+
begin
|
38
|
+
group = Etc.getgrgid(id)
|
39
|
+
rescue TypeError, ArgumentError
|
40
|
+
return nil
|
46
41
|
end
|
47
42
|
|
48
|
-
|
43
|
+
if group.gid == ""
|
44
|
+
return nil
|
45
|
+
else
|
46
|
+
return group.name
|
47
|
+
end
|
49
48
|
end
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
Integer(value) rescue uid(value) || false
|
50
|
+
def name2gid(value)
|
51
|
+
Integer(value) rescue gid(value) || false
|
54
52
|
end
|
55
53
|
|
56
|
-
def
|
54
|
+
def owner
|
57
55
|
unless stat = resource.stat
|
58
56
|
return :absent
|
59
57
|
end
|
@@ -71,27 +69,67 @@ Puppet::Type.type(:file).provide :posix do
|
|
71
69
|
currentvalue
|
72
70
|
end
|
73
71
|
|
74
|
-
def
|
72
|
+
def owner=(should)
|
75
73
|
# Set our method appropriately, depending on links.
|
76
|
-
if links == :manage
|
74
|
+
if resource[:links] == :manage
|
77
75
|
method = :lchown
|
78
76
|
else
|
79
77
|
method = :chown
|
80
78
|
end
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
begin
|
81
|
+
File.send(method, should, nil, resource[:path])
|
82
|
+
rescue => detail
|
83
|
+
raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def group
|
88
|
+
return :absent unless stat = resource.stat
|
89
|
+
|
90
|
+
currentvalue = stat.gid
|
91
|
+
|
92
|
+
# On OS X, files that are owned by -2 get returned as really
|
93
|
+
# large GIDs instead of negative ones. This isn't a Ruby bug,
|
94
|
+
# it's an OS X bug, since it shows up in perl, too.
|
95
|
+
if currentvalue > Puppet[:maximum_uid].to_i
|
96
|
+
self.warning "Apparently using negative GID (#{currentvalue}) on a platform that does not consistently handle them"
|
97
|
+
currentvalue = :silly
|
85
98
|
end
|
86
99
|
|
87
|
-
|
100
|
+
currentvalue
|
101
|
+
end
|
102
|
+
|
103
|
+
def group=(should)
|
104
|
+
# Set our method appropriately, depending on links.
|
105
|
+
if resource[:links] == :manage
|
106
|
+
method = :lchown
|
107
|
+
else
|
108
|
+
method = :chown
|
109
|
+
end
|
88
110
|
|
89
111
|
begin
|
90
|
-
File.send(method,
|
112
|
+
File.send(method, nil, should, resource[:path])
|
91
113
|
rescue => detail
|
92
|
-
raise Puppet::Error, "Failed to set
|
114
|
+
raise Puppet::Error, "Failed to set group to '#{should}': #{detail}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def mode
|
119
|
+
if stat = resource.stat
|
120
|
+
return (stat.mode & 007777).to_s(8)
|
121
|
+
else
|
122
|
+
return :absent
|
93
123
|
end
|
124
|
+
end
|
94
125
|
|
95
|
-
|
126
|
+
def mode=(value)
|
127
|
+
begin
|
128
|
+
File.chmod(value.to_i(8), resource[:path])
|
129
|
+
rescue => detail
|
130
|
+
error = Puppet::Error.new("failed to set mode #{mode} on #{resource[:path]}: #{detail.message}")
|
131
|
+
error.set_backtrace detail.backtrace
|
132
|
+
raise error
|
133
|
+
end
|
96
134
|
end
|
97
135
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
Puppet::Type.type(:file).provide :windows do
|
2
|
+
desc "Uses Microsoft Windows functionality to manage file's users and rights."
|
3
|
+
|
4
|
+
confine :feature => :microsoft_windows
|
5
|
+
|
6
|
+
include Puppet::Util::Warnings
|
7
|
+
|
8
|
+
if Puppet.features.microsoft_windows?
|
9
|
+
require 'puppet/util/windows'
|
10
|
+
require 'puppet/util/adsi'
|
11
|
+
include Puppet::Util::Windows::Security
|
12
|
+
end
|
13
|
+
|
14
|
+
ERROR_INVALID_SID_STRUCTURE = 1337
|
15
|
+
|
16
|
+
def id2name(id)
|
17
|
+
# If it's a valid sid, get the name. Otherwise, it's already a name, so
|
18
|
+
# just return it.
|
19
|
+
begin
|
20
|
+
if string_to_sid_ptr(id)
|
21
|
+
name = nil
|
22
|
+
Puppet::Util::ADSI.execquery(
|
23
|
+
"SELECT Name FROM Win32_Account WHERE SID = '#{id}'
|
24
|
+
AND LocalAccount = true"
|
25
|
+
).each { |a| name ||= a.name }
|
26
|
+
return name
|
27
|
+
end
|
28
|
+
rescue Puppet::Util::Windows::Error => e
|
29
|
+
raise unless e.code == ERROR_INVALID_SID_STRUCTURE
|
30
|
+
end
|
31
|
+
|
32
|
+
id
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determine if the account is valid, and if so, return the UID
|
36
|
+
def name2id(value)
|
37
|
+
# If it's a valid sid, then return it. Else, it's a name we need to convert
|
38
|
+
# to sid.
|
39
|
+
begin
|
40
|
+
return value if string_to_sid_ptr(value)
|
41
|
+
rescue Puppet::Util::Windows::Error => e
|
42
|
+
raise unless e.code == ERROR_INVALID_SID_STRUCTURE
|
43
|
+
end
|
44
|
+
|
45
|
+
Puppet::Util::ADSI.sid_for_account(value) rescue nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# We use users and groups interchangeably, so use the same methods for both
|
49
|
+
# (the type expects different methods, so we have to oblige).
|
50
|
+
alias :uid2name :id2name
|
51
|
+
alias :gid2name :id2name
|
52
|
+
|
53
|
+
alias :name2gid :name2id
|
54
|
+
alias :name2uid :name2id
|
55
|
+
|
56
|
+
def owner
|
57
|
+
return :absent unless resource.exist?
|
58
|
+
get_owner(resource[:path])
|
59
|
+
end
|
60
|
+
|
61
|
+
def owner=(should)
|
62
|
+
begin
|
63
|
+
set_owner(should, resource[:path])
|
64
|
+
rescue => detail
|
65
|
+
raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def group
|
70
|
+
return :absent unless resource.exist?
|
71
|
+
get_group(resource[:path])
|
72
|
+
end
|
73
|
+
|
74
|
+
def group=(should)
|
75
|
+
begin
|
76
|
+
set_group(should, resource[:path])
|
77
|
+
rescue => detail
|
78
|
+
raise Puppet::Error, "Failed to set group to '#{should}': #{detail}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def mode
|
83
|
+
if resource.exist?
|
84
|
+
get_mode(resource[:path]).to_s(8)
|
85
|
+
else
|
86
|
+
:absent
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def mode=(value)
|
91
|
+
begin
|
92
|
+
set_mode(value.to_i(8), resource[:path])
|
93
|
+
rescue => detail
|
94
|
+
error = Puppet::Error.new("failed to set mode #{mode} on #{resource[:path]}: #{detail.message}")
|
95
|
+
error.set_backtrace detail.backtrace
|
96
|
+
raise error
|
97
|
+
end
|
98
|
+
:file_changed
|
99
|
+
end
|
100
|
+
end
|
@@ -35,11 +35,11 @@ Puppet::Type.type(:group).provide :windows_adsi do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def gid
|
38
|
-
|
38
|
+
Puppet::Util::ADSI.sid_for_account(@resource[:name])
|
39
39
|
end
|
40
40
|
|
41
41
|
def gid=(value)
|
42
|
-
|
42
|
+
fail "gid is read-only"
|
43
43
|
end
|
44
44
|
|
45
45
|
def self.instances
|
@@ -7,7 +7,7 @@ Puppet::Type.type(:user).provide :windows_adsi do
|
|
7
7
|
confine :operatingsystem => :windows
|
8
8
|
confine :feature => :microsoft_windows
|
9
9
|
|
10
|
-
has_features :manages_homedir
|
10
|
+
has_features :manages_homedir, :manages_passwords
|
11
11
|
|
12
12
|
def user
|
13
13
|
@user ||= Puppet::Util::ADSI::User.new(@resource[:name])
|
@@ -57,11 +57,26 @@ Puppet::Type.type(:user).provide :windows_adsi do
|
|
57
57
|
user['HomeDirectory'] = value
|
58
58
|
end
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
def password
|
61
|
+
user.password_is?( @resource[:password] ) ? @resource[:password] : :absent
|
62
|
+
end
|
62
63
|
|
64
|
+
def password=(value)
|
65
|
+
user.password = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def uid
|
69
|
+
Puppet::Util::ADSI.sid_for_account(@resource[:name])
|
70
|
+
end
|
71
|
+
|
72
|
+
def uid=(value)
|
73
|
+
fail "uid is read-only"
|
74
|
+
end
|
75
|
+
|
76
|
+
[:gid, :shell].each do |prop|
|
77
|
+
define_method(prop) { nil }
|
63
78
|
define_method("#{prop}=") do |v|
|
64
|
-
|
79
|
+
fail "No support for managing property #{prop} of user #{@resource[:name]} on Windows"
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
data/lib/puppet/resource.rb
CHANGED
@@ -355,6 +355,22 @@ class Puppet::Resource
|
|
355
355
|
raise ArgumentError, "Invalid parameter #{name}" unless valid_parameter?(name)
|
356
356
|
end
|
357
357
|
|
358
|
+
def prune_parameters(options = {})
|
359
|
+
properties = resource_type.properties.map(&:name)
|
360
|
+
|
361
|
+
dup.collect do |attribute, value|
|
362
|
+
if value.to_s.empty? or Array(value).empty?
|
363
|
+
delete(attribute)
|
364
|
+
elsif value.to_s == "absent" and attribute.to_s != "ensure"
|
365
|
+
delete(attribute)
|
366
|
+
end
|
367
|
+
|
368
|
+
parameters_to_include = options[:parameters_to_include] || []
|
369
|
+
delete(attribute) unless properties.include?(attribute) || parameters_to_include.include?(attribute)
|
370
|
+
end
|
371
|
+
self
|
372
|
+
end
|
373
|
+
|
358
374
|
private
|
359
375
|
|
360
376
|
# Produce a canonical method name.
|
@@ -546,7 +546,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
|
|
546
546
|
def write_resource_file
|
547
547
|
::File.open(Puppet[:resourcefile], "w") do |f|
|
548
548
|
to_print = resources.map do |resource|
|
549
|
-
next
|
549
|
+
next unless resource.managed?
|
550
550
|
"#{resource.type}[#{resource[resource.name_var]}]"
|
551
551
|
end.compact
|
552
552
|
f.puts to_print.join("\n")
|
@@ -27,10 +27,10 @@ class Puppet::SSL::Certificate < Puppet::SSL::Base
|
|
27
27
|
[:s]
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def subject_alt_names
|
31
31
|
alts = content.extensions.find{|ext| ext.oid == "subjectAltName"}
|
32
32
|
return [] unless alts
|
33
|
-
alts.value.split(
|
33
|
+
alts.value.split(/\s*,\s*/)
|
34
34
|
end
|
35
35
|
|
36
36
|
def expiration
|
@@ -11,6 +11,15 @@ require 'puppet/ssl/certificate_request'
|
|
11
11
|
# it can also be seen as a general interface into all of the
|
12
12
|
# SSL stuff.
|
13
13
|
class Puppet::SSL::CertificateAuthority
|
14
|
+
# We will only sign extensions on this whitelist, ever. Any CSR with a
|
15
|
+
# requested extension that we don't recognize is rejected, against the risk
|
16
|
+
# that it will introduce some security issue through our ignorance of it.
|
17
|
+
#
|
18
|
+
# Adding an extension to this whitelist simply means we will consider it
|
19
|
+
# further, not that we will always accept a certificate with an extension
|
20
|
+
# requested on this list.
|
21
|
+
RequestExtensionWhitelist = %w{subjectAltName}
|
22
|
+
|
14
23
|
require 'puppet/ssl/certificate_factory'
|
15
24
|
require 'puppet/ssl/inventory'
|
16
25
|
require 'puppet/ssl/certificate_revocation_list'
|
@@ -33,6 +42,14 @@ class Puppet::SSL::CertificateAuthority
|
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
45
|
+
class CertificateSigningError < RuntimeError
|
46
|
+
attr_accessor :host
|
47
|
+
|
48
|
+
def initialize(host)
|
49
|
+
@host = host
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
36
53
|
def self.ca?
|
37
54
|
return false unless Puppet[:ca]
|
38
55
|
return false unless Puppet.run_mode.master?
|
@@ -54,7 +71,6 @@ class Puppet::SSL::CertificateAuthority
|
|
54
71
|
def apply(method, options)
|
55
72
|
raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
|
56
73
|
applier = Interface.new(method, options)
|
57
|
-
|
58
74
|
applier.apply(self)
|
59
75
|
end
|
60
76
|
|
@@ -110,13 +126,16 @@ class Puppet::SSL::CertificateAuthority
|
|
110
126
|
end
|
111
127
|
|
112
128
|
# Generate a new certificate.
|
113
|
-
def generate(name)
|
129
|
+
def generate(name, options = {})
|
114
130
|
raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.indirection.find(name)
|
115
131
|
host = Puppet::SSL::Host.new(name)
|
116
132
|
|
117
|
-
|
133
|
+
# Pass on any requested subjectAltName field.
|
134
|
+
san = options[:dns_alt_names]
|
118
135
|
|
119
|
-
|
136
|
+
host = Puppet::SSL::Host.new(name)
|
137
|
+
host.generate_certificate_request(:dns_alt_names => san)
|
138
|
+
sign(name, !!san)
|
120
139
|
end
|
121
140
|
|
122
141
|
# Generate our CA certificate.
|
@@ -125,14 +144,16 @@ class Puppet::SSL::CertificateAuthority
|
|
125
144
|
|
126
145
|
host.generate_key unless host.key
|
127
146
|
|
128
|
-
# Create a new cert request. We do this
|
129
|
-
#
|
130
|
-
# save the request anywhere.
|
147
|
+
# Create a new cert request. We do this specially, because we don't want
|
148
|
+
# to actually save the request anywhere.
|
131
149
|
request = Puppet::SSL::CertificateRequest.new(host.name)
|
150
|
+
|
151
|
+
# We deliberately do not put any subjectAltName in here: the CA
|
152
|
+
# certificate absolutely does not need them. --daniel 2011-10-13
|
132
153
|
request.generate(host.key)
|
133
154
|
|
134
155
|
# Create a self-signed certificate.
|
135
|
-
@certificate = sign(host.name,
|
156
|
+
@certificate = sign(host.name, false, request)
|
136
157
|
|
137
158
|
# And make sure we initialize our CRL.
|
138
159
|
crl
|
@@ -225,20 +246,34 @@ class Puppet::SSL::CertificateAuthority
|
|
225
246
|
end
|
226
247
|
|
227
248
|
# Sign a given certificate request.
|
228
|
-
def sign(hostname,
|
249
|
+
def sign(hostname, allow_dns_alt_names = false, self_signing_csr = nil)
|
229
250
|
# This is a self-signed certificate
|
230
251
|
if self_signing_csr
|
252
|
+
# # This is a self-signed certificate, which is for the CA. Since this
|
253
|
+
# # forces the certificate to be self-signed, anyone who manages to trick
|
254
|
+
# # the system into going through this path gets a certificate they could
|
255
|
+
# # generate anyway. There should be no security risk from that.
|
231
256
|
csr = self_signing_csr
|
257
|
+
cert_type = :ca
|
232
258
|
issuer = csr.content
|
233
259
|
else
|
260
|
+
allow_dns_alt_names = true if hostname == Puppet[:certname].downcase
|
234
261
|
unless csr = Puppet::SSL::CertificateRequest.indirection.find(hostname)
|
235
262
|
raise ArgumentError, "Could not find certificate request for #{hostname}"
|
236
263
|
end
|
264
|
+
|
265
|
+
cert_type = :server
|
237
266
|
issuer = host.certificate.content
|
267
|
+
|
268
|
+
# Make sure that the CSR conforms to our internal signing policies.
|
269
|
+
# This will raise if the CSR doesn't conform, but just in case...
|
270
|
+
check_internal_signing_policies(hostname, csr, allow_dns_alt_names) or
|
271
|
+
raise CertificateSigningError.new(hostname), "CSR had an unknown failure checking internal signing policies, will not sign!"
|
238
272
|
end
|
239
273
|
|
240
274
|
cert = Puppet::SSL::Certificate.new(hostname)
|
241
|
-
cert.content = Puppet::SSL::CertificateFactory.
|
275
|
+
cert.content = Puppet::SSL::CertificateFactory.
|
276
|
+
build(cert_type, csr, issuer, next_serial)
|
242
277
|
cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new)
|
243
278
|
|
244
279
|
Puppet.notice "Signed certificate request for #{hostname}"
|
@@ -258,6 +293,47 @@ class Puppet::SSL::CertificateAuthority
|
|
258
293
|
cert
|
259
294
|
end
|
260
295
|
|
296
|
+
def check_internal_signing_policies(hostname, csr, allow_dns_alt_names)
|
297
|
+
# Reject unknown request extensions.
|
298
|
+
unknown_req = csr.request_extensions.
|
299
|
+
reject {|x| RequestExtensionWhitelist.include? x["oid"] }
|
300
|
+
|
301
|
+
if unknown_req and not unknown_req.empty?
|
302
|
+
names = unknown_req.map {|x| x["oid"] }.sort.uniq.join(", ")
|
303
|
+
raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
|
304
|
+
end
|
305
|
+
|
306
|
+
# Wildcards: we don't allow 'em at any point.
|
307
|
+
#
|
308
|
+
# The stringification here makes the content visible, and saves us having
|
309
|
+
# to scrobble through the content of the CSR subject field to make sure it
|
310
|
+
# is what we expect where we expect it.
|
311
|
+
if csr.content.subject.to_s.include? '*'
|
312
|
+
raise CertificateSigningError.new(hostname), "CSR subject contains a wildcard, which is not allowed: #{csr.content.subject.to_s}"
|
313
|
+
end
|
314
|
+
|
315
|
+
unless csr.subject_alt_names.empty?
|
316
|
+
# If you alt names are allowed, they are required. Otherwise they are
|
317
|
+
# disallowed. Self-signed certs are implicitly trusted, however.
|
318
|
+
unless allow_dns_alt_names
|
319
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains subject alternative names (#{csr.subject_alt_names.join(', ')}), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{csr.name}` to sign this request."
|
320
|
+
end
|
321
|
+
|
322
|
+
# If subjectAltNames are present, validate that they are only for DNS
|
323
|
+
# labels, not any other kind.
|
324
|
+
unless csr.subject_alt_names.all? {|x| x =~ /^DNS:/ }
|
325
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains a subjectAltName outside the DNS label space: #{csr.subject_alt_names.join(', ')}. To continue, this CSR needs to be cleaned."
|
326
|
+
end
|
327
|
+
|
328
|
+
# Check for wildcards in the subjectAltName fields too.
|
329
|
+
if csr.subject_alt_names.any? {|x| x.include? '*' }
|
330
|
+
raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' subjectAltName contains a wildcard, which is not allowed: #{csr.subject_alt_names.join(', ')} To continue, this CSR needs to be cleaned."
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
return true # good enough for us!
|
335
|
+
end
|
336
|
+
|
261
337
|
# Verify a given host's certificate.
|
262
338
|
def verify(name)
|
263
339
|
unless cert = Puppet::SSL::Certificate.indirection.find(name)
|