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.

Files changed (149) hide show
  1. data/CHANGELOG +57 -0
  2. data/Rakefile +38 -410
  3. data/bin/puppet +14 -12
  4. data/bin/puppetca +1 -3
  5. data/bin/puppetd +25 -7
  6. data/bin/puppetdoc +161 -104
  7. data/bin/puppetmasterd +4 -4
  8. data/conf/epm.list +8 -0
  9. data/conf/redhat/client.init +6 -1
  10. data/conf/redhat/no-chuser-0.15.1.patch +38 -0
  11. data/conf/redhat/puppet.spec +20 -5
  12. data/conf/redhat/puppetd.conf +1 -1
  13. data/conf/redhat/puppetmasterd.conf +1 -1
  14. data/conf/redhat/server.init +2 -4
  15. data/examples/code/snippets/{casestatement → casestatement.pp} +12 -1
  16. data/examples/code/snippets/selectorvalues.pp +15 -0
  17. data/examples/code/snippets/singleselector.pp +22 -0
  18. data/examples/code/snippets/tag.pp +9 -0
  19. data/ext/module_puppet +1 -1
  20. data/install.rb +303 -303
  21. data/lib/puppet.rb +7 -9
  22. data/lib/puppet/client.rb +18 -5
  23. data/lib/puppet/client/dipper.rb +12 -10
  24. data/lib/puppet/client/master.rb +113 -41
  25. data/lib/puppet/client/pelement.rb +20 -0
  26. data/lib/puppet/config.rb +113 -6
  27. data/lib/puppet/element.rb +1 -3
  28. data/lib/puppet/event.rb +12 -23
  29. data/lib/puppet/filetype.rb +93 -5
  30. data/lib/puppet/inifile.rb +201 -0
  31. data/lib/puppet/log.rb +18 -6
  32. data/lib/puppet/parameter.rb +80 -29
  33. data/lib/puppet/parser/ast.rb +6 -4
  34. data/lib/puppet/parser/ast/caseopt.rb +13 -4
  35. data/lib/puppet/parser/ast/casestatement.rb +2 -2
  36. data/lib/puppet/parser/ast/component.rb +4 -14
  37. data/lib/puppet/parser/ast/hostclass.rb +1 -1
  38. data/lib/puppet/parser/ast/leaf.rb +12 -0
  39. data/lib/puppet/parser/ast/node.rb +4 -4
  40. data/lib/puppet/parser/ast/objectdef.rb +5 -51
  41. data/lib/puppet/parser/ast/selector.rb +2 -0
  42. data/lib/puppet/parser/ast/tag.rb +26 -0
  43. data/lib/puppet/parser/interpreter.rb +89 -74
  44. data/lib/puppet/parser/lexer.rb +4 -3
  45. data/lib/puppet/parser/parser.rb +440 -378
  46. data/lib/puppet/parser/scope.rb +844 -887
  47. data/lib/puppet/server.rb +12 -1
  48. data/lib/puppet/server/authconfig.rb +166 -0
  49. data/lib/puppet/server/authstore.rb +8 -6
  50. data/lib/puppet/server/ca.rb +23 -26
  51. data/lib/puppet/server/filebucket.rb +24 -23
  52. data/lib/puppet/server/fileserver.rb +116 -47
  53. data/lib/puppet/server/master.rb +58 -19
  54. data/lib/puppet/server/pelement.rb +176 -0
  55. data/lib/puppet/server/rights.rb +78 -0
  56. data/lib/puppet/server/servlet.rb +19 -6
  57. data/lib/puppet/sslcertificates.rb +4 -2
  58. data/lib/puppet/sslcertificates/ca.rb +66 -34
  59. data/lib/puppet/storage.rb +20 -26
  60. data/lib/puppet/transaction.rb +49 -92
  61. data/lib/puppet/type.rb +142 -35
  62. data/lib/puppet/type/cron.rb +29 -14
  63. data/lib/puppet/type/exec.rb +92 -35
  64. data/lib/puppet/type/group.rb +29 -11
  65. data/lib/puppet/type/nameservice.rb +50 -1
  66. data/lib/puppet/type/nameservice/netinfo.rb +68 -1
  67. data/lib/puppet/type/nameservice/objectadd.rb +1 -0
  68. data/lib/puppet/type/package.rb +150 -109
  69. data/lib/puppet/type/package/apple.rb +27 -0
  70. data/lib/puppet/type/package/apt.rb +1 -0
  71. data/lib/puppet/type/package/darwinport.rb +97 -0
  72. data/lib/puppet/type/package/dpkg.rb +10 -2
  73. data/lib/puppet/type/package/freebsd.rb +19 -0
  74. data/lib/puppet/type/package/{bsd.rb → openbsd.rb} +36 -7
  75. data/lib/puppet/type/package/ports.rb +98 -0
  76. data/lib/puppet/type/package/rpm.rb +43 -7
  77. data/lib/puppet/type/package/sun.rb +53 -36
  78. data/lib/puppet/type/package/yum.rb +5 -16
  79. data/lib/puppet/type/parsedtype.rb +41 -29
  80. data/lib/puppet/type/parsedtype/host.rb +13 -5
  81. data/lib/puppet/type/parsedtype/mount.rb +250 -0
  82. data/lib/puppet/type/parsedtype/port.rb +8 -6
  83. data/lib/puppet/type/pfile.rb +284 -30
  84. data/lib/puppet/type/pfile/checksum.rb +96 -68
  85. data/lib/puppet/type/pfile/content.rb +16 -13
  86. data/lib/puppet/type/pfile/ensure.rb +64 -126
  87. data/lib/puppet/type/pfile/group.rb +12 -5
  88. data/lib/puppet/type/pfile/mode.rb +16 -4
  89. data/lib/puppet/type/pfile/source.rb +47 -73
  90. data/lib/puppet/type/pfile/target.rb +81 -0
  91. data/lib/puppet/type/pfile/uid.rb +10 -3
  92. data/lib/puppet/type/pfilebucket.rb +12 -3
  93. data/lib/puppet/type/schedule.rb +5 -1
  94. data/lib/puppet/type/service.rb +138 -66
  95. data/lib/puppet/type/service/debian.rb +9 -3
  96. data/lib/puppet/type/service/init.rb +51 -56
  97. data/lib/puppet/type/service/smf.rb +16 -6
  98. data/lib/puppet/type/state.rb +71 -32
  99. data/lib/puppet/type/symlink.rb +12 -7
  100. data/lib/puppet/type/tidy.rb +5 -1
  101. data/lib/puppet/type/user.rb +116 -20
  102. data/lib/puppet/type/yumrepo.rb +314 -0
  103. data/lib/puppet/util.rb +84 -14
  104. data/test/client/client.rb +41 -18
  105. data/test/client/master.rb +50 -4
  106. data/test/executables/puppetbin.rb +31 -4
  107. data/test/executables/puppetca.rb +18 -2
  108. data/test/language/ast.rb +201 -31
  109. data/test/language/interpreter.rb +8 -2
  110. data/test/{parser → language}/lexer.rb +1 -1
  111. data/test/language/node.rb +84 -0
  112. data/test/{parser → language}/parser.rb +1 -1
  113. data/test/language/scope.rb +101 -2
  114. data/test/language/snippets.rb +23 -2
  115. data/test/other/config.rb +99 -1
  116. data/test/other/filetype.rb +95 -0
  117. data/test/other/inifile.rb +114 -0
  118. data/test/other/log.rb +3 -2
  119. data/test/other/transactions.rb +55 -10
  120. data/test/puppet/utiltest.rb +25 -1
  121. data/test/puppettest.rb +140 -46
  122. data/test/server/authconfig.rb +56 -0
  123. data/test/server/bucket.rb +32 -18
  124. data/test/server/fileserver.rb +75 -30
  125. data/test/server/master.rb +27 -4
  126. data/test/server/pelement.rb +298 -0
  127. data/test/server/rights.rb +41 -0
  128. data/test/server/server.rb +2 -2
  129. data/test/tagging/tagging.rb +100 -1
  130. data/test/types/basic.rb +3 -3
  131. data/test/types/cron.rb +24 -1
  132. data/test/types/exec.rb +99 -1
  133. data/test/types/file.rb +298 -2
  134. data/test/types/filebucket.rb +4 -15
  135. data/test/types/filesources.rb +43 -14
  136. data/test/types/group.rb +1 -13
  137. data/test/types/mount.rb +277 -0
  138. data/test/types/package.rb +164 -33
  139. data/test/types/parameter.rb +107 -0
  140. data/test/types/port.rb +2 -1
  141. data/test/types/service.rb +37 -2
  142. data/test/types/state.rb +92 -0
  143. data/test/types/symlink.rb +30 -2
  144. data/test/types/tidy.rb +2 -14
  145. data/test/types/type.rb +35 -1
  146. data/test/types/user.rb +110 -1
  147. data/test/types/yumrepo.rb +95 -0
  148. metadata +316 -290
  149. data/test/types/filetype.rb +0 -160
