1#!/usr/local/bin/perl 2# Request a new SSL cert using Let's Encrypt 3 4use strict; 5use warnings; 6 7require "./webmin-lib.pl"; 8our %text; 9our %miniserv; 10our %in; 11our $config_directory; 12our %config; 13our $module_name; 14&error_setup($text{'letsencrypt_err'}); 15 16# Re-check if let's encrypt is available 17my $err = &check_letsencrypt(); 18&error($err) if ($err); 19 20# Validate inputs 21&ReadParse(); 22my @doms = split(/\s+/, $in{'dom'}); 23foreach my $dom (@doms) { 24 $dom =~ /^(\*\.)?[a-z0-9\-\.\_]+$/i || &error($text{'letsencrypt_edom'}); 25 } 26$in{'renew_def'} || $in{'renew'} =~ /^[1-9][0-9]*$/ || 27 &error($text{'letsencrypt_erenew'}); 28$in{'size_def'} || $in{'size'} =~ /^\d+$/ || 29 &error($text{'newkey_esize'}); 30my $size = $in{'size_def'} ? undef : $in{'size'}; 31my $webroot; 32my $mode = "web"; 33if ($in{'webroot_mode'} == 3) { 34 # Validation via DNS 35 $mode = "dns"; 36 } 37elsif ($in{'webroot_mode'} == 2) { 38 # Some directory 39 $in{'webroot'} =~ /^\/\S+/ && -d $in{'webroot'} || 40 &error($text{'letsencrypt_ewebroot'}); 41 $webroot = $in{'webroot'}; 42 } 43else { 44 # Apache domain 45 &foreign_require("apache"); 46 my $conf = &apache::get_config(); 47 foreach my $virt (&apache::find_directive_struct( 48 "VirtualHost", $conf)) { 49 my $sn = &apache::find_directive( 50 "ServerName", $virt->{'members'}); 51 my $match = 0; 52 if ($in{'webroot_mode'} == 0 && $sn eq $doms[0]) { 53 # Based on domain name 54 $match = 1; 55 } 56 elsif ($in{'webroot_mode'} == 1 && $sn eq $in{'vhost'}) { 57 # Specifically selected domain 58 $match = 1; 59 } 60 if ($match) { 61 # Get document root 62 $webroot = &apache::find_directive( 63 "DocumentRoot", $virt->{'members'}, 1); 64 $webroot || &error(&text('letsencrypt_edroot', $sn)); 65 last; 66 } 67 } 68 $webroot || &error(&text('letsencrypt_evhost', $doms[0])); 69 } 70 71if ($in{'save'}) { 72 # Just update renewal 73 &save_renewal_only(\@doms, $webroot, $mode); 74 &redirect("edit_ssl.cgi"); 75 } 76else { 77 # Request the cert 78 &ui_print_unbuffered_header(undef, $text{'letsencrypt_title'}, ""); 79 80 print &text($mode eq 'dns' ? 'letsencrypt_doingdns' 81 : 'letsencrypt_doing', 82 "<tt>".&html_escape(join(", ", @doms))."</tt>", 83 "<tt>".&html_escape($webroot)."</tt>"),"<p>\n"; 84 my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(\@doms, $webroot, undef, $size, $mode, $in{'staging'}); 85 if (!$ok) { 86 print &text('letsencrypt_failed', $cert),"<p>\n"; 87 } 88 else { 89 # Worked, now copy to Webmin 90 print $text{'letsencrypt_done'},"<p>\n"; 91 92 if ($in{'use'}) { 93 # Copy cert, key and chain to Webmin 94 print $text{'letsencrypt_webmin'},"<p>\n"; 95 &lock_file($ENV{'MINISERV_CONFIG'}); 96 &get_miniserv_config(\%miniserv); 97 98 $miniserv{'keyfile'} = $config_directory. 99 "/letsencrypt-key.pem"; 100 &lock_file($miniserv{'keyfile'}); 101 ©_source_dest($key, $miniserv{'keyfile'}, 1); 102 &unlock_file($miniserv{'keyfile'}); 103 104 $miniserv{'certfile'} = $config_directory. 105 "/letsencrypt-cert.pem"; 106 &lock_file($miniserv{'certfile'}); 107 ©_source_dest($cert, $miniserv{'certfile'}, 1); 108 &unlock_file($miniserv{'certfile'}); 109 110 if ($chain) { 111 $miniserv{'extracas'} = $config_directory. 112 "/letsencrypt-ca.pem"; 113 &lock_file($miniserv{'extracas'}); 114 ©_source_dest($chain, $miniserv{'extracas'}, 1); 115 &unlock_file($miniserv{'extracas'}); 116 } 117 else { 118 delete($miniserv{'extracas'}); 119 } 120 &put_miniserv_config(\%miniserv); 121 &unlock_file($ENV{'MINISERV_CONFIG'}); 122 123 &save_renewal_only(\@doms, $webroot, $mode); 124 125 &webmin_log("letsencrypt"); 126 &restart_miniserv(1); 127 print $text{'letsencrypt_wdone'},"<p>\n"; 128 } 129 else { 130 # Just tell the user 131 print $text{'letsencrypt_show'},"<p>\n"; 132 my @grid = ( $text{'letsencrypt_cert'}, $cert, 133 $text{'letsencrypt_key'}, $key ); 134 if ($chain) { 135 push(@grid, $text{'letsencrypt_chain'}, $chain); 136 } 137 print &ui_grid_table(\@grid, 2); 138 } 139 } 140 141 &ui_print_footer("", $text{'index_return'}); 142 } 143 144# save_renewal_only(&doms, webroot, mode) 145# Save for future renewals 146sub save_renewal_only 147{ 148my ($doms, $webroot, $mode) = @_; 149$config{'letsencrypt_doms'} = join(" ", @$doms); 150$config{'letsencrypt_webroot'} = $webroot; 151$config{'letsencrypt_mode'} = $mode; 152$config{'letsencrypt_size'} = $size; 153&save_module_config(); 154if (&foreign_check("webmincron")) { 155 my $job = &find_letsencrypt_cron_job(); 156 if ($in{'renew_def'}) { 157 &webmincron::delete_webmin_cron($job) if ($job); 158 } 159 else { 160 my @tm = localtime(time() - 60); 161 $job ||= { 'module' => $module_name, 162 'func' => 'renew_letsencrypt_cert' }; 163 $job->{'mins'} ||= $tm[1]; 164 $job->{'hours'} ||= $tm[2]; 165 $job->{'days'} ||= $tm[3]; 166 $job->{'months'} = '*/'.$in{'renew'}; 167 $job->{'weekdays'} = '*'; 168 &webmincron::create_webmin_cron($job); 169 } 170 } 171} 172