class Puppet::Application::Ssl

Public Class Methods

new(command_line = Puppet::Util::CommandLine.new) click to toggle source
Calls superclass method Puppet::Application::new
   # File lib/puppet/application/ssl.rb
91 def initialize(command_line = Puppet::Util::CommandLine.new)
92   super(command_line)
93 
94   @cert_provider = Puppet::X509::CertProvider.new
95   @ssl_provider = Puppet::SSL::SSLProvider.new
96   @machine = Puppet::SSL::StateMachine.new
97   @session = Puppet.runtime[:http].create_session
98 end

Public Instance Methods

clean(certname) click to toggle source
    # File lib/puppet/application/ssl.rb
234   def clean(certname)
235     # make sure cert has been removed from the CA
236     if certname == Puppet[:ca_server]
237       cert = nil
238 
239       begin
240         ssl_context = @machine.ensure_ca_certificates
241         route = create_route(ssl_context)
242         _, cert = route.get_certificate(certname, ssl_context: ssl_context)
243       rescue Puppet::HTTP::ResponseError => e
244         if e.response.code.to_i != 404
245           raise Puppet::Error.new(_("Failed to connect to the CA to determine if certificate %{certname} has been cleaned") % { certname: certname }, e)
246         end
247       rescue => e
248         raise Puppet::Error.new(_("Failed to connect to the CA to determine if certificate %{certname} has been cleaned") % { certname: certname }, e)
249       end
250 
251       if cert
252         raise Puppet::Error, _(<<END) % { certname: certname }
253 The certificate %{certname} must be cleaned from the CA first. To fix this,
254 run the following commands on the CA:
255   puppetserver ca clean --certname %{certname}
256   puppet ssl clean
257 END
258       end
259     end
260 
261     paths = {
262       'private key' => Puppet[:hostprivkey],
263       'public key'  => Puppet[:hostpubkey],
264       'certificate request' => Puppet[:hostcsr],
265       'certificate' => Puppet[:hostcert],
266       'private key password file' => Puppet[:passfile]
267     }
268     if options[:localca]
269       paths['local CA certificate'] = Puppet[:localcacert]
270       paths['local CRL'] = Puppet[:hostcrl]
271     end
272     paths.each_pair do |label, path|
273       if Puppet::FileSystem.exist?(path)
274         Puppet::FileSystem.unlink(path)
275         Puppet.notice _("Removed %{label} %{path}") % { label: label, path: path }
276       end
277     end
278   end
download_cert(ssl_context) click to toggle source
    # File lib/puppet/application/ssl.rb
191 def download_cert(ssl_context)
192   key = @cert_provider.load_private_key(Puppet[:certname])
193 
194   # try to download cert
195   route = create_route(ssl_context)
196   Puppet.info _("Downloading certificate '%{name}' from %{url}") % { name: Puppet[:certname], url: route.url }
197 
198   _, x509 = route.get_certificate(Puppet[:certname], ssl_context: ssl_context)
199   cert = OpenSSL::X509::Certificate.new(x509)
200   Puppet.notice _("Downloaded certificate '%{name}' with fingerprint %{fingerprint}") % { name: Puppet[:certname], fingerprint: fingerprint(cert) }
201 
202   # verify client cert before saving
203   @ssl_provider.create_context(
204     cacerts: ssl_context.cacerts, crls: ssl_context.crls, private_key: key, client_cert: cert
205   )
206   @cert_provider.save_client_cert(Puppet[:certname], cert)
207   @cert_provider.delete_request(Puppet[:certname])
208   cert
209 rescue Puppet::HTTP::ResponseError => e
210   if e.response.code == 404
211     return nil
212   else
213     raise Puppet::Error.new(_("Failed to download certificate: %{message}") % { message: e.message }, e)
214   end
215 rescue => e
216   raise Puppet::Error.new(_("Failed to download certificate: %{message}") % { message: e.message }, e)
217 end
help() click to toggle source
   # File lib/puppet/application/ssl.rb
