puppet 0.9.2
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 +0 -0
- data/COPYING +340 -0
- data/LICENSE +17 -0
- data/README +24 -0
- data/Rakefile +294 -0
- data/TODO +4 -0
- data/bin/cf2puppet +186 -0
- data/bin/puppet +176 -0
- data/bin/puppetca +213 -0
- data/bin/puppetd +246 -0
- data/bin/puppetdoc +184 -0
- data/bin/puppetmasterd +258 -0
- data/examples/code/allatonce +13 -0
- data/examples/code/assignments +11 -0
- data/examples/code/classing +35 -0
- data/examples/code/components +73 -0
- data/examples/code/execs +16 -0
- data/examples/code/failers/badclassnoparam +10 -0
- data/examples/code/failers/badclassparam +10 -0
- data/examples/code/failers/badcompnoparam +9 -0
- data/examples/code/failers/badcompparam +9 -0
- data/examples/code/failers/badtypeparam +3 -0
- data/examples/code/file.bl +11 -0
- data/examples/code/filedefaults +10 -0
- data/examples/code/fileparsing +116 -0
- data/examples/code/filerecursion +15 -0
- data/examples/code/functions +3 -0
- data/examples/code/groups +7 -0
- data/examples/code/head +30 -0
- data/examples/code/importing +8 -0
- data/examples/code/nodes +20 -0
- data/examples/code/one +8 -0
- data/examples/code/relationships +34 -0
- data/examples/code/selectors +28 -0
- data/examples/code/simpletests +11 -0
- data/examples/code/snippets/argumentdefaults +14 -0
- data/examples/code/snippets/casestatement +39 -0
- data/examples/code/snippets/classheirarchy.pp +15 -0
- data/examples/code/snippets/classincludes.pp +17 -0
- data/examples/code/snippets/classpathtest +11 -0
- data/examples/code/snippets/dirchmod +19 -0
- data/examples/code/snippets/failmissingexecpath.pp +13 -0
- data/examples/code/snippets/falsevalues.pp +3 -0
- data/examples/code/snippets/filecreate +11 -0
- data/examples/code/snippets/implicititeration +15 -0
- data/examples/code/snippets/multipleinstances +7 -0
- data/examples/code/snippets/namevartest +9 -0
- data/examples/code/snippets/scopetest +13 -0
- data/examples/code/snippets/selectorvalues.pp +22 -0
- data/examples/code/snippets/simpledefaults +5 -0
- data/examples/code/snippets/simpleselector +38 -0
- data/examples/code/svncommit +13 -0
- data/examples/root/bin/sleeper +69 -0
- data/examples/root/etc/configfile +0 -0
- data/examples/root/etc/debian-passwd +29 -0
- data/examples/root/etc/debian-syslog.conf +71 -0
- data/examples/root/etc/init.d/sleeper +65 -0
- data/examples/root/etc/otherfile +0 -0
- data/examples/root/etc/puppet/fileserver.conf +3 -0
- data/examples/root/etc/puppet/puppetmasterd.conf +10 -0
- data/ext/module:puppet +195 -0
- data/install.rb +270 -0
- data/lib/puppet.rb +249 -0
- data/lib/puppet/base64.rb +19 -0
- data/lib/puppet/client.rb +519 -0
- data/lib/puppet/config.rb +49 -0
- data/lib/puppet/daemon.rb +208 -0
- data/lib/puppet/element.rb +71 -0
- data/lib/puppet/event.rb +259 -0
- data/lib/puppet/log.rb +321 -0
- data/lib/puppet/metric.rb +250 -0
- data/lib/puppet/parsedfile.rb +38 -0
- data/lib/puppet/parser/ast.rb +1560 -0
- data/lib/puppet/parser/interpreter.rb +150 -0
- data/lib/puppet/parser/lexer.rb +226 -0
- data/lib/puppet/parser/parser.rb +1354 -0
- data/lib/puppet/parser/scope.rb +755 -0
- data/lib/puppet/server.rb +170 -0
- data/lib/puppet/server/authstore.rb +227 -0
- data/lib/puppet/server/ca.rb +140 -0
- data/lib/puppet/server/filebucket.rb +147 -0
- data/lib/puppet/server/fileserver.rb +477 -0
- data/lib/puppet/server/logger.rb +43 -0
- data/lib/puppet/server/master.rb +103 -0
- data/lib/puppet/server/servlet.rb +247 -0
- data/lib/puppet/sslcertificates.rb +737 -0
- data/lib/puppet/statechange.rb +150 -0
- data/lib/puppet/storage.rb +95 -0
- data/lib/puppet/transaction.rb +179 -0
- data/lib/puppet/transportable.rb +151 -0
- data/lib/puppet/type.rb +1354 -0
- data/lib/puppet/type/component.rb +141 -0
- data/lib/puppet/type/cron.rb +543 -0
- data/lib/puppet/type/exec.rb +316 -0
- data/lib/puppet/type/group.rb +152 -0
- data/lib/puppet/type/nameservice.rb +3 -0
- data/lib/puppet/type/nameservice/netinfo.rb +173 -0
- data/lib/puppet/type/nameservice/objectadd.rb +146 -0
- data/lib/puppet/type/nameservice/posix.rb +200 -0
- data/lib/puppet/type/package.rb +420 -0
- data/lib/puppet/type/package/apt.rb +70 -0
- data/lib/puppet/type/package/dpkg.rb +108 -0
- data/lib/puppet/type/package/rpm.rb +81 -0
- data/lib/puppet/type/package/sun.rb +117 -0
- data/lib/puppet/type/package/yum.rb +58 -0
- data/lib/puppet/type/pfile.rb +569 -0
- data/lib/puppet/type/pfile/checksum.rb +219 -0
- data/lib/puppet/type/pfile/create.rb +108 -0
- data/lib/puppet/type/pfile/group.rb +129 -0
- data/lib/puppet/type/pfile/mode.rb +131 -0
- data/lib/puppet/type/pfile/source.rb +264 -0
- data/lib/puppet/type/pfile/type.rb +31 -0
- data/lib/puppet/type/pfile/uid.rb +166 -0
- data/lib/puppet/type/pfilebucket.rb +80 -0
- data/lib/puppet/type/pprocess.rb +97 -0
- data/lib/puppet/type/service.rb +347 -0
- data/lib/puppet/type/service/base.rb +17 -0
- data/lib/puppet/type/service/debian.rb +50 -0
- data/lib/puppet/type/service/init.rb +145 -0
- data/lib/puppet/type/service/smf.rb +29 -0
- data/lib/puppet/type/state.rb +182 -0
- data/lib/puppet/type/symlink.rb +183 -0
- data/lib/puppet/type/tidy.rb +183 -0
- data/lib/puppet/type/typegen.rb +149 -0
- data/lib/puppet/type/typegen/filerecord.rb +243 -0
- data/lib/puppet/type/typegen/filetype.rb +316 -0
- data/lib/puppet/type/user.rb +290 -0
- data/lib/puppet/util.rb +138 -0
- data/test/certmgr/certmgr.rb +265 -0
- data/test/client/client.rb +203 -0
- data/test/executables/puppetbin.rb +53 -0
- data/test/executables/puppetca.rb +79 -0
- data/test/executables/puppetd.rb +71 -0
- data/test/executables/puppetmasterd.rb +153 -0
- data/test/executables/puppetmodule.rb +60 -0
- data/test/language/ast.rb +412 -0
- data/test/language/interpreter.rb +71 -0
- data/test/language/scope.rb +412 -0
- data/test/language/snippets.rb +445 -0
- data/test/other/events.rb +111 -0
- data/test/other/log.rb +195 -0
- data/test/other/metrics.rb +92 -0
- data/test/other/overrides.rb +115 -0
- data/test/other/parsedfile.rb +31 -0
- data/test/other/relationships.rb +113 -0
- data/test/other/state.rb +106 -0
- data/test/other/storage.rb +39 -0
- data/test/other/transactions.rb +235 -0
- data/test/parser/lexer.rb +120 -0
- data/test/parser/parser.rb +180 -0
- data/test/puppet/conffiles.rb +104 -0
- data/test/puppet/defaults.rb +100 -0
- data/test/puppet/error.rb +23 -0
- data/test/puppet/utiltest.rb +120 -0
- data/test/puppettest.rb +774 -0
- data/test/server/authstore.rb +209 -0
- data/test/server/bucket.rb +227 -0
- data/test/server/ca.rb +201 -0
- data/test/server/fileserver.rb +710 -0
- data/test/server/logger.rb +175 -0
- data/test/server/master.rb +150 -0
- data/test/server/server.rb +130 -0
- data/test/tagging/tagging.rb +80 -0
- data/test/test +51 -0
- data/test/types/basic.rb +119 -0
- data/test/types/component.rb +272 -0
- data/test/types/cron.rb +261 -0
- data/test/types/exec.rb +273 -0
- data/test/types/file.rb +616 -0
- data/test/types/filebucket.rb +167 -0
- data/test/types/fileignoresource.rb +287 -0
- data/test/types/filesources.rb +587 -0
- data/test/types/filetype.rb +162 -0
- data/test/types/group.rb +271 -0
- data/test/types/package.rb +205 -0
- data/test/types/query.rb +101 -0
- data/test/types/service.rb +100 -0
- data/test/types/symlink.rb +93 -0
- data/test/types/tidy.rb +124 -0
- data/test/types/type.rb +135 -0
- data/test/types/user.rb +371 -0
- metadata +243 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Puppet
|
2
|
+
class Server # :nodoc:
|
3
|
+
class LoggerError < RuntimeError; end
|
4
|
+
|
5
|
+
# Receive logs from remote hosts.
|
6
|
+
class Logger < Handler
|
7
|
+
@interface = XMLRPC::Service::Interface.new("puppetlogger") { |iface|
|
8
|
+
iface.add_method("void addlog(string)")
|
9
|
+
}
|
10
|
+
|
11
|
+
# accept a log message from a client, and route it accordingly
|
12
|
+
def addlog(message, client = nil, clientip = nil)
|
13
|
+
# if the client is set, then we're not local
|
14
|
+
if client
|
15
|
+
begin
|
16
|
+
message = Marshal::load(CGI.unescape(message))
|
17
|
+
#message = message
|
18
|
+
rescue => detail
|
19
|
+
raise XMLRPC::FaultException.new(
|
20
|
+
1, "Could not unMarshal log message from %s" % client
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Mark it as remote, so it's not sent to syslog
|
26
|
+
message.remote = true
|
27
|
+
|
28
|
+
if client
|
29
|
+
if ! message.source or message.source == "Puppet"
|
30
|
+
message.source = client
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Puppet::Log.newmessage(message)
|
35
|
+
|
36
|
+
# This is necessary or XMLRPC gets all pukey
|
37
|
+
return ""
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# $Id: logger.rb 733 2005-10-28 21:28:59Z luke $
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'puppet'
|
3
|
+
require 'puppet/parser/interpreter'
|
4
|
+
require 'puppet/sslcertificates'
|
5
|
+
require 'xmlrpc/server'
|
6
|
+
|
7
|
+
module Puppet
|
8
|
+
class Server
|
9
|
+
class MasterError < Puppet::Error; end
|
10
|
+
class Master < Handler
|
11
|
+
attr_accessor :ast, :local
|
12
|
+
attr_reader :ca
|
13
|
+
|
14
|
+
@interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface|
|
15
|
+
iface.add_method("string getconfig(string)")
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(hash = {})
|
19
|
+
|
20
|
+
# FIXME this should all be s/:File/:Manifest/g or something
|
21
|
+
# build our AST
|
22
|
+
@file = hash[:File] || Puppet[:manifest]
|
23
|
+
hash.delete(:File)
|
24
|
+
|
25
|
+
if hash[:Local]
|
26
|
+
@local = hash[:Local]
|
27
|
+
else
|
28
|
+
@local = false
|
29
|
+
end
|
30
|
+
|
31
|
+
if hash.include?(:CA) and hash[:CA]
|
32
|
+
@ca = Puppet::SSLCertificates::CA.new()
|
33
|
+
else
|
34
|
+
@ca = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
Puppet.debug("Creating interpreter")
|
38
|
+
|
39
|
+
args = {:Manifest => @file}
|
40
|
+
|
41
|
+
if hash.include?(:UseNodes)
|
42
|
+
args[:UseNodes] = hash[:UseNodes]
|
43
|
+
elsif @local
|
44
|
+
args[:UseNodes] = false
|
45
|
+
end
|
46
|
+
|
47
|
+
# This is only used by the cfengine module
|
48
|
+
if hash.include?(:Classes)
|
49
|
+
args[:Classes] = hash[:Classes]
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
@interpreter = Puppet::Parser::Interpreter.new(args)
|
54
|
+
rescue => detail
|
55
|
+
Puppet.err detail
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def getconfig(facts, client = nil, clientip = nil)
|
61
|
+
if @local
|
62
|
+
# we don't need to do anything, since we should already
|
63
|
+
# have raw objects
|
64
|
+
Puppet.debug "Our client is local"
|
65
|
+
else
|
66
|
+
Puppet.debug "Our client is remote"
|
67
|
+
|
68
|
+
# XXX this should definitely be done in the protocol, somehow
|
69
|
+
begin
|
70
|
+
facts = Marshal::load(CGI.unescape(facts))
|
71
|
+
rescue => detail
|
72
|
+
raise XMLRPC::FaultException.new(
|
73
|
+
1, "Could not rebuild facts"
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
unless client
|
79
|
+
client = facts["hostname"]
|
80
|
+
clientip = facts["ipaddress"]
|
81
|
+
end
|
82
|
+
Puppet.debug("Running interpreter")
|
83
|
+
begin
|
84
|
+
retobjects = @interpreter.run(client, facts)
|
85
|
+
rescue Puppet::Error => detail
|
86
|
+
Puppet.err detail
|
87
|
+
raise XMLRPC::FaultException.new(
|
88
|
+
1, detail.to_s
|
89
|
+
)
|
90
|
+
rescue => detail
|
91
|
+
Puppet.err detail.to_s
|
92
|
+
return ""
|
93
|
+
end
|
94
|
+
|
95
|
+
if @local
|
96
|
+
return retobjects
|
97
|
+
else
|
98
|
+
return CGI.escape(Marshal::dump(retobjects))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'xmlrpc/server'
|
2
|
+
|
3
|
+
module Puppet
|
4
|
+
class Server
|
5
|
+
class ServletError < RuntimeError; end
|
6
|
+
class Servlet < XMLRPC::WEBrickServlet
|
7
|
+
ERR_UNAUTHORIZED = 30
|
8
|
+
|
9
|
+
attr_accessor :request
|
10
|
+
|
11
|
+
# this is just a duplicate of the normal method; it's here for
|
12
|
+
# debugging when i need it
|
13
|
+
def self.get_instance(server, *options)
|
14
|
+
self.new(server, *options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# This is a hackish way to avoid an auth message every time we have a
|
18
|
+
# normal operation
|
19
|
+
def self.log(msg)
|
20
|
+
unless defined? @logs
|
21
|
+
@logs = {}
|
22
|
+
end
|
23
|
+
if @logs.include?(msg)
|
24
|
+
@logs[msg] += 1
|
25
|
+
else
|
26
|
+
Puppet.info msg
|
27
|
+
@logs[msg] = 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_handler(interface, handler)
|
32
|
+
@loadedhandlers << interface.prefix
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
# Verify that our client has access. We allow untrusted access to
|
37
|
+
# puppetca methods but none others.
|
38
|
+
def authorize(request, method)
|
39
|
+
namespace = method.sub(/\..+/, '')
|
40
|
+
client = request.peeraddr[2]
|
41
|
+
ip = request.peeraddr[3]
|
42
|
+
if request.client_cert
|
43
|
+
Servlet.log "Allowing %s(%s) trusted access to %s" %
|
44
|
+
[client, ip, method]
|
45
|
+
return true
|
46
|
+
else
|
47
|
+
if method =~ /^puppetca\./
|
48
|
+
Puppet.notice "Allowing %s(%s) untrusted access to CA methods" %
|
49
|
+
[client, ip]
|
50
|
+
else
|
51
|
+
Puppet.err "Unauthenticated client %s(%s) cannot call %s" %
|
52
|
+
[client, ip, method]
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def available?(method)
|
59
|
+
namespace = method.sub(/\..+/, '')
|
60
|
+
client = request.peeraddr[2]
|
61
|
+
ip = request.peeraddr[3]
|
62
|
+
if @loadedhandlers.include?(namespace)
|
63
|
+
return true
|
64
|
+
else
|
65
|
+
Puppet.warning "Client %s(%s) requested unavailable functionality %s" %
|
66
|
+
[client, ip, namespace]
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(server, handlers)
|
72
|
+
#Puppet.info server.inspect
|
73
|
+
|
74
|
+
# the servlet base class does not consume any arguments
|
75
|
+
# and its BasicServer base class only accepts a 'class_delim'
|
76
|
+
# option which won't change in Puppet at all
|
77
|
+
# thus, we don't need to pass any args to our base class,
|
78
|
+
# and we can consume them all ourselves
|
79
|
+
super()
|
80
|
+
|
81
|
+
@loadedhandlers = []
|
82
|
+
handlers.each { |handler|
|
83
|
+
#Puppet.debug "adding handler for %s" % handler.class
|
84
|
+
self.add_handler(handler.class.interface, handler)
|
85
|
+
}
|
86
|
+
|
87
|
+
# Initialize these to nil, but they will get set to values
|
88
|
+
# by the 'service' method. These have to instance variables
|
89
|
+
# because I don't have a clear line from the service method to
|
90
|
+
# the service hook.
|
91
|
+
@request = nil
|
92
|
+
@client = nil
|
93
|
+
@clientip = nil
|
94
|
+
|
95
|
+
self.set_service_hook { |obj, *args|
|
96
|
+
#raise "crap!"
|
97
|
+
if @client and @clientip
|
98
|
+
args.push(@client, @clientip)
|
99
|
+
#obj.call(args, @request)
|
100
|
+
end
|
101
|
+
begin
|
102
|
+
obj.call(*args)
|
103
|
+
rescue XMLRPC::FaultException
|
104
|
+
raise
|
105
|
+
rescue Puppet::Server::AuthorizationError => detail
|
106
|
+
#Puppet.warning obj.inspect
|
107
|
+
#Puppet.warning args.inspect
|
108
|
+
Puppet.err "Permission denied: %s" % detail.to_s
|
109
|
+
raise XMLRPC::FaultException.new(
|
110
|
+
1, detail.to_s
|
111
|
+
)
|
112
|
+
rescue Puppet::Error => detail
|
113
|
+
#Puppet.warning obj.inspect
|
114
|
+
#Puppet.warning args.inspect
|
115
|
+
Puppet.err detail.to_s
|
116
|
+
raise XMLRPC::FaultException.new(
|
117
|
+
1, detail.to_s
|
118
|
+
)
|
119
|
+
rescue => detail
|
120
|
+
#Puppet.warning obj.inspect
|
121
|
+
#Puppet.warning args.inspect
|
122
|
+
puts detail.inspect
|
123
|
+
Puppet.err "Could not call: %s" % detail.to_s
|
124
|
+
raise XMLRPC::FaultException.new(1, detail.to_s)
|
125
|
+
end
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
# Handle the actual request. This does some basic collection of
|
130
|
+
# data, and then just calls the parent method.
|
131
|
+
def service(request, response)
|
132
|
+
@request = request
|
133
|
+
|
134
|
+
# The only way that @client can be nil is if the request is local.
|
135
|
+
if peer = request.peeraddr
|
136
|
+
@client = peer[2]
|
137
|
+
@clientip = peer[3]
|
138
|
+
else
|
139
|
+
raise XMLRPC::FaultException.new(
|
140
|
+
ERR_UNCAUGHT_EXCEPTION,
|
141
|
+
"Could not retrieve client information"
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
# If they have a certificate (which will almost always be true)
|
146
|
+
# then we get the hostname from the cert, instead of via IP
|
147
|
+
# info
|
148
|
+
if cert = request.client_cert
|
149
|
+
nameary = cert.subject.to_a.find { |ary|
|
150
|
+
ary[0] == "CN"
|
151
|
+
}
|
152
|
+
|
153
|
+
if nameary.nil?
|
154
|
+
Puppet.warning "Could not retrieve server name from cert"
|
155
|
+
else
|
156
|
+
unless @client == nameary[1]
|
157
|
+
Puppet.debug "Overriding %s with cert name %s" %
|
158
|
+
[@client, nameary[1]]
|
159
|
+
@client = nameary[1]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
#if request.server_cert
|
164
|
+
# Puppet.info "server cert is %s" % @request.server_cert
|
165
|
+
#end
|
166
|
+
#p @request
|
167
|
+
begin
|
168
|
+
super
|
169
|
+
rescue => detail
|
170
|
+
Puppet.err "Could not service request: %s: %s" %
|
171
|
+
[detail.class, detail]
|
172
|
+
end
|
173
|
+
@client = nil
|
174
|
+
@clientip = nil
|
175
|
+
@request = nil
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
# this is pretty much just a copy of the original method but with more
|
181
|
+
# feedback
|
182
|
+
# here's where we have our authorization hooks
|
183
|
+
def dispatch(methodname, *args)
|
184
|
+
|
185
|
+
if defined? @request and @request
|
186
|
+
unless self.available?(methodname)
|
187
|
+
raise XMLRPC::FaultException.new(
|
188
|
+
ERR_UNAUTHORIZED,
|
189
|
+
"Functionality %s not available" %
|
190
|
+
methodname.sub(/\..+/, '')
|
191
|
+
)
|
192
|
+
end
|
193
|
+
unless self.authorize(@request, methodname)
|
194
|
+
raise XMLRPC::FaultException.new(
|
195
|
+
ERR_UNAUTHORIZED,
|
196
|
+
"Host %s not authorized to call %s" %
|
197
|
+
[@request.host, methodname]
|
198
|
+
)
|
199
|
+
end
|
200
|
+
else
|
201
|
+
raise Puppet::DevError, "Did not get request in dispatch"
|
202
|
+
end
|
203
|
+
|
204
|
+
#Puppet.warning "dispatch on %s called with %s" %
|
205
|
+
# [methodname, args.inspect]
|
206
|
+
for name, obj in @handler
|
207
|
+
if obj.kind_of? Proc
|
208
|
+
unless methodname == name
|
209
|
+
#Puppet.debug "obj is proc but %s != %s" %
|
210
|
+
# [methodname, name]
|
211
|
+
next
|
212
|
+
end
|
213
|
+
else
|
214
|
+
unless methodname =~ /^#{name}(.+)$/
|
215
|
+
#Puppet.debug "methodname did not match"
|
216
|
+
next
|
217
|
+
end
|
218
|
+
unless obj.respond_to? $1
|
219
|
+
#Puppet.debug "methodname does not respond to %s" % $1
|
220
|
+
next
|
221
|
+
end
|
222
|
+
obj = obj.method($1)
|
223
|
+
end
|
224
|
+
|
225
|
+
if check_arity(obj, args.size)
|
226
|
+
if @service_hook.nil?
|
227
|
+
return obj.call(*args)
|
228
|
+
else
|
229
|
+
return @service_hook.call(obj, *args)
|
230
|
+
end
|
231
|
+
else
|
232
|
+
Puppet.debug "arity is incorrect"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
if @default_handler.nil?
|
237
|
+
raise XMLRPC::FaultException.new(
|
238
|
+
ERR_METHOD_MISSING,
|
239
|
+
"Method #{methodname} missing or wrong number of parameters!"
|
240
|
+
)
|
241
|
+
else
|
242
|
+
@default_handler.call(methodname, *args)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,737 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
#--------------------
|
4
|
+
# the puppet client
|
5
|
+
#
|
6
|
+
# $Id: sslcertificates.rb 720 2005-10-21 06:16:43Z luke $
|
7
|
+
|
8
|
+
|
9
|
+
require 'puppet'
|
10
|
+
require 'openssl'
|
11
|
+
|
12
|
+
module Puppet
|
13
|
+
module SSLCertificates
|
14
|
+
def self.mkdir(dir)
|
15
|
+
# this is all a bunch of stupid hackery
|
16
|
+
unless FileTest.exists?(dir)
|
17
|
+
comp = Puppet::Type::Component.create(
|
18
|
+
:name => "certdir creation"
|
19
|
+
)
|
20
|
+
path = ['']
|
21
|
+
|
22
|
+
dir.split(File::SEPARATOR).each { |d|
|
23
|
+
path << d
|
24
|
+
if FileTest.exists?(File.join(path))
|
25
|
+
unless FileTest.directory?(File.join(path))
|
26
|
+
raise "%s exists but is not a directory" % File.join(path)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
obj = Puppet::Type.type(:file).create(
|
30
|
+
:name => File.join(path),
|
31
|
+
:mode => "750",
|
32
|
+
:create => "directory"
|
33
|
+
)
|
34
|
+
|
35
|
+
comp.push obj
|
36
|
+
end
|
37
|
+
}
|
38
|
+
trans = comp.evaluate
|
39
|
+
trans.evaluate
|
40
|
+
end
|
41
|
+
|
42
|
+
Puppet::Type.allclear
|
43
|
+
end
|
44
|
+
|
45
|
+
#def self.mkcert(type, name, days, issuercert, issuername, serial, publickey)
|
46
|
+
def self.mkcert(hash)
|
47
|
+
[:type, :name, :days, :issuer, :serial, :publickey].each { |param|
|
48
|
+
unless hash.include?(param)
|
49
|
+
raise ArgumentError, "mkcert called without %s" % param
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
cert = OpenSSL::X509::Certificate.new
|
54
|
+
from = Time.now
|
55
|
+
|
56
|
+
cert.subject = hash[:name]
|
57
|
+
if hash[:issuer]
|
58
|
+
cert.issuer = hash[:issuer].subject
|
59
|
+
else
|
60
|
+
# we're a self-signed cert
|
61
|
+
cert.issuer = hash[:name]
|
62
|
+
end
|
63
|
+
cert.not_before = from
|
64
|
+
cert.not_after = from + (hash[:days] * 24 * 60 * 60)
|
65
|
+
cert.version = 2 # X509v3
|
66
|
+
|
67
|
+
cert.public_key = hash[:publickey]
|
68
|
+
cert.serial = hash[:serial]
|
69
|
+
|
70
|
+
basic_constraint = nil
|
71
|
+
key_usage = nil
|
72
|
+
ext_key_usage = nil
|
73
|
+
|
74
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
75
|
+
|
76
|
+
ef.subject_certificate = cert
|
77
|
+
|
78
|
+
if hash[:issuer]
|
79
|
+
ef.issuer_certificate = hash[:issuer]
|
80
|
+
else
|
81
|
+
ef.issuer_certificate = cert
|
82
|
+
end
|
83
|
+
|
84
|
+
ex = []
|
85
|
+
case hash[:type]
|
86
|
+
when :ca:
|
87
|
+
basic_constraint = "CA:TRUE"
|
88
|
+
key_usage = %w{cRLSign keyCertSign}
|
89
|
+
when :terminalsubca:
|
90
|
+
basic_constraint = "CA:TRUE,pathlen:0"
|
91
|
+
key_usage = %w{cRLSign keyCertSign}
|
92
|
+
when :server:
|
93
|
+
basic_constraint = "CA:FALSE"
|
94
|
+
key_usage = %w{digitalSignature keyEncipherment}
|
95
|
+
ext_key_usage = %w{serverAuth clientAuth}
|
96
|
+
when :ocsp:
|
97
|
+
basic_constraint = "CA:FALSE"
|
98
|
+
key_usage = %w{nonRepudiation digitalSignature}
|
99
|
+
ext_key_usage = %w{serverAuth OCSPSigning}
|
100
|
+
when :client:
|
101
|
+
basic_constraint = "CA:FALSE"
|
102
|
+
key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
|
103
|
+
ext_key_usage = %w{clientAuth emailProtection}
|
104
|
+
ex << ef.create_extension("nsCertType", "client,email")
|
105
|
+
else
|
106
|
+
raise Puppet::Error, "unknown cert type '%s'" % hash[:type]
|
107
|
+
end
|
108
|
+
|
109
|
+
ex << ef.create_extension("nsComment",
|
110
|
+
"Puppet Ruby/OpenSSL Generated Certificate")
|
111
|
+
ex << ef.create_extension("basicConstraints", basic_constraint, true)
|
112
|
+
ex << ef.create_extension("subjectKeyIdentifier", "hash")
|
113
|
+
|
114
|
+
if key_usage
|
115
|
+
ex << ef.create_extension("keyUsage", key_usage.join(","))
|
116
|
+
end
|
117
|
+
if ext_key_usage
|
118
|
+
ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(","))
|
119
|
+
end
|
120
|
+
|
121
|
+
#if @ca_config[:cdp_location] then
|
122
|
+
# ex << ef.create_extension("crlDistributionPoints",
|
123
|
+
# @ca_config[:cdp_location])
|
124
|
+
#end
|
125
|
+
|
126
|
+
#if @ca_config[:ocsp_location] then
|
127
|
+
# ex << ef.create_extension("authorityInfoAccess",
|
128
|
+
# "OCSP;" << @ca_config[:ocsp_location])
|
129
|
+
#end
|
130
|
+
cert.extensions = ex
|
131
|
+
|
132
|
+
# for some reason this _must_ be the last extension added
|
133
|
+
if hash[:type] == :ca
|
134
|
+
ex << ef.create_extension("authorityKeyIdentifier",
|
135
|
+
"keyid:always,issuer:always")
|
136
|
+
end
|
137
|
+
|
138
|
+
return cert
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.mkhash(dir, cert, certfile)
|
142
|
+
hash = "%x" % cert.issuer.hash
|
143
|
+
hashpath = nil
|
144
|
+
10.times { |i|
|
145
|
+
path = File.join(dir, "%s.%s" % [hash, i])
|
146
|
+
if FileTest.exists?(path)
|
147
|
+
if FileTest.symlink?(path)
|
148
|
+
dest = File.readlink(path)
|
149
|
+
if dest == certfile
|
150
|
+
# the correct link already exists
|
151
|
+
hashpath = path
|
152
|
+
break
|
153
|
+
else
|
154
|
+
next
|
155
|
+
end
|
156
|
+
else
|
157
|
+
next
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
File.symlink(certfile, path)
|
162
|
+
|
163
|
+
hashpath = path
|
164
|
+
break
|
165
|
+
}
|
166
|
+
|
167
|
+
return hashpath
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
class CA
|
173
|
+
attr_accessor :keyfile, :file, :config, :dir, :cert
|
174
|
+
|
175
|
+
@@params = [
|
176
|
+
:certdir,
|
177
|
+
:publickeydir,
|
178
|
+
:privatekeydir,
|
179
|
+
:cadir,
|
180
|
+
:cakey,
|
181
|
+
:cacert,
|
182
|
+
:capass,
|
183
|
+
:capub,
|
184
|
+
:csrdir,
|
185
|
+
:signeddir,
|
186
|
+
:serial,
|
187
|
+
:privatedir,
|
188
|
+
:ca_crl_days,
|
189
|
+
:ca_days,
|
190
|
+
:ca_md,
|
191
|
+
:req_bits,
|
192
|
+
:keylength,
|
193
|
+
:autosign
|
194
|
+
]
|
195
|
+
|
196
|
+
@@defaults = {
|
197
|
+
:certdir => [:ssldir, "certs"],
|
198
|
+
:publickeydir => [:ssldir, "public_keys"],
|
199
|
+
:privatekeydir => [:ssldir, "private_keys"],
|
200
|
+
:cadir => [:ssldir, "ca"],
|
201
|
+
:cacert => [:cadir, "ca_crt.pem"],
|
202
|
+
:cakey => [:cadir, "ca_key.pem"],
|
203
|
+
:capub => [:cadir, "ca_pub.pem"],
|
204
|
+
:csrdir => [:cadir, "requests"],
|
205
|
+
:signeddir => [:cadir, "signed"],
|
206
|
+
:capass => [:cadir, "ca.pass"],
|
207
|
+
:serial => [:cadir, "serial"],
|
208
|
+
:privatedir => [:ssldir, "private"],
|
209
|
+
:passfile => [:privatedir, "password"],
|
210
|
+
:autosign => [:puppetconf, "autosign.conf"],
|
211
|
+
:ca_crl_days => 365,
|
212
|
+
:ca_days => 1825,
|
213
|
+
:ca_md => "md5",
|
214
|
+
:req_bits => 2048,
|
215
|
+
:keylength => 1024,
|
216
|
+
}
|
217
|
+
|
218
|
+
@@params.each { |param|
|
219
|
+
Puppet.setdefault(param,@@defaults[param])
|
220
|
+
}
|
221
|
+
|
222
|
+
def certfile
|
223
|
+
@config[:cacert]
|
224
|
+
end
|
225
|
+
|
226
|
+
def host2csrfile(hostname)
|
227
|
+
File.join(Puppet[:csrdir], [hostname, "pem"].join("."))
|
228
|
+
end
|
229
|
+
|
230
|
+
# this stores signed certs in a directory unrelated to
|
231
|
+
# normal client certs
|
232
|
+
def host2certfile(hostname)
|
233
|
+
File.join(Puppet[:signeddir], [hostname, "pem"].join("."))
|
234
|
+
end
|
235
|
+
|
236
|
+
def thing2name(thing)
|
237
|
+
thing.subject.to_a.find { |ary|
|
238
|
+
ary[0] == "CN"
|
239
|
+
}[1]
|
240
|
+
end
|
241
|
+
|
242
|
+
def initialize(hash = {})
|
243
|
+
self.setconfig(hash)
|
244
|
+
|
245
|
+
self.getcert
|
246
|
+
unless FileTest.exists?(@config[:serial])
|
247
|
+
File.open(@config[:serial], "w") { |f|
|
248
|
+
f << "%04X" % 1
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
if Puppet[:capass] and ! FileTest.exists?(Puppet[:capass])
|
253
|
+
self.genpass
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def genpass
|
258
|
+
pass = ""
|
259
|
+
20.times { pass += (rand(74) + 48).chr }
|
260
|
+
|
261
|
+
unless @config[:capass]
|
262
|
+
raise "No passfile"
|
263
|
+
end
|
264
|
+
Puppet::SSLCertificates.mkdir(File.dirname(@config[:capass]))
|
265
|
+
File.open(@config[:capass], "w", 0600) { |f| f.print pass }
|
266
|
+
return pass
|
267
|
+
end
|
268
|
+
|
269
|
+
def getcert
|
270
|
+
if FileTest.exists?(@config[:cacert])
|
271
|
+
@cert = OpenSSL::X509::Certificate.new(
|
272
|
+
File.read(@config[:cacert])
|
273
|
+
)
|
274
|
+
else
|
275
|
+
self.mkrootcert
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def getclientcsr(host)
|
280
|
+
csrfile = host2csrfile(host)
|
281
|
+
unless File.exists?(csrfile)
|
282
|
+
return nil
|
283
|
+
end
|
284
|
+
|
285
|
+
return OpenSSL::X509::Request.new(File.read(csrfile))
|
286
|
+
end
|
287
|
+
|
288
|
+
def getclientcert(host)
|
289
|
+
certfile = host2certfile(host)
|
290
|
+
unless File.exists?(certfile)
|
291
|
+
return [nil, nil]
|
292
|
+
end
|
293
|
+
|
294
|
+
return [OpenSSL::X509::Certificate.new(File.read(certfile)), @cert]
|
295
|
+
end
|
296
|
+
|
297
|
+
def list
|
298
|
+
return Dir.entries(Puppet[:csrdir]).reject { |file|
|
299
|
+
file =~ /^\.+$/
|
300
|
+
}.collect { |file|
|
301
|
+
file.sub(/\.pem$/, '')
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
def mkrootcert
|
306
|
+
cert = Certificate.new(
|
307
|
+
:name => "CAcert",
|
308
|
+
:cert => @config[:cacert],
|
309
|
+
:encrypt => @config[:passfile],
|
310
|
+
:key => @config[:cakey],
|
311
|
+
:selfsign => true,
|
312
|
+
:length => 1825,
|
313
|
+
:type => :ca
|
314
|
+
)
|
315
|
+
@cert = cert.mkselfsigned
|
316
|
+
File.open(@config[:cacert], "w", 0660) { |f|
|
317
|
+
f.puts @cert.to_pem
|
318
|
+
}
|
319
|
+
@key = cert.key
|
320
|
+
return cert
|
321
|
+
end
|
322
|
+
|
323
|
+
def removeclientcsr(host)
|
324
|
+
csrfile = host2csrfile(host)
|
325
|
+
unless File.exists?(csrfile)
|
326
|
+
raise Puppet::Error, "No certificate request for %s" % host
|
327
|
+
end
|
328
|
+
|
329
|
+
File.unlink(csrfile)
|
330
|
+
end
|
331
|
+
|
332
|
+
def setconfig(hash)
|
333
|
+
@config = {}
|
334
|
+
@@params.each { |param|
|
335
|
+
if hash.include?(param)
|
336
|
+
begin
|
337
|
+
@config[param] = hash[param]
|
338
|
+
Puppet[param] = hash[param]
|
339
|
+
hash.delete(param)
|
340
|
+
rescue => detail
|
341
|
+
puts detail
|
342
|
+
exit
|
343
|
+
end
|
344
|
+
else
|
345
|
+
begin
|
346
|
+
@config[param] = Puppet[param]
|
347
|
+
rescue => detail
|
348
|
+
puts detail
|
349
|
+
exit
|
350
|
+
end
|
351
|
+
end
|
352
|
+
}
|
353
|
+
|
354
|
+
if hash.include?(:password)
|
355
|
+
@config[:password] = hash[:password]
|
356
|
+
hash.delete(:password)
|
357
|
+
end
|
358
|
+
|
359
|
+
if hash.length > 0
|
360
|
+
raise ArgumentError, "Unknown parameters %s" % hash.keys.join(",")
|
361
|
+
end
|
362
|
+
|
363
|
+
[:cadir, :csrdir, :signeddir].each { |dir|
|
364
|
+
unless @config[dir]
|
365
|
+
raise "%s is undefined" % dir
|
366
|
+
end
|
367
|
+
unless FileTest.exists?(@config[dir])
|
368
|
+
Puppet::SSLCertificates.mkdir(@config[dir])
|
369
|
+
end
|
370
|
+
}
|
371
|
+
end
|
372
|
+
|
373
|
+
def sign(csr)
|
374
|
+
unless csr.is_a?(OpenSSL::X509::Request)
|
375
|
+
raise Puppet::Error,
|
376
|
+
"CA#sign only accepts OpenSSL::X509::Request objects, not %s" %
|
377
|
+
csr.class
|
378
|
+
end
|
379
|
+
|
380
|
+
unless csr.verify(csr.public_key)
|
381
|
+
raise Puppet::Error, "CSR sign verification failed"
|
382
|
+
end
|
383
|
+
|
384
|
+
# i should probably check key length...
|
385
|
+
|
386
|
+
# read the ca cert in
|
387
|
+
cacert = OpenSSL::X509::Certificate.new(
|
388
|
+
File.read(@config[:cacert])
|
389
|
+
)
|
390
|
+
|
391
|
+
cakey = nil
|
392
|
+
if @config[:password]
|
393
|
+
cakey = OpenSSL::PKey::RSA.new(
|
394
|
+
File.read(@config[:cakey]), @config[:password]
|
395
|
+
)
|
396
|
+
else
|
397
|
+
cakey = OpenSSL::PKey::RSA.new(
|
398
|
+
File.read(@config[:cakey])
|
399
|
+
)
|
400
|
+
end
|
401
|
+
|
402
|
+
unless cacert.check_private_key(cakey)
|
403
|
+
raise Puppet::Error, "CA Certificate is invalid"
|
404
|
+
end
|
405
|
+
|
406
|
+
serial = File.read(@config[:serial]).chomp.hex
|
407
|
+
newcert = SSLCertificates.mkcert(
|
408
|
+
:type => :server,
|
409
|
+
:name => csr.subject,
|
410
|
+
:days => @config[:ca_days],
|
411
|
+
:issuer => cacert,
|
412
|
+
:serial => serial,
|
413
|
+
:publickey => csr.public_key
|
414
|
+
)
|
415
|
+
|
416
|
+
# increment the serial
|
417
|
+
File.open(@config[:serial], "w") { |f|
|
418
|
+
f << "%04X" % (serial + 1)
|
419
|
+
}
|
420
|
+
|
421
|
+
newcert.sign(cakey, OpenSSL::Digest::SHA1.new)
|
422
|
+
|
423
|
+
self.storeclientcert(newcert)
|
424
|
+
|
425
|
+
return [newcert, cacert]
|
426
|
+
end
|
427
|
+
|
428
|
+
def storeclientcsr(csr)
|
429
|
+
host = thing2name(csr)
|
430
|
+
|
431
|
+
csrfile = host2csrfile(host)
|
432
|
+
if File.exists?(csrfile)
|
433
|
+
raise Puppet::Error, "Certificate request for %s already exists" % host
|
434
|
+
end
|
435
|
+
|
436
|
+
File.open(csrfile, "w", 0660) { |f|
|
437
|
+
f.print csr.to_pem
|
438
|
+
}
|
439
|
+
end
|
440
|
+
|
441
|
+
def storeclientcert(cert)
|
442
|
+
host = thing2name(cert)
|
443
|
+
|
444
|
+
certfile = host2certfile(host)
|
445
|
+
if File.exists?(certfile)
|
446
|
+
Puppet.notice "Overwriting signed certificate %s for %s" %
|
447
|
+
[certfile, host]
|
448
|
+
end
|
449
|
+
|
450
|
+
File.open(certfile, "w", 0660) { |f|
|
451
|
+
f.print cert.to_pem
|
452
|
+
}
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
|
457
|
+
class Certificate
|
458
|
+
attr_accessor :certfile, :keyfile, :name, :dir, :hash, :type
|
459
|
+
attr_accessor :key, :cert, :csr, :cacert
|
460
|
+
|
461
|
+
@@params2names = {
|
462
|
+
:name => "CN",
|
463
|
+
:state => "ST",
|
464
|
+
:country => "C",
|
465
|
+
:email => "emailAddress",
|
466
|
+
:org => "O",
|
467
|
+
:city => "L",
|
468
|
+
:ou => "OU"
|
469
|
+
}
|
470
|
+
|
471
|
+
def certname
|
472
|
+
OpenSSL::X509::Name.new self.subject
|
473
|
+
end
|
474
|
+
|
475
|
+
def delete
|
476
|
+
[@certfile,@keyfile].each { |file|
|
477
|
+
if FileTest.exists?(file)
|
478
|
+
File.unlink(file)
|
479
|
+
end
|
480
|
+
}
|
481
|
+
|
482
|
+
if defined? @hash and @hash
|
483
|
+
if FileTest.symlink?(@hash)
|
484
|
+
File.unlink(@hash)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def exists?
|
490
|
+
return FileTest.exists?(@certfile)
|
491
|
+
end
|
492
|
+
|
493
|
+
def getkey
|
494
|
+
unless FileTest.exists?(@keyfile)
|
495
|
+
self.mkkey()
|
496
|
+
end
|
497
|
+
if @password
|
498
|
+
@key = OpenSSL::PKey::RSA.new(
|
499
|
+
File.read(@keyfile),
|
500
|
+
@password
|
501
|
+
)
|
502
|
+
else
|
503
|
+
@key = OpenSSL::PKey::RSA.new(
|
504
|
+
File.read(@keyfile)
|
505
|
+
)
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
def initialize(hash)
|
510
|
+
unless hash.include?(:name)
|
511
|
+
raise "You must specify the common name for the certificate"
|
512
|
+
end
|
513
|
+
@name = hash[:name]
|
514
|
+
|
515
|
+
# init a few variables
|
516
|
+
@cert = @key = @csr = nil
|
517
|
+
|
518
|
+
if hash.include?(:cert)
|
519
|
+
@certfile = hash[:cert]
|
520
|
+
@dir = File.dirname(@certfile)
|
521
|
+
else
|
522
|
+
@dir = hash[:dir] || Puppet[:certdir]
|
523
|
+
@certfile = File.join(@dir, @name)
|
524
|
+
end
|
525
|
+
|
526
|
+
@cacertfile ||= File.join(Puppet[:certdir], "ca.pem")
|
527
|
+
|
528
|
+
unless FileTest.directory?(@dir)
|
529
|
+
Puppet::SSLCertificates.mkdir(@dir)
|
530
|
+
end
|
531
|
+
|
532
|
+
unless @certfile =~ /\.pem$/
|
533
|
+
@certfile += ".pem"
|
534
|
+
end
|
535
|
+
@keyfile = hash[:key] || File.join(
|
536
|
+
Puppet[:privatekeydir], [@name,"pem"].join(".")
|
537
|
+
)
|
538
|
+
unless FileTest.directory?(@dir)
|
539
|
+
Puppet::SSLCertificates.mkdir(@dir)
|
540
|
+
end
|
541
|
+
|
542
|
+
[@keyfile].each { |file|
|
543
|
+
dir = File.dirname(file)
|
544
|
+
|
545
|
+
unless FileTest.directory?(dir)
|
546
|
+
Puppet::SSLCertificates.mkdir(dir)
|
547
|
+
end
|
548
|
+
}
|
549
|
+
|
550
|
+
@days = hash[:length] || 365
|
551
|
+
@selfsign = hash[:selfsign] || false
|
552
|
+
@encrypt = hash[:encrypt] || false
|
553
|
+
@replace = hash[:replace] || false
|
554
|
+
@issuer = hash[:issuer] || nil
|
555
|
+
|
556
|
+
if hash.include?(:type)
|
557
|
+
case hash[:type]
|
558
|
+
when :ca, :client, :server: @type = hash[:type]
|
559
|
+
else
|
560
|
+
raise "Invalid Cert type %s" % hash[:type]
|
561
|
+
end
|
562
|
+
else
|
563
|
+
@type = :client
|
564
|
+
end
|
565
|
+
|
566
|
+
@params = {:name => @name}
|
567
|
+
[:state, :country, :email, :org, :ou].each { |param|
|
568
|
+
if hash.include?(param)
|
569
|
+
@params[param] = hash[param]
|
570
|
+
end
|
571
|
+
}
|
572
|
+
|
573
|
+
if @encrypt
|
574
|
+
if @encrypt =~ /^\//
|
575
|
+
File.open(@encrypt) { |f|
|
576
|
+
@password = f.read.chomp
|
577
|
+
}
|
578
|
+
else
|
579
|
+
raise ":encrypt must be a path to a pass phrase file"
|
580
|
+
end
|
581
|
+
else
|
582
|
+
@password = nil
|
583
|
+
end
|
584
|
+
|
585
|
+
if hash.include?(:selfsign)
|
586
|
+
@selfsign = hash[:selfsign]
|
587
|
+
else
|
588
|
+
@selfsign = false
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
# this only works for servers, not for users
|
593
|
+
def mkcsr
|
594
|
+
unless defined? @key and @key
|
595
|
+
self.getkey
|
596
|
+
end
|
597
|
+
|
598
|
+
name = OpenSSL::X509::Name.new self.subject
|
599
|
+
|
600
|
+
@csr = OpenSSL::X509::Request.new
|
601
|
+
@csr.version = 0
|
602
|
+
@csr.subject = name
|
603
|
+
@csr.public_key = @key.public_key
|
604
|
+
@csr.sign(@key, OpenSSL::Digest::SHA1.new)
|
605
|
+
|
606
|
+
#File.open(@csrfile, "w") { |f|
|
607
|
+
# f << @csr.to_pem
|
608
|
+
#}
|
609
|
+
|
610
|
+
unless @csr.verify(@key.public_key)
|
611
|
+
raise Puppet::Error, "CSR sign verification failed"
|
612
|
+
end
|
613
|
+
|
614
|
+
return @csr
|
615
|
+
end
|
616
|
+
|
617
|
+
def mkkey
|
618
|
+
# @key is the file
|
619
|
+
|
620
|
+
@key = OpenSSL::PKey::RSA.new(1024)
|
621
|
+
# { |p,n|
|
622
|
+
# case p
|
623
|
+
# when 0; Puppet.info "key info: ." # BN_generate_prime
|
624
|
+
# when 1; Puppet.info "key info: +" # BN_generate_prime
|
625
|
+
# when 2; Puppet.info "key info: *" # searching good prime,
|
626
|
+
# # n = #of try,
|
627
|
+
# # but also data from BN_generate_prime
|
628
|
+
# when 3; Puppet.info "key info: \n" # found good prime, n==0 - p, n==1 - q,
|
629
|
+
# # but also data from BN_generate_prime
|
630
|
+
# else; Puppet.info "key info: *" # BN_generate_prime
|
631
|
+
# end
|
632
|
+
# }
|
633
|
+
|
634
|
+
if @password
|
635
|
+
#passwdproc = proc { @password }
|
636
|
+
keytext = @key.export(
|
637
|
+
OpenSSL::Cipher::DES.new(:EDE3, :CBC),
|
638
|
+
@password
|
639
|
+
)
|
640
|
+
File.open(@keyfile, "w", 0400) { |f|
|
641
|
+
f << keytext
|
642
|
+
}
|
643
|
+
else
|
644
|
+
File.open(@keyfile, "w", 0400) { |f|
|
645
|
+
f << @key.to_pem
|
646
|
+
}
|
647
|
+
end
|
648
|
+
|
649
|
+
#cmd = "#{ossl} genrsa -out #{@key} 1024"
|
650
|
+
end
|
651
|
+
|
652
|
+
def mkselfsigned
|
653
|
+
unless defined? @key and @key
|
654
|
+
self.getkey
|
655
|
+
end
|
656
|
+
|
657
|
+
if defined? @cert and @cert
|
658
|
+
raise Puppet::Error, "Cannot replace existing certificate"
|
659
|
+
end
|
660
|
+
|
661
|
+
args = {
|
662
|
+
:name => self.certname,
|
663
|
+
:days => @days,
|
664
|
+
:issuer => nil,
|
665
|
+
:serial => 0x0,
|
666
|
+
:publickey => @key.public_key
|
667
|
+
}
|
668
|
+
if @type
|
669
|
+
args[:type] = @type
|
670
|
+
else
|
671
|
+
args[:type] = :server
|
672
|
+
end
|
673
|
+
@cert = SSLCertificates.mkcert(args)
|
674
|
+
|
675
|
+
@cert.sign(@key, OpenSSL::Digest::SHA1.new) if @selfsign
|
676
|
+
|
677
|
+
return @cert
|
678
|
+
end
|
679
|
+
|
680
|
+
def subject(string = false)
|
681
|
+
subj = @@params2names.collect { |param, name|
|
682
|
+
if @params.include?(param)
|
683
|
+
[name, @params[param]]
|
684
|
+
end
|
685
|
+
}.reject { |ary| ary.nil? }
|
686
|
+
|
687
|
+
if string
|
688
|
+
return "/" + subj.collect { |ary|
|
689
|
+
"%s=%s" % ary
|
690
|
+
}.join("/") + "/"
|
691
|
+
else
|
692
|
+
return subj
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
# verify that we can track down the cert chain or whatever
|
697
|
+
def verify
|
698
|
+
"openssl verify -verbose -CAfile /home/luke/.puppet/ssl/certs/ca.pem -purpose sslserver culain.madstop.com.pem"
|
699
|
+
end
|
700
|
+
|
701
|
+
def write
|
702
|
+
files = {
|
703
|
+
@certfile => @cert,
|
704
|
+
@keyfile => @key,
|
705
|
+
}
|
706
|
+
if defined? @cacert
|
707
|
+
files[@cacertfile] = @cacert
|
708
|
+
end
|
709
|
+
|
710
|
+
files.each { |file,thing|
|
711
|
+
if defined? thing and thing
|
712
|
+
if FileTest.exists?(file)
|
713
|
+
next
|
714
|
+
end
|
715
|
+
|
716
|
+
text = nil
|
717
|
+
|
718
|
+
if thing.is_a?(OpenSSL::PKey::RSA) and @password
|
719
|
+
text = thing.export(
|
720
|
+
OpenSSL::Cipher::DES.new(:EDE3, :CBC),
|
721
|
+
@password
|
722
|
+
)
|
723
|
+
else
|
724
|
+
text = thing.to_pem
|
725
|
+
end
|
726
|
+
|
727
|
+
File.open(file, "w", 0660) { |f| f.print text }
|
728
|
+
end
|
729
|
+
}
|
730
|
+
|
731
|
+
if defined? @cacert
|
732
|
+
SSLCertificates.mkhash(Puppet[:certdir], @cacert, @cacertfile)
|
733
|
+
end
|
734
|
+
end
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|