@@ -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
- # FIXME this should all be s/:File/:Manifest/g or something
39
- # build our AST
40
- @file = hash[:File] || Puppet[:manifest]
41
- hash.delete(:File)
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
- @parsecheck = hash[:FileTimeout] || 15
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
- Puppet.notice("Compiling configuration for %s" % client)
114
- begin
115
- retobjects = @interpreter.run(client, facts)
116
- rescue Puppet::Error => detail
117
- Puppet.err detail
118
- raise XMLRPC::FaultException.new(
119
- 1, detail.to_s
120
- )
121
- rescue => detail
122
- Puppet.err detail.to_s
123
- return ""
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 none others.
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
- Servlet.log "Allowing %s(%s) trusted access to %s" %
44
- [client, ip, method]
45
- return true
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
- #Puppet.info server.inspect
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 = "%x" % cert.issuer.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 966 2006-03-02 17:12:26Z luke $
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 => ["$cadir/ca_pub.pem", "The CA public key."],
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 => ["$cadir/requests",
24
- "Where the CA stores certificate requests"],
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 => ["$cadir/serial",
34
- "Where the serial number for certificates is stored."],
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
- File.open(@config[:serial], "w") { |f|
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
- File.open(@config[:capass], "w", 0600) { |f| f.print pass }
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]).reject { |file|
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 => "CAcert",
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
- @cert = cert.mkselfsigned
169
- File.open(@config[:cacert], "w", 0660) { |f|
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
- File.open(@config[:serial], "w") { |f|
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
- File.open(csrfile, "w", 0660) { |f|
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
- File.open(certfile, "w", 0660) { |f|
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 965 2006-03-02 07:30:14Z luke $
333
+ # $Id: ca.rb 1117 2006-04-19 15:35:04Z luke $