13   def help
14     <<-HELP
15 puppet-ssl(8) -- #{summary}
16 ========
17 
18 SYNOPSIS
19 --------
20 Manage SSL keys and certificates for SSL clients needing
21 to communicate with a puppet infrastructure.
22 
23 USAGE
24 -----
25 puppet ssl <action> [-h|--help] [-v|--verbose] [-d|--debug] [--localca] [--target CERTNAME]
26 
27 
28 OPTIONS
29 -------
30 
31 * --help:
32   Print this help message.
33 
34 * --verbose:
35   Print extra information.
36 
37 * --debug:
38   Enable full debugging.
39 
40 * --localca
41   Also clean the local CA certificate and CRL.
42 
43 * --target CERTNAME
44   Clean the specified device certificate instead of this host's certificate.
45 
46 ACTIONS
47 -------
48 
49 * bootstrap:
50   Perform all of the steps necessary to request and download a client
51   certificate. If autosigning is disabled, then puppet will wait every
52   `waitforcert` seconds for its certificate to be signed. To only attempt
53   once and never wait, specify a time of 0. Since `waitforcert` is a
54   Puppet setting, it can be specified as a time interval, such as 30s,
55   5m, 1h.
56 
57 * submit_request:
58   Generate a certificate signing request (CSR) and submit it to the CA. If
59   a private and public key pair already exist, they will be used to generate
60   the CSR. Otherwise a new key pair will be generated. If a CSR has already
61   been submitted with the given `certname`, then the operation will fail.
62 
63 * download_cert:
64   Download a certificate for this host. If the current private key matches
65   the downloaded certificate, then the certificate will be saved and used
66   for subsequent requests. If there is already an existing certificate, it
67   will be overwritten.
68 
69 * verify:
70   Verify the private key and certificate are present and match, verify the
71   certificate is issued by a trusted CA, and check revocation status.
72 
73 * clean:
74   Remove the private key and certificate related files for this host. If
75   `--localca` is specified, then also remove this host's local copy of the
76   CA certificate(s) and CRL bundle. if `--target CERTNAME` is specified, then
77   remove the files for the specified device on this host instead of this host.
78 
79  * show:
80   Print the full-text version of this host's certificate.
81 HELP
82   end
main() click to toggle source
    # File lib/puppet/application/ssl.rb
105 def main
106   if command_line.args.empty?
107     raise Puppet::Error, _("An action must be specified.")
108   end
109 
110   if options[:target]
111     # Override the following, as per lib/puppet/application/device.rb
112     Puppet[:certname] = options[:target]
113     Puppet[:confdir]  = File.join(Puppet[:devicedir], Puppet[:certname])
114     Puppet[:vardir]   = File.join(Puppet[:devicedir], Puppet[:certname])
115     Puppet.settings.use(:main, :agent, :device)
116   else
117     Puppet.settings.use(:main, :agent)
118   end
119 
120   Puppet::SSL::Oids.register_puppet_oids
121   Puppet::SSL::Oids.load_custom_oid_file(Puppet[:trusted_oid_mapping_file])
122 
123   certname = Puppet[:certname]
124   action = command_line.args.first
125   case action
126   when 'submit_request'
127     ssl_context = @machine.ensure_ca_certificates
128     if submit_request(ssl_context)
129       cert = download_cert(ssl_context)
130       unless cert
131         Puppet.info(_("The certificate for '%{name}' has not yet been signed") % { name: certname })
132       end
133     end
134   when 'download_cert'
135     ssl_context = @machine.ensure_ca_certificates
136     cert = download_cert(ssl_context)
137     unless cert
138       raise Puppet::Error, _("The certificate for '%{name}' has not yet been signed") % { name: certname }
139     end
140   when 'verify'
141     verify(certname)
142   when 'clean'
143     clean(certname)
144   when 'bootstrap'
145     if !Puppet::Util::Log.sendlevel?(:info)
146       Puppet::Util::Log.level = :info
147     end
148     @machine.ensure_client_certificate
149     Puppet.notice(_("Completed SSL initialization"))
150   when 'show'
151     show(certname)
152   else
153     raise Puppet::Error, _("Unknown action '%{action}'") % { action: action }
154   end
155 end
setup_logs() click to toggle source
    # File lib/puppet/application/ssl.rb
