puppet 2.6.11 → 2.6.12

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 (67) hide show
  1. data/CHANGELOG +33 -0
  2. data/conf/redhat/puppet.spec +7 -4
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application/cert.rb +17 -3
  5. data/lib/puppet/application/kick.rb +0 -2
  6. data/lib/puppet/defaults.rb +52 -3
  7. data/lib/puppet/network/handler/ca.rb +16 -106
  8. data/lib/puppet/network/handler/master.rb +0 -3
  9. data/lib/puppet/network/handler/runner.rb +1 -0
  10. data/lib/puppet/ssl/certificate.rb +6 -0
  11. data/lib/puppet/ssl/certificate_authority.rb +86 -11
  12. data/lib/puppet/ssl/certificate_authority/interface.rb +64 -19
  13. data/lib/puppet/ssl/certificate_factory.rb +112 -91
  14. data/lib/puppet/ssl/certificate_request.rb +88 -1
  15. data/lib/puppet/ssl/host.rb +16 -3
  16. data/lib/puppet/type/file.rb +0 -1
  17. data/lib/puppet/util/command_line/puppetca +23 -2
  18. data/lib/puppet/util/monkey_patches.rb +69 -0
  19. data/lib/puppet/util/settings.rb +5 -0
  20. data/spec/integration/defaults_spec.rb +11 -0
  21. data/spec/integration/network/handler_spec.rb +1 -1
  22. data/spec/unit/configurer_spec.rb +2 -2
  23. data/spec/unit/network/handler/ca_spec.rb +86 -0
  24. data/spec/unit/ssl/certificate_authority/interface_spec.rb +92 -53
  25. data/spec/unit/ssl/certificate_authority_spec.rb +133 -23
  26. data/spec/unit/ssl/certificate_factory_spec.rb +90 -70
  27. data/spec/unit/ssl/certificate_request_spec.rb +62 -1
  28. data/spec/unit/ssl/certificate_spec.rb +31 -0
  29. data/spec/unit/ssl/host_spec.rb +44 -2
  30. data/spec/unit/util/settings_spec.rb +10 -0
  31. data/test/language/functions.rb +0 -1
  32. data/test/language/snippets.rb +0 -9
  33. data/test/lib/puppettest/exetest.rb +1 -1
  34. data/test/lib/puppettest/servertest.rb +0 -1
  35. data/test/rails/rails.rb +0 -1
  36. data/test/ral/type/filesources.rb +0 -60
  37. metadata +5 -34
  38. data/lib/puppet/network/client.rb +0 -179
  39. data/lib/puppet/network/client/ca.rb +0 -56
  40. data/lib/puppet/network/client/file.rb +0 -6
  41. data/lib/puppet/network/client/proxy.rb +0 -27
  42. data/lib/puppet/network/client/report.rb +0 -26
  43. data/lib/puppet/network/client/runner.rb +0 -10
  44. data/lib/puppet/network/client/status.rb +0 -4
  45. data/lib/puppet/network/http_server.rb +0 -3
  46. data/lib/puppet/network/http_server/mongrel.rb +0 -150
  47. data/lib/puppet/network/http_server/webrick.rb +0 -155
  48. data/lib/puppet/network/xmlrpc/client.rb +0 -211
  49. data/lib/puppet/sslcertificates.rb +0 -146
  50. data/lib/puppet/sslcertificates/ca.rb +0 -375
  51. data/lib/puppet/sslcertificates/certificate.rb +0 -255
  52. data/lib/puppet/sslcertificates/inventory.rb +0 -38
  53. data/lib/puppet/sslcertificates/monkey_patch.rb +0 -6
  54. data/lib/puppet/sslcertificates/support.rb +0 -146
  55. data/spec/integration/network/client_spec.rb +0 -19
  56. data/spec/unit/network/client_spec.rb +0 -45
  57. data/spec/unit/network/xmlrpc/client_spec.rb +0 -172
  58. data/spec/unit/sslcertificates/ca_spec.rb +0 -110
  59. data/test/certmgr/certmgr.rb +0 -308
  60. data/test/certmgr/inventory.rb +0 -69
  61. data/test/certmgr/support.rb +0 -105
  62. data/test/network/client/ca.rb +0 -69
  63. data/test/network/client/dipper.rb +0 -34
  64. data/test/network/handler/ca.rb +0 -273
  65. data/test/network/server/mongrel_test.rb +0 -99
  66. data/test/network/server/webrick.rb +0 -128
  67. data/test/network/xmlrpc/client.rb +0 -45
