puppet 0.13.6 → 0.16.0
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 +57 -0
- data/Rakefile +38 -410
- data/bin/puppet +14 -12
- data/bin/puppetca +1 -3
- data/bin/puppetd +25 -7
- data/bin/puppetdoc +161 -104
- data/bin/puppetmasterd +4 -4
- data/conf/epm.list +8 -0
- data/conf/redhat/client.init +6 -1
- data/conf/redhat/no-chuser-0.15.1.patch +38 -0
- data/conf/redhat/puppet.spec +20 -5
- data/conf/redhat/puppetd.conf +1 -1
- data/conf/redhat/puppetmasterd.conf +1 -1
- data/conf/redhat/server.init +2 -4
- data/examples/code/snippets/{casestatement → casestatement.pp} +12 -1
- data/examples/code/snippets/selectorvalues.pp +15 -0
- data/examples/code/snippets/singleselector.pp +22 -0
- data/examples/code/snippets/tag.pp +9 -0
- data/ext/module_puppet +1 -1
- data/install.rb +303 -303
- data/lib/puppet.rb +7 -9
- data/lib/puppet/client.rb +18 -5
- data/lib/puppet/client/dipper.rb +12 -10
- data/lib/puppet/client/master.rb +113 -41
- data/lib/puppet/client/pelement.rb +20 -0
- data/lib/puppet/config.rb +113 -6
- data/lib/puppet/element.rb +1 -3
- data/lib/puppet/event.rb +12 -23
- data/lib/puppet/filetype.rb +93 -5
- data/lib/puppet/inifile.rb +201 -0
- data/lib/puppet/log.rb +18 -6
- data/lib/puppet/parameter.rb +80 -29
- data/lib/puppet/parser/ast.rb +6 -4
- data/lib/puppet/parser/ast/caseopt.rb +13 -4
- data/lib/puppet/parser/ast/casestatement.rb +2 -2
- data/lib/puppet/parser/ast/component.rb +4 -14
- data/lib/puppet/parser/ast/hostclass.rb +1 -1
- data/lib/puppet/parser/ast/leaf.rb +12 -0
- data/lib/puppet/parser/ast/node.rb +4 -4
- data/lib/puppet/parser/ast/objectdef.rb +5 -51
- data/lib/puppet/parser/ast/selector.rb +2 -0
- data/lib/puppet/parser/ast/tag.rb +26 -0
- data/lib/puppet/parser/interpreter.rb +89 -74
- data/lib/puppet/parser/lexer.rb +4 -3
- data/lib/puppet/parser/parser.rb +440 -378
- data/lib/puppet/parser/scope.rb +844 -887
- data/lib/puppet/server.rb +12 -1
- data/lib/puppet/server/authconfig.rb +166 -0
- data/lib/puppet/server/authstore.rb +8 -6
- data/lib/puppet/server/ca.rb +23 -26
- data/lib/puppet/server/filebucket.rb +24 -23
- data/lib/puppet/server/fileserver.rb +116 -47
- data/lib/puppet/server/master.rb +58 -19
- data/lib/puppet/server/pelement.rb +176 -0
- data/lib/puppet/server/rights.rb +78 -0
- data/lib/puppet/server/servlet.rb +19 -6
- data/lib/puppet/sslcertificates.rb +4 -2
- data/lib/puppet/sslcertificates/ca.rb +66 -34
- data/lib/puppet/storage.rb +20 -26
- data/lib/puppet/transaction.rb +49 -92
- data/lib/puppet/type.rb +142 -35
- data/lib/puppet/type/cron.rb +29 -14
- data/lib/puppet/type/exec.rb +92 -35
- data/lib/puppet/type/group.rb +29 -11
- data/lib/puppet/type/nameservice.rb +50 -1
- data/lib/puppet/type/nameservice/netinfo.rb +68 -1
- data/lib/puppet/type/nameservice/objectadd.rb +1 -0
- data/lib/puppet/type/package.rb +150 -109
- data/lib/puppet/type/package/apple.rb +27 -0
- data/lib/puppet/type/package/apt.rb +1 -0
- data/lib/puppet/type/package/darwinport.rb +97 -0
- data/lib/puppet/type/package/dpkg.rb +10 -2
- data/lib/puppet/type/package/freebsd.rb +19 -0
- data/lib/puppet/type/package/{bsd.rb → openbsd.rb} +36 -7
- data/lib/puppet/type/package/ports.rb +98 -0
- data/lib/puppet/type/package/rpm.rb +43 -7
- data/lib/puppet/type/package/sun.rb +53 -36
- data/lib/puppet/type/package/yum.rb +5 -16
- data/lib/puppet/type/parsedtype.rb +41 -29
- data/lib/puppet/type/parsedtype/host.rb +13 -5
- data/lib/puppet/type/parsedtype/mount.rb +250 -0
- data/lib/puppet/type/parsedtype/port.rb +8 -6
- data/lib/puppet/type/pfile.rb +284 -30
- data/lib/puppet/type/pfile/checksum.rb +96 -68
- data/lib/puppet/type/pfile/content.rb +16 -13
- data/lib/puppet/type/pfile/ensure.rb +64 -126
- data/lib/puppet/type/pfile/group.rb +12 -5
- data/lib/puppet/type/pfile/mode.rb +16 -4
- data/lib/puppet/type/pfile/source.rb +47 -73
- data/lib/puppet/type/pfile/target.rb +81 -0
- data/lib/puppet/type/pfile/uid.rb +10 -3
- data/lib/puppet/type/pfilebucket.rb +12 -3
- data/lib/puppet/type/schedule.rb +5 -1
- data/lib/puppet/type/service.rb +138 -66
- data/lib/puppet/type/service/debian.rb +9 -3
- data/lib/puppet/type/service/init.rb +51 -56
- data/lib/puppet/type/service/smf.rb +16 -6
- data/lib/puppet/type/state.rb +71 -32
- data/lib/puppet/type/symlink.rb +12 -7
- data/lib/puppet/type/tidy.rb +5 -1
- data/lib/puppet/type/user.rb +116 -20
- data/lib/puppet/type/yumrepo.rb +314 -0
- data/lib/puppet/util.rb +84 -14
- data/test/client/client.rb +41 -18
- data/test/client/master.rb +50 -4
- data/test/executables/puppetbin.rb +31 -4
- data/test/executables/puppetca.rb +18 -2
- data/test/language/ast.rb +201 -31
- data/test/language/interpreter.rb +8 -2
- data/test/{parser → language}/lexer.rb +1 -1
- data/test/language/node.rb +84 -0
- data/test/{parser → language}/parser.rb +1 -1
- data/test/language/scope.rb +101 -2
- data/test/language/snippets.rb +23 -2
- data/test/other/config.rb +99 -1
- data/test/other/filetype.rb +95 -0
- data/test/other/inifile.rb +114 -0
- data/test/other/log.rb +3 -2
- data/test/other/transactions.rb +55 -10
- data/test/puppet/utiltest.rb +25 -1
- data/test/puppettest.rb +140 -46
- data/test/server/authconfig.rb +56 -0
- data/test/server/bucket.rb +32 -18
- data/test/server/fileserver.rb +75 -30
- data/test/server/master.rb +27 -4
- data/test/server/pelement.rb +298 -0
- data/test/server/rights.rb +41 -0
- data/test/server/server.rb +2 -2
- data/test/tagging/tagging.rb +100 -1
- data/test/types/basic.rb +3 -3
- data/test/types/cron.rb +24 -1
- data/test/types/exec.rb +99 -1
- data/test/types/file.rb +298 -2
- data/test/types/filebucket.rb +4 -15
- data/test/types/filesources.rb +43 -14
- data/test/types/group.rb +1 -13
- data/test/types/mount.rb +277 -0
- data/test/types/package.rb +164 -33
- data/test/types/parameter.rb +107 -0
- data/test/types/port.rb +2 -1
- data/test/types/service.rb +37 -2
- data/test/types/state.rb +92 -0
- data/test/types/symlink.rb +30 -2
- data/test/types/tidy.rb +2 -14
- data/test/types/type.rb +35 -1
- data/test/types/user.rb +110 -1
- data/test/types/yumrepo.rb +95 -0
- metadata +316 -290
- data/test/types/filetype.rb +0 -160
data/lib/puppet/server/master.rb
CHANGED
@@ -9,6 +9,8 @@ module Puppet
|
|
9
9
|
class Server
|
10
10
|
class MasterError < Puppet::Error; end
|
11
11
|
class Master < Handler
|
12
|
+
include Puppet::Util
|
13
|
+
|
12
14
|
attr_accessor :ast, :local
|
13
15
|
attr_reader :ca
|
14
16
|
|
@@ -17,6 +19,16 @@ class Server
|
|
17
19
|
iface.add_method("int freshness()")
|
18
20
|
}
|
19
21
|
|
22
|
+
# FIXME At some point, this should be autodocumenting.
|
23
|
+
def addfacts(facts)
|
24
|
+
# Add our server version to the fact list
|
25
|
+
facts["serverversion"] = Puppet.version.to_s
|
26
|
+
|
27
|
+
# And then add the server name and IP
|
28
|
+
facts["servername"] = Facter["hostname"].value
|
29
|
+
facts["serverip"] = Facter["ipaddress"].value
|
30
|
+
end
|
31
|
+
|
20
32
|
def filetimeout
|
21
33
|
@interpreter.filetimeout
|
22
34
|
end
|
@@ -35,10 +47,14 @@ class Server
|
|
35
47
|
end
|
36
48
|
|
37
49
|
def initialize(hash = {})
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
hash
|
50
|
+
args = {}
|
51
|
+
|
52
|
+
# Allow specification of a code snippet or of a file
|
53
|
+
if code = hash[:Code]
|
54
|
+
args[:Code] = code
|
55
|
+
else
|
56
|
+
args[:Manifest] = hash[:Manifest] || Puppet[:manifest]
|
57
|
+
end
|
42
58
|
|
43
59
|
if hash[:Local]
|
44
60
|
@local = hash[:Local]
|
@@ -46,25 +62,26 @@ class Server
|
|
46
62
|
@local = false
|
47
63
|
end
|
48
64
|
|
65
|
+
args[:Local] = @local
|
66
|
+
|
49
67
|
if hash.include?(:CA) and hash[:CA]
|
50
68
|
@ca = Puppet::SSLCertificates::CA.new()
|
51
69
|
else
|
52
70
|
@ca = nil
|
53
71
|
end
|
54
72
|
|
55
|
-
|
73
|
+
args[:ParseCheck] = hash[:FileTimeout] || 15
|
56
74
|
|
57
75
|
Puppet.debug("Creating interpreter")
|
58
76
|
|
59
|
-
args = {:Manifest => @file, :ParseCheck => @parsecheck}
|
60
|
-
|
61
77
|
if hash.include?(:UseNodes)
|
62
78
|
args[:UseNodes] = hash[:UseNodes]
|
63
79
|
elsif @local
|
64
80
|
args[:UseNodes] = false
|
65
81
|
end
|
66
82
|
|
67
|
-
# This is only used by the cfengine module
|
83
|
+
# This is only used by the cfengine module, or if --loadclasses was
|
84
|
+
# specified in +puppet+.
|
68
85
|
if hash.include?(:Classes)
|
69
86
|
args[:Classes] = hash[:Classes]
|
70
87
|
end
|
@@ -110,17 +127,39 @@ class Server
|
|
110
127
|
clientip = facts["ipaddress"]
|
111
128
|
end
|
112
129
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
130
|
+
# Add any server-side facts to our server.
|
131
|
+
addfacts(facts)
|
132
|
+
|
133
|
+
retobjects = nil
|
134
|
+
|
135
|
+
# This is hackish, but there's no "silence" option for benchmarks
|
136
|
+
# right now
|
137
|
+
if @local
|
138
|
+
begin
|
139
|
+
retobjects = @interpreter.run(client, facts)
|
140
|
+
rescue Puppet::Error => detail
|
141
|
+
Puppet.err detail
|
142
|
+
raise XMLRPC::FaultException.new(
|
143
|
+
1, detail.to_s
|
144
|
+
)
|
145
|
+
rescue => detail
|
146
|
+
Puppet.err detail.to_s
|
147
|
+
return ""
|
148
|
+
end
|
149
|
+
else
|
150
|
+
benchmark(:notice, "Compiled configuration for %s" % client) do
|
151
|
+
begin
|
152
|
+
retobjects = @interpreter.run(client, facts)
|
153
|
+
rescue Puppet::Error => detail
|
154
|
+
Puppet.err detail
|
155
|
+
raise XMLRPC::FaultException.new(
|
156
|
+
1, detail.to_s
|
157
|
+
)
|
158
|
+
rescue => detail
|
159
|
+
Puppet.err detail.to_s
|
160
|
+
return ""
|
161
|
+
end
|
162
|
+
end
|
124
163
|
end
|
125
164
|
|
126
165
|
if @local
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'puppet'
|
2
|
+
require 'puppet/server'
|
3
|
+
|
4
|
+
module Puppet
|
5
|
+
|
6
|
+
# Serve Puppet elements. Useful for querying, copying, and, um, other stuff.
|
7
|
+
class Server::PElement < Server::Handler
|
8
|
+
attr_accessor :local
|
9
|
+
|
10
|
+
@interface = XMLRPC::Service::Interface.new("pelementserver") { |iface|
|
11
|
+
iface.add_method("string apply(string, string)")
|
12
|
+
iface.add_method("string describe(string, string, array, array)")
|
13
|
+
iface.add_method("string list(string, array, string)")
|
14
|
+
}
|
15
|
+
|
16
|
+
# Apply a TransBucket as a transaction.
|
17
|
+
def apply(bucket, format = "yaml", client = nil, clientip = nil)
|
18
|
+
unless @local
|
19
|
+
begin
|
20
|
+
case format
|
21
|
+
when "yaml":
|
22
|
+
tmp = YAML::load(CGI.unescape(bucket))
|
23
|
+
bucket = tmp
|
24
|
+
else
|
25
|
+
raise Puppet::Error, "Unsupported format '%s'" % format
|
26
|
+
end
|
27
|
+
rescue => detail
|
28
|
+
raise Puppet::Error, "Could not load YAML TransBucket: %s" % detail
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
component = bucket.to_type
|
33
|
+
|
34
|
+
# Create a client, but specify the remote machine as the server
|
35
|
+
# because the class requires it, even though it's unused
|
36
|
+
client = Puppet::Client::MasterClient.new(:Server => client||"localhost")
|
37
|
+
|
38
|
+
# Set the objects
|
39
|
+
client.objects = component
|
40
|
+
|
41
|
+
# And then apply the configuration. This way we're reusing all
|
42
|
+
# the code in there. It should probably just be separated out, though.
|
43
|
+
transaction = client.apply
|
44
|
+
|
45
|
+
# It'd be nice to return some kind of report, but... at this point
|
46
|
+
# we have no such facility.
|
47
|
+
return "success"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Describe a given object. This returns the 'is' values for every state
|
51
|
+
# available on the object type.
|
52
|
+
def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil)
|
53
|
+
@local = true unless client
|
54
|
+
typeklass = nil
|
55
|
+
unless typeklass = Puppet.type(type)
|
56
|
+
raise Puppet::Error, "Puppet type %s is unsupported" % type
|
57
|
+
end
|
58
|
+
|
59
|
+
obj = nil
|
60
|
+
|
61
|
+
retrieve ||= :all
|
62
|
+
|
63
|
+
if obj = typeklass[name]
|
64
|
+
obj[:check] = retrieve
|
65
|
+
else
|
66
|
+
begin
|
67
|
+
obj = typeklass.create(:name => name, :check => retrieve)
|
68
|
+
rescue Puppet::Error => detail
|
69
|
+
raise Puppet::Error, "%s[%s] could not be created: %s" %
|
70
|
+
[type, name, detail]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
trans = obj.to_trans
|
75
|
+
|
76
|
+
# Now get rid of any attributes they specifically don't want
|
77
|
+
ignore.each do |st|
|
78
|
+
if trans.include? st
|
79
|
+
trans.delete(st)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# And get rid of any attributes that are nil
|
84
|
+
trans.each do |attr, value|
|
85
|
+
if value.nil?
|
86
|
+
trans.delete(attr)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if @local
|
91
|
+
return trans
|
92
|
+
else
|
93
|
+
str = nil
|
94
|
+
case format
|
95
|
+
when "yaml":
|
96
|
+
str = CGI.escape(YAML::dump(trans))
|
97
|
+
else
|
98
|
+
raise XMLRPC::FaultException.new(
|
99
|
+
1, "Unavailable config format %s" % format
|
100
|
+
)
|
101
|
+
end
|
102
|
+
return CGI.escape(str)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Create a new fileserving module.
|
107
|
+
def initialize(hash = {})
|
108
|
+
if hash[:Local]
|
109
|
+
@local = hash[:Local]
|
110
|
+
else
|
111
|
+
@local = false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# List all of the elements of a given type.
|
116
|
+
def list(type, ignore = [], base = nil, client = nil, clientip = nil)
|
117
|
+
@local = true unless client
|
118
|
+
typeklass = nil
|
119
|
+
unless typeklass = Puppet.type(type)
|
120
|
+
raise Puppet::Error, "Puppet type %s is unsupported" % type
|
121
|
+
end
|
122
|
+
|
123
|
+
ignore = [ignore] unless ignore.is_a? Array
|
124
|
+
bucket = TransBucket.new
|
125
|
+
bucket.type = typeklass.name
|
126
|
+
|
127
|
+
typeklass.list.each do |obj|
|
128
|
+
next if ignore.include? obj.name
|
129
|
+
|
130
|
+
object = TransObject.new(obj.name, typeklass.name)
|
131
|
+
bucket << object
|
132
|
+
end
|
133
|
+
|
134
|
+
if @local
|
135
|
+
return bucket
|
136
|
+
else
|
137
|
+
str = nil
|
138
|
+
case format
|
139
|
+
when "yaml":
|
140
|
+
str = YAML.dump(bucket)
|
141
|
+
else
|
142
|
+
raise XMLRPC::FaultException.new(
|
143
|
+
1, "Unavailable config format %s" % format
|
144
|
+
)
|
145
|
+
end
|
146
|
+
return CGI.escape(str)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def authcheck(file, mount, client, clientip)
|
153
|
+
unless mount.allowed?(client, clientip)
|
154
|
+
mount.warning "%s cannot access %s" %
|
155
|
+
[client, file]
|
156
|
+
raise Puppet::Server::AuthorizationError, "Cannot access %s" % mount
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Deal with ignore parameters.
|
161
|
+
def handleignore(children, path, ignore)
|
162
|
+
ignore.each { |ignore|
|
163
|
+
Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match|
|
164
|
+
children.delete(File.basename(match))
|
165
|
+
}
|
166
|
+
}
|
167
|
+
return children
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_s
|
171
|
+
"pelementserver"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# $Id: pelement.rb 1129 2006-04-21 19:14:59Z luke $
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'puppet/server/authstore'
|
3
|
+
|
4
|
+
module Puppet
|
5
|
+
class Server
|
6
|
+
# Define a set of rights and who has access to them.
|
7
|
+
class Rights < Hash
|
8
|
+
# We basically just proxy directly to our rights. Each Right stores
|
9
|
+
# its own auth abilities.
|
10
|
+
[:allow, :allowed?, :deny].each do |method|
|
11
|
+
define_method(method) do |name, *args|
|
12
|
+
name = name.intern if name.is_a? String
|
13
|
+
|
14
|
+
if obj = right(name)
|
15
|
+
obj.send(method, *args)
|
16
|
+
else
|
17
|
+
raise ArgumentError, "Unknown right '%s'" % name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](name)
|
23
|
+
name = name.intern if name.is_a? String
|
24
|
+
super(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Define a new right to which access can be provided.
|
28
|
+
def newright(name)
|
29
|
+
name = name.intern if name.is_a? String
|
30
|
+
shortname = Right.shortname(name)
|
31
|
+
if self.include? name
|
32
|
+
raise ArgumentError, "Right '%s' is already defined" % name
|
33
|
+
else
|
34
|
+
self[name] = Right.new(name, shortname)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Retrieve a right by name.
|
41
|
+
def right(name)
|
42
|
+
name = name.intern if name.is_a? String
|
43
|
+
self[name]
|
44
|
+
end
|
45
|
+
|
46
|
+
# A right.
|
47
|
+
class Right < AuthStore
|
48
|
+
attr_accessor :name, :shortname
|
49
|
+
|
50
|
+
Puppet::Util.logmethods(self, true)
|
51
|
+
|
52
|
+
def self.shortname(name)
|
53
|
+
name.to_s[0..0]
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(name, shortname = nil)
|
57
|
+
@name = name
|
58
|
+
@shortname = shortname
|
59
|
+
unless @shortname
|
60
|
+
@shortname = Right.shortname(name)
|
61
|
+
end
|
62
|
+
super()
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"access[%s]" % @name
|
67
|
+
end
|
68
|
+
|
69
|
+
# There's no real check to do at this point
|
70
|
+
def valid?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
#
|
78
|
+
# $Id: rights.rb 1129 2006-04-21 19:14:59Z luke $
|
@@ -34,15 +34,29 @@ class Server
|
|
34
34
|
end
|
35
35
|
|
36
36
|
# Verify that our client has access. We allow untrusted access to
|
37
|
-
# puppetca methods but
|
37
|
+
# puppetca methods but no others.
|
38
38
|
def authorize(request, method)
|
39
39
|
namespace = method.sub(/\..+/, '')
|
40
40
|
client = request.peeraddr[2]
|
41
41
|
ip = request.peeraddr[3]
|
42
42
|
if request.client_cert
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
if @puppetserver.authconfig.exists?
|
44
|
+
return @puppetserver.authconfig.allowed?(method, client, ip)
|
45
|
+
else
|
46
|
+
# This is pretty hackish, but...
|
47
|
+
# This means we can't actually test this method at this point.
|
48
|
+
# The next release of Puppet will almost definitely require
|
49
|
+
# this file to exist or will default to denying all access.
|
50
|
+
if Puppet.name == "puppetmasterd" or defined? Test::Unit::TestCase
|
51
|
+
Servlet.log "Allowing %s(%s) trusted access to %s" %
|
52
|
+
[client, ip, method]
|
53
|
+
return true
|
54
|
+
else
|
55
|
+
Servlet.log "Denying %s(%s) trusted access to %s on %s" %
|
56
|
+
[client, ip, method, Puppet.name]
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
46
60
|
else
|
47
61
|
if method =~ /^puppetca\./
|
48
62
|
Puppet.notice "Allowing %s(%s) untrusted access to CA methods" %
|
@@ -69,8 +83,7 @@ class Server
|
|
69
83
|
end
|
70
84
|
|
71
85
|
def initialize(server, handlers)
|
72
|
-
|
73
|
-
|
86
|
+
@puppetserver = server
|
74
87
|
# the servlet base class does not consume any arguments
|
75
88
|
# and its BasicServer base class only accepts a 'class_delim'
|
76
89
|
# option which won't change in Puppet at all
|
@@ -124,7 +124,8 @@ module Puppet::SSLCertificates
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def self.mkhash(dir, cert, certfile)
|
127
|
-
hash
|
127
|
+
# Make sure the hash is zero-padded to 8 chars
|
128
|
+
hash = "%08x" % cert.issuer.hash
|
128
129
|
hashpath = nil
|
129
130
|
10.times { |i|
|
130
131
|
path = File.join(dir, "%s.%s" % [hash, i])
|
@@ -149,10 +150,11 @@ module Puppet::SSLCertificates
|
|
149
150
|
break
|
150
151
|
}
|
151
152
|
|
153
|
+
|
152
154
|
return hashpath
|
153
155
|
end
|
154
156
|
require 'puppet/sslcertificates/certificate'
|
155
157
|
require 'puppet/sslcertificates/ca'
|
156
158
|
end
|
157
159
|
|
158
|
-
# $Id: sslcertificates.rb
|
160
|
+
# $Id: sslcertificates.rb 1117 2006-04-19 15:35:04Z luke $
|
@@ -4,34 +4,56 @@ class Puppet::SSLCertificates::CA
|
|
4
4
|
|
5
5
|
Puppet.setdefaults(:ca,
|
6
6
|
:cadir => { :default => "$ssldir/ca",
|
7
|
+
:owner => "$user",
|
8
|
+
:group => "$group",
|
7
9
|
:mode => 0770,
|
8
10
|
:desc => "The root directory for the certificate authority."
|
9
11
|
},
|
10
12
|
:cacert => { :default => "$cadir/ca_crt.pem",
|
13
|
+
:owner => "$user",
|
14
|
+
:group => "$group",
|
11
15
|
:mode => 0660,
|
12
16
|
:desc => "The CA certificate."
|
13
17
|
},
|
14
18
|
:cakey => { :default => "$cadir/ca_key.pem",
|
19
|
+
:owner => "$user",
|
20
|
+
:group => "$group",
|
15
21
|
:mode => 0660,
|
16
22
|
:desc => "The CA private key."
|
17
23
|
},
|
18
|
-
:capub =>
|
24
|
+
:capub => { :default => "$cadir/ca_pub.pem",
|
25
|
+
:owner => "$user",
|
26
|
+
:group => "$group",
|
27
|
+
:desc => "The CA public key."
|
28
|
+
},
|
19
29
|
:caprivatedir => { :default => "$cadir/private",
|
30
|
+
:owner => "$user",
|
31
|
+
:group => "$group",
|
20
32
|
:mode => 0770,
|
21
33
|
:desc => "Where the CA stores private certificate information."
|
22
34
|
},
|
23
|
-
:csrdir =>
|
24
|
-
|
35
|
+
:csrdir => { :default => "$cadir/requests",
|
36
|
+
:owner => "$user",
|
37
|
+
:group => "$group",
|
38
|
+
:desc => "Where the CA stores certificate requests"
|
39
|
+
},
|
25
40
|
:signeddir => { :default => "$cadir/signed",
|
41
|
+
:owner => "$user",
|
42
|
+
:group => "$group",
|
26
43
|
:mode => 0770,
|
27
44
|
:desc => "Where the CA stores signed certificates."
|
28
45
|
},
|
29
46
|
:capass => { :default => "$caprivatedir/ca.pass",
|
47
|
+
:owner => "$user",
|
48
|
+
:group => "$group",
|
30
49
|
:mode => 0660,
|
31
50
|
:desc => "Where the CA stores the password for the private key"
|
32
51
|
},
|
33
|
-
:serial =>
|
34
|
-
|
52
|
+
:serial => { :default => "$cadir/serial",
|
53
|
+
:owner => "$user",
|
54
|
+
:group => "$group",
|
55
|
+
:desc => "Where the serial number for certificates is stored."
|
56
|
+
},
|
35
57
|
:autosign => { :default => "$confdir/autosign.conf",
|
36
58
|
:mode => 0640,
|
37
59
|
:desc => "Whether to enable autosign. Valid values are true (which
|
@@ -44,10 +66,6 @@ class Puppet::SSLCertificates::CA
|
|
44
66
|
:keylength => [1024, "The bit length of keys."]
|
45
67
|
)
|
46
68
|
|
47
|
-
#@@params.each { |param|
|
48
|
-
# Puppet.setdefault(param,@@defaults[param])
|
49
|
-
#}
|
50
|
-
|
51
69
|
def certfile
|
52
70
|
@config[:cacert]
|
53
71
|
end
|
@@ -62,6 +80,7 @@ class Puppet::SSLCertificates::CA
|
|
62
80
|
File.join(Puppet[:signeddir], [hostname, "pem"].join("."))
|
63
81
|
end
|
64
82
|
|
83
|
+
# Turn our hostname into a Name object
|
65
84
|
def thing2name(thing)
|
66
85
|
thing.subject.to_a.find { |ary|
|
67
86
|
ary[0] == "CN"
|
@@ -88,29 +107,26 @@ class Puppet::SSLCertificates::CA
|
|
88
107
|
|
89
108
|
self.getcert
|
90
109
|
unless FileTest.exists?(@config[:serial])
|
91
|
-
|
110
|
+
Puppet.config.write(:serial) do |f|
|
92
111
|
f << "%04X" % 1
|
93
|
-
|
112
|
+
end
|
94
113
|
end
|
95
114
|
end
|
96
115
|
|
116
|
+
# Generate a new password for the CA.
|
97
117
|
def genpass
|
98
118
|
pass = ""
|
99
119
|
20.times { pass += (rand(74) + 48).chr }
|
100
120
|
|
101
|
-
# FIXME It's a hack that this still needs to be here :/
|
102
|
-
unless FileTest.exists?(File.dirname(@config[:capass]))
|
103
|
-
Puppet::Util.recmkdir(File.dirname(@config[:capass]), 0770)
|
104
|
-
end
|
105
|
-
|
106
121
|
begin
|
107
|
-
|
122
|
+
Puppet.config.write(:capass) { |f| f.print pass }
|
108
123
|
rescue Errno::EACCES => detail
|
109
124
|
raise Puppet::Error, detail.to_s
|
110
125
|
end
|
111
126
|
return pass
|
112
127
|
end
|
113
128
|
|
129
|
+
# Get the CA password.
|
114
130
|
def getpass
|
115
131
|
if @config[:capass] and File.readable?(@config[:capass])
|
116
132
|
return File.read(@config[:capass])
|
@@ -119,6 +135,7 @@ class Puppet::SSLCertificates::CA
|
|
119
135
|
end
|
120
136
|
end
|
121
137
|
|
138
|
+
# Get the CA cert.
|
122
139
|
def getcert
|
123
140
|
if FileTest.exists?(@config[:cacert])
|
124
141
|
@cert = OpenSSL::X509::Certificate.new(
|
@@ -129,6 +146,7 @@ class Puppet::SSLCertificates::CA
|
|
129
146
|
end
|
130
147
|
end
|
131
148
|
|
149
|
+
# Retrieve a client's CSR.
|
132
150
|
def getclientcsr(host)
|
133
151
|
csrfile = host2csrfile(host)
|
134
152
|
unless File.exists?(csrfile)
|
@@ -138,6 +156,7 @@ class Puppet::SSLCertificates::CA
|
|
138
156
|
return OpenSSL::X509::Request.new(File.read(csrfile))
|
139
157
|
end
|
140
158
|
|
159
|
+
# Retrieve a client's certificate.
|
141
160
|
def getclientcert(host)
|
142
161
|
certfile = host2certfile(host)
|
143
162
|
unless File.exists?(certfile)
|
@@ -147,17 +166,24 @@ class Puppet::SSLCertificates::CA
|
|
147
166
|
return [OpenSSL::X509::Certificate.new(File.read(certfile)), @cert]
|
148
167
|
end
|
149
168
|
|
169
|
+
# List certificates waiting to be signed.
|
150
170
|
def list
|
151
|
-
return Dir.entries(Puppet[:csrdir]).
|
152
|
-
file =~
|
171
|
+
return Dir.entries(Puppet[:csrdir]).find_all { |file|
|
172
|
+
file =~ /\.pem$/
|
153
173
|
}.collect { |file|
|
154
174
|
file.sub(/\.pem$/, '')
|
155
175
|
}
|
156
176
|
end
|
157
177
|
|
178
|
+
# Create the root certificate.
|
158
179
|
def mkrootcert
|
180
|
+
# Make the root cert's name the FQDN of the host running the CA.
|
181
|
+
name = Facter["hostname"].value
|
182
|
+
if domain = Facter["domain"].value
|
183
|
+
name += "." + domain
|
184
|
+
end
|
159
185
|
cert = Certificate.new(
|
160
|
-
:name =>
|
186
|
+
:name => name,
|
161
187
|
:cert => @config[:cacert],
|
162
188
|
:encrypt => @config[:capass],
|
163
189
|
:key => @config[:cakey],
|
@@ -165,10 +191,14 @@ class Puppet::SSLCertificates::CA
|
|
165
191
|
:length => 1825,
|
166
192
|
:type => :ca
|
167
193
|
)
|
168
|
-
|
169
|
-
|
194
|
+
|
195
|
+
# This creates the cakey file
|
196
|
+
Puppet::Util.asuser(Puppet[:user], Puppet[:group]) do
|
197
|
+
@cert = cert.mkselfsigned
|
198
|
+
end
|
199
|
+
Puppet.config.write(:cacert) do |f|
|
170
200
|
f.puts @cert.to_pem
|
171
|
-
|
201
|
+
end
|
172
202
|
@key = cert.key
|
173
203
|
return cert
|
174
204
|
end
|
@@ -182,6 +212,7 @@ class Puppet::SSLCertificates::CA
|
|
182
212
|
File.unlink(csrfile)
|
183
213
|
end
|
184
214
|
|
215
|
+
# Take the Puppet config and store it locally.
|
185
216
|
def setconfig(hash)
|
186
217
|
@config = {}
|
187
218
|
Puppet.config.params("ca").each { |param|
|
@@ -208,12 +239,10 @@ class Puppet::SSLCertificates::CA
|
|
208
239
|
unless @config[dir]
|
209
240
|
raise Puppet::DevError, "%s is undefined" % dir
|
210
241
|
end
|
211
|
-
unless FileTest.exists?(@config[dir])
|
212
|
-
Puppet.recmkdir(@config[dir])
|
213
|
-
end
|
214
242
|
}
|
215
243
|
end
|
216
244
|
|
245
|
+
# Sign a given certificate request.
|
217
246
|
def sign(csr)
|
218
247
|
unless csr.is_a?(OpenSSL::X509::Request)
|
219
248
|
raise Puppet::Error,
|
@@ -238,7 +267,6 @@ class Puppet::SSLCertificates::CA
|
|
238
267
|
File.read(@config[:cakey]), @config[:password]
|
239
268
|
)
|
240
269
|
else
|
241
|
-
system("ls -al %s" % Puppet[:capass])
|
242
270
|
cakey = OpenSSL::PKey::RSA.new(
|
243
271
|
File.read(@config[:cakey])
|
244
272
|
)
|
@@ -259,9 +287,9 @@ class Puppet::SSLCertificates::CA
|
|
259
287
|
)
|
260
288
|
|
261
289
|
# increment the serial
|
262
|
-
|
290
|
+
Puppet.config.write(:serial) do |f|
|
263
291
|
f << "%04X" % (serial + 1)
|
264
|
-
|
292
|
+
end
|
265
293
|
|
266
294
|
newcert.sign(cakey, OpenSSL::Digest::SHA1.new)
|
267
295
|
|
@@ -270,6 +298,9 @@ class Puppet::SSLCertificates::CA
|
|
270
298
|
return [newcert, cacert]
|
271
299
|
end
|
272
300
|
|
301
|
+
# Store the client's CSR for later signing. This is called from
|
302
|
+
# server/ca.rb, and the CSRs are deleted once the certificate is actually
|
303
|
+
# signed.
|
273
304
|
def storeclientcsr(csr)
|
274
305
|
host = thing2name(csr)
|
275
306
|
|
@@ -278,11 +309,12 @@ class Puppet::SSLCertificates::CA
|
|
278
309
|
raise Puppet::Error, "Certificate request for %s already exists" % host
|
279
310
|
end
|
280
311
|
|
281
|
-
|
312
|
+
Puppet.config.writesub(:csrdir, csrfile) do |f|
|
282
313
|
f.print csr.to_pem
|
283
|
-
|
314
|
+
end
|
284
315
|
end
|
285
316
|
|
317
|
+
# Store the certificate that we generate.
|
286
318
|
def storeclientcert(cert)
|
287
319
|
host = thing2name(cert)
|
288
320
|
|
@@ -292,10 +324,10 @@ class Puppet::SSLCertificates::CA
|
|
292
324
|
[certfile, host]
|
293
325
|
end
|
294
326
|
|
295
|
-
|
327
|
+
Puppet.config.writesub(:signeddir, certfile) do |f|
|
296
328
|
f.print cert.to_pem
|
297
|
-
|
329
|
+
end
|
298
330
|
end
|
299
331
|
end
|
300
332
|
|
301
|
-
# $Id: ca.rb
|
333
|
+
# $Id: ca.rb 1117 2006-04-19 15:35:04Z luke $
|