100 def setup_logs
101   set_log_level(options)
102   Puppet::Util::Log.newdestination(:console)
103 end
show(certname) click to toggle source
    # File lib/puppet/application/ssl.rb
157 def show(certname)
158   password = @cert_provider.load_private_key_password
159   ssl_context = @ssl_provider.load_context(certname: certname, password: password)
160   puts ssl_context.client_cert.to_text
161 end
submit_request(ssl_context) click to toggle source
    # File lib/puppet/application/ssl.rb
163 def submit_request(ssl_context)
164   key = @cert_provider.load_private_key(Puppet[:certname])
165   unless key
166     if Puppet[:key_type] == 'ec'
167       Puppet.info _("Creating a new EC SSL key for %{name} using curve %{curve}") % { name: Puppet[:certname], curve: Puppet[:named_curve] }
168       key = OpenSSL::PKey::EC.generate(Puppet[:named_curve])
169     else
170       Puppet.info _("Creating a new SSL key for %{name}") % { name: Puppet[:certname] }
171       key = OpenSSL::PKey::RSA.new(Puppet[:keylength].to_i)
172     end
173     @cert_provider.save_private_key(Puppet[:certname], key)
174   end
175 
176   csr = @cert_provider.create_request(Puppet[:certname], key)
177   route = create_route(ssl_context)
178   route.put_certificate_request(Puppet[:certname], csr, ssl_context: ssl_context)
179   @cert_provider.save_request(Puppet[:certname], csr)
180   Puppet.notice _("Submitted certificate request for '%{name}' to %{url}") % { name: Puppet[:certname], url: route.url }
181 rescue Puppet::HTTP::ResponseError => e
182   if e.response.code == 400
183     raise Puppet::Error.new(_("Could not submit certificate request for '%{name}' to %{url} due to a conflict on the server") % { name: Puppet[:certname], url: route.url })
184   else
185     raise Puppet::Error.new(_("Failed to submit certificate request: %{message}") % { message: e.message }, e)
186   end
187 rescue => e
188   raise Puppet::Error.new(_("Failed to submit certificate request: %{message}") % { message: e.message }, e)
189 end
summary() click to toggle source
   # File lib/puppet/application/ssl.rb
 9 def summary
10   _("Manage SSL keys and certificates for puppet SSL clients")
11 end
verify(certname) click to toggle source
    # File lib/puppet/application/ssl.rb
219 def verify(certname)
220   password = @cert_provider.load_private_key_password
221   ssl_context = @ssl_provider.load_context(certname: certname, password: password)
222 
223   # print from root to client
224   ssl_context.client_chain.reverse.each_with_index do |cert, i|
225     digest = Puppet::SSL::Digest.new('SHA256', cert.to_der)
226     if i == ssl_context.client_chain.length - 1
227       Puppet.notice("Verified client certificate '#{cert.subject.to_utf8}' fingerprint #{digest}")
228     else
229       Puppet.notice("Verified CA certificate '#{cert.subject.to_utf8}' fingerprint #{digest}")
230     end
231   end
232 end

Private Instance Methods

create_route(ssl_context) click to toggle source
    # File lib/puppet/application/ssl.rb
286 def create_route(ssl_context)
287   @session.route_to(:ca, ssl_context: ssl_context)
288 end
fingerprint(cert) click to toggle source
    # File lib/puppet/application/ssl.rb
282 def fingerprint(cert)
283   Puppet::SSL::Digest.new(nil, cert.to_der)
284 end