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
90 def initialize(command_line = Puppet::Util::CommandLine.new)
91   super(command_line)
92 
93   @cert_provider = Puppet::X509::CertProvider.new
94   @ssl_provider = Puppet::SSL::SSLProvider.new
95   @machine = Puppet::SSL::StateMachine.new
96   @session = Puppet.runtime[:http].create_session
97 end

Public Instance Methods

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

Private Instance Methods

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