@@ -1,211 +0,0 @@
1
- require 'puppet/sslcertificates'
2
- require 'puppet/network/http_pool'
3
- require 'openssl'
4
- require 'puppet/external/base64'
5
-
6
- require 'xmlrpc/client'
7
- require 'net/https'
8
- require 'yaml'
9
-
10
- module Puppet::Network
11
- class ClientError < Puppet::Error; end
12
- class XMLRPCClientError < Puppet::Error; end
13
- class XMLRPCClient < ::XMLRPC::Client
14
-
15
- attr_accessor :puppet_server, :puppet_port
16
- @clients = {}
17
-
18
- class << self
19
- include Puppet::Util
20
- include Puppet::Util::ClassGen
21
- end
22
-
23
- # Create a netclient for each handler
24
- def self.mkclient(handler)
25
- interface = handler.interface
26
- namespace = interface.prefix
27
-
28
- # Create a subclass for every client type. This is
29
- # so that all of the methods are on their own class,
30
- # so that their namespaces can define the same methods if
31
- # they want.
32
- constant = handler.name.to_s.capitalize
33
- name = namespace.downcase
34
- newclient = genclass(name, :hash => @clients, :constant => constant)
35
-
36
- interface.methods.each { |ary|
37
- method = ary[0]
38
- newclient.send(:define_method,method) { |*args|
39
- make_rpc_call(namespace, method, *args)
40
- }
41
- }
42
-
43
- newclient
44
- end
45
-
46
- def self.handler_class(handler)
47
- @clients[handler] || self.mkclient(handler)
48
- end
49
-
50
- class ErrorHandler
51
- def initialize(&block)
52
- singleton_class.define_method(:execute, &block)
53
- end
54
- end
55
-
56
- # Use a class variable so all subclasses have access to it.
57
- @@error_handlers = {}
58
-
59
- def self.error_handler(exception)
60
- if handler = @@error_handlers[exception.class]
61
- return handler
62
- else
63
- return @@error_handlers[:default]
64
- end
65
- end
66
-
67
- def self.handle_error(*exceptions, &block)
68
- handler = ErrorHandler.new(&block)
69
-
70
- exceptions.each do |exception|
71
- @@error_handlers[exception] = handler
72
- end
73
- end
74
-
75
- handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
76
- if detail.message =~ /bad write retry/
77
- Puppet.warning "Transient SSL write error; restarting connection and retrying"
78
- client.recycle_connection
79
- return :retry
80
- end
81
- ["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
82
- Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
83
- end
84
- raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
85
- end
86
-
87
- handle_error(:default) do |client, detail, namespace, method|
88
- if detail.message.to_s =~ /^Wrong size\. Was \d+, should be \d+$/
89
- Puppet.warning "XMLRPC returned wrong size. Retrying."
90
- return :retry
91
- end
92
- Puppet.err "Could not call #{namespace}.#{method}: #{detail.inspect}"
93
- error = XMLRPCClientError.new(detail.to_s)
94
- error.set_backtrace detail.backtrace
95
- raise error
96
- end
97
-
98
- handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
99
- if detail.message =~ /bad write retry/
100
- Puppet.warning "Transient SSL write error; restarting connection and retrying"
101
- client.recycle_connection
102
- return :retry
103
- end
104
- ["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
105
- Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
106
- end
107
- raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
108
- end
109
-
110
- handle_error(::XMLRPC::FaultException) do |client, detail, namespace, method|
111
- raise XMLRPCClientError, detail.faultString
112
- end
113
-
114
- handle_error(Errno::ECONNREFUSED) do |client, detail, namespace, method|
115
- msg = "Could not connect to #{client.host} on port #{client.port}"
116
- raise XMLRPCClientError, msg
117
- end
118
-
119
- handle_error(SocketError) do |client, detail, namespace, method|
120
- Puppet.err "Could not find server #{@host}: #{detail}"
121
- error = XMLRPCClientError.new("Could not find server #{client.host}")
122
- error.set_backtrace detail.backtrace
123
- raise error
124
- end
125
-
126
- handle_error(Errno::EPIPE, EOFError) do |client, detail, namespace, method|
127
- Puppet.info "Other end went away; restarting connection and retrying"
128
- client.recycle_connection
129
- return :retry
130
- end
131
-
132
- handle_error(Timeout::Error) do |client, detail, namespace, method|
133
- Puppet.err "Connection timeout calling #{namespace}.#{method}: #{detail}"
134
- error = XMLRPCClientError.new("Connection Timeout")
135
- error.set_backtrace(detail.backtrace)
136
- raise error
137
- end
138
-
139
- def make_rpc_call(namespace, method, *args)
140
- Puppet.debug "Calling #{namespace}.#{method}"
141
- begin
142
- call("#{namespace}.#{method}",*args)
143
- rescue SystemExit,NoMemoryError
144
- raise
145
- rescue Exception => detail
146
- retry if self.class.error_handler(detail).execute(self, detail, namespace, method) == :retry
147
- end
148
- ensure
149
- http.finish if http.started?
150
- end
151
-
152
- def http
153
- @http ||= Puppet::Network::HttpPool.http_instance(host, port, true)
154
- end
155
-
156
- attr_reader :host, :port
157
-
158
- def initialize(hash = {})
159
- hash[:Path] ||= "/RPC2"
160
- hash[:Server] ||= Puppet[:server]
161
- hash[:Port] ||= Puppet[:masterport]
162
- hash[:HTTPProxyHost] ||= Puppet[:http_proxy_host]
163
- hash[:HTTPProxyPort] ||= Puppet[:http_proxy_port]
164
-
165
- if "none" == hash[:HTTPProxyHost]
166
- hash[:HTTPProxyHost] = nil
167
- hash[:HTTPProxyPort] = nil
168
- end
169
-
170
-
171
- super(
172
-
173
- hash[:Server],
174
- hash[:Path],
175
- hash[:Port],
176
- hash[:HTTPProxyHost],
177
- hash[:HTTPProxyPort],
178
-
179
- nil, # user
180
- nil, # password
181
- true, # use_ssl
182
- Puppet[:configtimeout] # use configured timeout (#1176)
183
- )
184
- @http = Puppet::Network::HttpPool.http_instance(@host, @port)
185
- end
186
-
187
- # Get rid of our existing connection, replacing it with a new one.
188
- # This should only happen if we lose our connection somehow (e.g., an EPIPE)
189
- # or we've just downloaded certs and we need to create new http instances
190
- # with the certs added.
191
- def recycle_connection
192
- http.finish if http.started?
193
- @http = nil
194
- self.http # force a new one
195
- end
196
-
197
- def start
198
- @http.start unless @http.started?
199
- rescue => detail
200
- Puppet.err "Could not connect to server: #{detail}"
201
- end
202
-
203
- def local
204
- false
205
- end
206
-
207
- def local?
208
- false
209
- end
210
- end
211
- end
@@ -1,146 +0,0 @@
1
- # The library for manipulating SSL certs.
2
-
3
- require 'puppet'
4
-
5
- raise Puppet::Error, "You must have the Ruby openssl library installed" unless Puppet.features.openssl?
6
-
7
- module Puppet::SSLCertificates
8
- #def self.mkcert(type, name, dnsnames, ttl, issuercert, issuername, serial, publickey)
9
- def self.mkcert(hash)
10
- [:type, :name, :ttl, :issuer, :serial, :publickey].each { |param|
11
- raise ArgumentError, "mkcert called without #{param}" unless hash.include?(param)
12
- }
13
-
14
- cert = OpenSSL::X509::Certificate.new
15
- # Make the certificate valid as of yesterday, because
16
- # so many people's clocks are out of sync.
17
- from = Time.now - (60*60*24)
18
-
19
- cert.subject = hash[:name]
20
- if hash[:issuer]
21
- cert.issuer = hash[:issuer].subject
22
- else
23
- # we're a self-signed cert
24
- cert.issuer = hash[:name]
25
- end
26
- cert.not_before = from
27
- cert.not_after = from + hash[:ttl]
28
- cert.version = 2 # X509v3
29
-
30
- cert.public_key = hash[:publickey]
31
- cert.serial = hash[:serial]
32
-
33
- basic_constraint = nil
34
- key_usage = nil
35
- ext_key_usage = nil
36
- subject_alt_name = []
37
-
38
- ef = OpenSSL::X509::ExtensionFactory.new
39
-
40
- ef.subject_certificate = cert
41
-
42
- if hash[:issuer]
43
- ef.issuer_certificate = hash[:issuer]
44
- else
45
- ef.issuer_certificate = cert
46
- end
47
-
48
- ex = []
49
- case hash[:type]
50
- when :ca
51
- basic_constraint = "CA:TRUE"
52
- key_usage = %w{cRLSign keyCertSign}
53
- when :terminalsubca
54
- basic_constraint = "CA:TRUE,pathlen:0"
55
- key_usage = %w{cRLSign keyCertSign}
56
- when :server
57
- basic_constraint = "CA:FALSE"
58
- dnsnames = Puppet[:certdnsnames]
59
- name = hash[:name].to_s.sub(%r{/CN=},'')
60
- if dnsnames != ""
61
- dnsnames.split(':').each { |d| subject_alt_name << 'DNS:' + d }
62
- subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
63
- elsif name == Facter.value(:fqdn) # we're a CA server, and thus probably the server
64
- subject_alt_name << 'DNS:' + "puppet" # Add 'puppet' as an alias
65
- subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
66
- subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias
67
- end
68
- key_usage = %w{digitalSignature keyEncipherment}
69
- ext_key_usage = %w{serverAuth clientAuth emailProtection}
70
- when :ocsp
71
- basic_constraint = "CA:FALSE"
72
- key_usage = %w{nonRepudiation digitalSignature}
73
- ext_key_usage = %w{serverAuth OCSPSigning}
74
- when :client
75
- basic_constraint = "CA:FALSE"
76
- key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
77
- ext_key_usage = %w{clientAuth emailProtection}
78
- ex << ef.create_extension("nsCertType", "client,email")
79
- else
80
- raise Puppet::Error, "unknown cert type '#{hash[:type]}'"
81
- end
82
-
83
-
84
- ex << ef.create_extension(
85
- "nsComment",
86
-
87
- "Puppet Ruby/OpenSSL Generated Certificate")
88
- ex << ef.create_extension("basicConstraints", basic_constraint, true)
89
- ex << ef.create_extension("subjectKeyIdentifier", "hash")
90
-
91
- ex << ef.create_extension("keyUsage", key_usage.join(",")) if key_usage
92
- ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) if ext_key_usage
93
- ex << ef.create_extension("subjectAltName", subject_alt_name.join(",")) if ! subject_alt_name.empty?
94
-
95
- #if @ca_config[:cdp_location] then
96
- # ex << ef.create_extension("crlDistributionPoints",
97
- # @ca_config[:cdp_location])
98
- #end
99
-
100
- #if @ca_config[:ocsp_location] then
101
- # ex << ef.create_extension("authorityInfoAccess",
102
- # "OCSP;" << @ca_config[:ocsp_location])
103
- #end
104
- cert.extensions = ex
105
-
106
- # for some reason this _must_ be the last extension added
107
- ex << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if hash[:type] == :ca
108
-
109
- cert
110
- end
111
-
112
- def self.mkhash(dir, cert, certfile)
113
- # Make sure the hash is zero-padded to 8 chars
114
- hash = "%08x" % cert.issuer.hash
115
- hashpath = nil
116
- 10.times { |i|
117
- path = File.join(dir, "#{hash}.#{i}")
118
- if FileTest.exists?(path)
119
- if FileTest.symlink?(path)
120
- dest = File.readlink(path)
121
- if dest == certfile
122
- # the correct link already exists
123
- hashpath = path
124
- break
125
- else
126
- next
127
- end
128
- else
129
- next
130
- end
131
- end
132
-
133
- File.symlink(certfile, path)
134
-
135
- hashpath = path
136
- break
137
- }
138
-
139
-
140
- hashpath
141
- end
142
- require 'puppet/sslcertificates/certificate'
143
- require 'puppet/sslcertificates/inventory'
144
- require 'puppet/sslcertificates/ca'
145
- end
146
-
@@ -1,375 +0,0 @@
1
- require 'sync'
2
-
3
- class Puppet::SSLCertificates::CA
4
- include Puppet::Util::Warnings
5
-
6
- Certificate = Puppet::SSLCertificates::Certificate
7
- attr_accessor :keyfile, :file, :config, :dir, :cert, :crl
8
-
9
- def certfile
10
- @config[:cacert]
11
- end
12
-
13
- # Remove all traces of a given host. This is kind of hackish, but, eh.
14
- def clean(host)
15
- host = host.downcase
16
- [:csrdir, :signeddir, :publickeydir, :privatekeydir, :certdir].each do |name|
17
- dir = Puppet[name]
18
-
19
- file = File.join(dir, host + ".pem")
20
-
21
- if FileTest.exists?(file)
22
- begin
23
- if Puppet[:name] == "cert"
24
- puts "Removing #{file}"
25
- else
26
- Puppet.info "Removing #{file}"
27
- end
28
- File.unlink(file)
29
- rescue => detail
30
- raise Puppet::Error, "Could not delete #{file}: #{detail}"
31
- end
32
- end
33
-
34
- end
35
- end
36
-
37
- def host2csrfile(hostname)
38
- File.join(Puppet[:csrdir], [hostname.downcase, "pem"].join("."))
39
- end
40
-
41
- # this stores signed certs in a directory unrelated to
42
- # normal client certs
43
- def host2certfile(hostname)
44
- File.join(Puppet[:signeddir], [hostname.downcase, "pem"].join("."))
45
- end
46
-
47
- # Turn our hostname into a Name object
48
- def thing2name(thing)
49
- thing.subject.to_a.find { |ary|
50
- ary[0] == "CN"
51
- }[1]
52
- end
53
-
54
- def initialize(hash = {})
55
- Puppet.settings.use(:main, :ca, :ssl)
56
- self.setconfig(hash)
57
-
58
- if Puppet[:capass]
59
- if FileTest.exists?(Puppet[:capass])
60
- #puts "Reading #{Puppet[:capass]}"
61
- #system "ls -al #{Puppet[:capass]}"
62
- #File.read Puppet[:capass]
63
- @config[:password] = self.getpass
64
- else
65
- # Don't create a password if the cert already exists
66
- @config[:password] = self.genpass unless FileTest.exists?(@config[:cacert])
67
- end
68
- end
69
-
70
- self.getcert
71
- init_crl
72
- unless FileTest.exists?(@config[:serial])
73
- Puppet.settings.write(:serial) do |f|
74
- f << "%04X" % 1
75
- end
76
- end
77
- end
78
-
79
- # Generate a new password for the CA.
80
- def genpass
81
- pass = ""
82
- 20.times { pass += (rand(74) + 48).chr }
83
-
84
- begin
85
- Puppet.settings.write(:capass) { |f| f.print pass }
86
- rescue Errno::EACCES => detail
87
- raise Puppet::Error, detail.to_s
88
- end
89
- pass
90
- end
91
-
92
- # Get the CA password.
93
- def getpass
94
- if @config[:capass] and File.readable?(@config[:capass])
95
- return File.read(@config[:capass])
96
- else
97
- raise Puppet::Error, "Could not decrypt CA key with password: #{detail}"
98
- end
99
- end
100
-
101
- # Get the CA cert.
102
- def getcert
103
- if FileTest.exists?(@config[:cacert])
104
- @cert = OpenSSL::X509::Certificate.new(
105
- File.read(@config[:cacert])
106
- )
107
- else
108
- self.mkrootcert
109
- end
110
- end
111
-
112
- # Retrieve a client's CSR.
113
- def getclientcsr(host)
114
- csrfile = host2csrfile(host)
115
- return nil unless File.exists?(csrfile)
116
-
117
- OpenSSL::X509::Request.new(File.read(csrfile))
118
- end
119
-
120
- # Retrieve a client's certificate.
121
- def getclientcert(host)
122
- certfile = host2certfile(host)
123
- return [nil, nil] unless File.exists?(certfile)
124
-
125
- [OpenSSL::X509::Certificate.new(File.read(certfile)), @cert]
126
- end
127
-
128
- # List certificates waiting to be signed. This returns a list of hostnames, not actual
129
- # files -- the names can be converted to full paths with host2csrfile.
130
- def list(dummy_argument=:work_arround_for_ruby_GC_bug)
131
- return Dir.entries(Puppet[:csrdir]).find_all { |file|
132
- file =~ /\.pem$/
133
- }.collect { |file|
134
- file.sub(/\.pem$/, '')
135
- }
136
- end
137
-
138
- # List signed certificates. This returns a list of hostnames, not actual
139
- # files -- the names can be converted to full paths with host2csrfile.
140
- def list_signed(dummy_argument=:work_arround_for_ruby_GC_bug)
141
- return Dir.entries(Puppet[:signeddir]).find_all { |file|
142
- file =~ /\.pem$/
143
- }.collect { |file|
144
- file.sub(/\.pem$/, '')
145
- }
146
- end
147
-
148
- # Create the root certificate.
149
- def mkrootcert
150
- # Make the root cert's name "Puppet CA: " plus the FQDN of the host running the CA.
151
- name = "Puppet CA: #{Facter["hostname"].value}"
152
- if domain = Facter["domain"].value
153
- name += ".#{domain}"
154
- end
155
-
156
- cert = Certificate.new(
157
- :name => name,
158
- :cert => @config[:cacert],
159
- :encrypt => @config[:capass],
160
- :key => @config[:cakey],
161
- :selfsign => true,
162
- :ttl => ttl,
163
- :type => :ca
164
- )
165
-
166
- # This creates the cakey file
167
- Puppet::Util::SUIDManager.asuser(Puppet[:user], Puppet[:group]) do
168
- @cert = cert.mkselfsigned
169
- end
170
- Puppet.settings.write(:cacert) do |f|
171
- f.puts @cert.to_pem
172
- end
173
- Puppet.settings.write(:capub) do |f|
174
- f.puts @cert.public_key
175
- end
176
- cert
177
- end
178
-
179
- def removeclientcsr(host)
180
- csrfile = host2csrfile(host)
181
- raise Puppet::Error, "No certificate request for #{host}" unless File.exists?(csrfile)
182
-
183
- File.unlink(csrfile)
184
- end
185
-
186
- # Revoke the certificate with serial number SERIAL issued by this
187
- # CA. The REASON must be one of the OpenSSL::OCSP::REVOKED_* reasons
188
- def revoke(serial, reason = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)
189
- time = Time.now
190
- revoked = OpenSSL::X509::Revoked.new
191
- revoked.serial = serial
192
- revoked.time = time
193
- enum = OpenSSL::ASN1::Enumerated(reason)
194
- ext = OpenSSL::X509::Extension.new("CRLReason", enum)
195
- revoked.add_extension(ext)
196
- @crl.add_revoked(revoked)
197
- store_crl
198
- end
199
-
200
- # Take the Puppet config and store it locally.
201
- def setconfig(hash)
202
- @config = {}
203
- Puppet.settings.params("ca").each { |param|
204
- param = param.intern if param.is_a? String
205
- if hash.include?(param)
206
- @config[param] = hash[param]
207
- Puppet[param] = hash[param]
208
- hash.delete(param)
209
- else
210
- @config[param] = Puppet[param]
211
- end
212
- }
213
-
214
- if hash.include?(:password)
215
- @config[:password] = hash[:password]
216
- hash.delete(:password)
217
- end
218
-
219
- raise ArgumentError, "Unknown parameters #{hash.keys.join(",")}" if hash.length > 0
220
-
221
- [:cadir, :csrdir, :signeddir].each { |dir|
222
- raise Puppet::DevError, "#{dir} is undefined" unless @config[dir]
223
- }
224
- end
225
-
226
- # Sign a given certificate request.
227
- def sign(csr)
228
- unless csr.is_a?(OpenSSL::X509::Request)
229
- raise Puppet::Error,
230
- "CA#sign only accepts OpenSSL::X509::Request objects, not #{csr.class}"
231
- end
232
-
233
- raise Puppet::Error, "CSR sign verification failed" unless csr.verify(csr.public_key)
234
-
235
- serial = nil
236
- Puppet.settings.readwritelock(:serial) { |f|
237
- serial = File.read(@config[:serial]).chomp.hex
238
- # increment the serial
239
- f << "%04X" % (serial + 1)
240
- }
241
-
242
- newcert = Puppet::SSLCertificates.mkcert(
243
- :type => :server,
244
- :name => csr.subject,
245
- :ttl => ttl,
246
- :issuer => @cert,
247
- :serial => serial,
248
- :publickey => csr.public_key
249
- )
250
-
251
- sign_with_key(newcert)
252
-
253
- self.storeclientcert(newcert)
254
-
255
- [newcert, @cert]
256
- end
257
-
258
- # Store the client's CSR for later signing. This is called from
259
- # server/ca.rb, and the CSRs are deleted once the certificate is actually
260
- # signed.
261
- def storeclientcsr(csr)
262
- host = thing2name(csr)
263
-
264
- csrfile = host2csrfile(host)
265
- raise Puppet::Error, "Certificate request for #{host} already exists" if File.exists?(csrfile)
266
-
267
- Puppet.settings.writesub(:csrdir, csrfile) do |f|
268
- f.print csr.to_pem
269
- end
270
- end
271
-
272
- # Store the certificate that we generate.
273
- def storeclientcert(cert)
274
- host = thing2name(cert)
275
-
276
- certfile = host2certfile(host)
277
- Puppet.notice "Overwriting signed certificate #{certfile} for #{host}" if File.exists?(certfile)
278
-
279
- Puppet::SSLCertificates::Inventory::add(cert)
280
- Puppet.settings.writesub(:signeddir, certfile) do |f|
281
- f.print cert.to_pem
282
- end
283
- end
284
-
285
- # TTL for new certificates in seconds. If config param :ca_ttl is set,
286
- # use that, otherwise use :ca_days for backwards compatibility
287
- def ttl
288
- days = @config[:ca_days]
289
- if days && days.size > 0
290
- warnonce "Parameter ca_ttl is not set. Using depecated ca_days instead."
291
- return @config[:ca_days] * 24 * 60 * 60
292
- else
293
- ttl = @config[:ca_ttl]
294
- if ttl.is_a?(String)
295
- unless ttl =~ /^(\d+)(y|d|h|s)$/
296
- raise ArgumentError, "Invalid ca_ttl #{ttl}"
297
- end
298
- case $2
299
- when 'y'
300
- unit = 365 * 24 * 60 * 60
301
- when 'd'
302
- unit = 24 * 60 * 60
303
- when 'h'
304
- unit = 60 * 60
305
- when 's'
306
- unit = 1
307
- else
308
- raise ArgumentError, "Invalid unit for ca_ttl #{ttl}"
309
- end
310
- return $1.to_i * unit
311
- else
312
- return ttl
313
- end
314
- end
315
- end
316
-
317
- private
318
- def init_crl
319
- if FileTest.exists?(@config[:cacrl])
320
- @crl = OpenSSL::X509::CRL.new(
321
- File.read(@config[:cacrl])
322
- )
323
- else
324
- # Create new CRL
325
- @crl = OpenSSL::X509::CRL.new
326
- @crl.issuer = @cert.subject
327
- @crl.version = 1
328
- store_crl
329
- @crl
330
- end
331
- end
332
-
333
- def store_crl
334
- # Increment the crlNumber
335
- e = @crl.extensions.find { |e| e.oid == 'crlNumber' }
336
- ext = @crl.extensions.reject { |e| e.oid == 'crlNumber' }
337
- crlNum = OpenSSL::ASN1::Integer(e ? e.value.to_i + 1 : 0)
338
- ext << OpenSSL::X509::Extension.new("crlNumber", crlNum)
339
- @crl.extensions = ext
340
-
341
- # Set last/next update
342
- now = Time.now
343
- @crl.last_update = now
344
- # Keep CRL valid for 5 years
345
- @crl.next_update = now + 5 * 365*24*60*60
346
-
347
- sign_with_key(@crl)
348
- Puppet.settings.write(:cacrl) do |f|
349
- f.puts @crl.to_pem
350
- end
351
- end
352
-
353
- def sign_with_key(signable, digest = OpenSSL::Digest::SHA1.new)
354
- cakey = nil
355
- if @config[:password]
356
- begin
357
- cakey = OpenSSL::PKey::RSA.new(
358
- File.read(@config[:cakey]), @config[:password]
359
- )
360
- rescue
361
- raise Puppet::Error,
362
- "Decrypt of CA private key with password stored in @config[:capass] not possible"
363
- end
364
- else
365
- cakey = OpenSSL::PKey::RSA.new(
366
- File.read(@config[:cakey])
367
- )
368
- end
369
-
370
- raise Puppet::Error, "CA Certificate is invalid" unless @cert.check_private_key(cakey)
371
-
372
- signable.sign(cakey, digest)
373
- end
374
- end
375
-