#!/usr/bin/perl
# Request a new SSL cert using Let's Encrypt
use strict;
use warnings;
no warnings 'redefine';
no warnings 'uninitialized';
require "./webmin-lib.pl";
our %text;
our %miniserv;
our %in;
our $config_directory;
our %config;
our $module_name;
our $letsencrypt_cmd;
&error_setup($text{'letsencrypt_err'});
my $letsencrypt_ip_cert_renewal_max_days = 5;
# Re-check if let's encrypt is available
my $err = &check_letsencrypt();
&error($err) if ($err);
# Validate inputs
&ReadParse();
my @doms = split(/\s+/, $in{'dom'});
foreach my $dom (@doms) {
$dom =~ /^(\*\.)?[a-z0-9\-\.\_]+$/i ||
&letsencrypt_is_ip_cert_identifier($dom) ||
&error($text{'letsencrypt_edom'});
}
my $has_ip_doms = &letsencrypt_doms_have_ips(\@doms);
$in{'directory_url'} = &trim($in{'directory_url'});
$in{'eab_kid'} = &trim($in{'eab_kid'});
$in{'eab_hmac'} = &trim($in{'eab_hmac'});
if ($in{'directory_url'}) {
my ($host, $port, $page, $ssl) = &parse_http_url($in{'directory_url'});
$host && $ssl < 2 || &error($text{'letsencrypt_eacmedir'});
}
if ($in{'eab_kid'} || $in{'eab_hmac'}) {
$in{'directory_url'} || &error($text{'letsencrypt_eeabdir'});
$in{'eab_kid'} && $in{'eab_hmac'} ||
&error($text{'letsencrypt_eeabpair'});
$letsencrypt_cmd || &error($text{'letsencrypt_eeabnative'});
}
if (!$in{'renew_def'}) {
$in{'renew'} =~ /^[1-9][0-9]*$/ ||
&error($text{'letsencrypt_erenew'});
$in{'renew_unit'} ||= $has_ip_doms ? "days" : "months";
$in{'renew_unit'} =~ /^(days|months)$/ ||
&error($text{'letsencrypt_erenewunit'});
my $ip_max_days = $letsencrypt_ip_cert_renewal_max_days;
if ($has_ip_doms && ($in{'renew_unit'} ne "days" ||
$in{'renew'} > $ip_max_days)) {
&error(&text('letsencrypt_erenewipauto',
$ip_max_days));
}
}
$in{'size_def'} || $in{'size'} =~ /^\d+$/ ||
&error($text{'newkey_esize'});
my $size = $in{'size_def'} ? undef : $in{'size'};
my $webroot;
my $mode = "web";
if ($in{'webroot_mode'} == 3) {
# Validation via DNS
$mode = "dns";
}
elsif ($in{'webroot_mode'} == 4) {
# Validation via Certbot webserver
$mode = "certbot";
}
elsif ($in{'webroot_mode'} == 2) {
# Some directory
$in{'webroot'} =~ /^\/\S+/ && -d $in{'webroot'} ||
&error($text{'letsencrypt_ewebroot'});
$webroot = $in{'webroot'};
}
else {
# Apache domain
&foreign_require("apache");
my $conf = &apache::get_config();
foreach my $virt (&apache::find_directive_struct(
"VirtualHost", $conf)) {
my $sn = &apache::find_directive(
"ServerName", $virt->{'members'});
my @sa = &apache::find_directive(
"ServerAlias", $virt->{'members'});
my $match = 0;
if ($in{'webroot_mode'} == 0 &&
&indexof($doms[0], $sn, @sa) >= 0) {
# Based on domain name
$match = 1;
}
elsif ($in{'webroot_mode'} == 1 && $sn eq $in{'vhost'}) {
# Specifically selected domain
$match = 1;
}
my @ports;
foreach my $w (@{$virt->{'words'}}) {
if ($w =~ /:(\d+)$/) {
push(@ports, $1);
}
else {
push(@ports, 80);
}
}
if ($match && &indexof(80, @ports) >= 0) {
# Get document root
$webroot = &apache::find_directive(
"DocumentRoot", $virt->{'members'}, 1);
$webroot || &error(&text('letsencrypt_edroot', $sn));
last;
}
}
$webroot || &error(&text('letsencrypt_evhost', $doms[0]));
}
if ($in{'save'}) {
# Just update renewal
&save_renewal_only(\@doms, $webroot, $mode, $size,
$in{'subset'}, $in{'use'},
$in{'directory_url'},
$in{'eab_kid'}, $in{'eab_hmac'});
&redirect("edit_ssl.cgi?mode=lets");
}
else {
# Request the cert
&ui_print_unbuffered_header(undef, $text{'letsencrypt_title'}, "");
print &text($mode eq 'dns' ? 'letsencrypt_doingdns' :
$mode eq 'certbot' ? 'letsencrypt_doingcertbot' :
'letsencrypt_doing',
"".&html_escape(join(", ", @doms))."",
"".&html_escape($webroot).""),"
\n";
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
\@doms, $webroot, undef, $size, $mode, $in{'staging'},
undef, undef, undef, $in{'directory_url'},
$in{'eab_kid'}, $in{'eab_hmac'}, $in{'subset'});
if (!$ok) {
print &text('letsencrypt_failed', $cert),"\n";
}
else {
# Worked, now copy to Webmin
my @grid = ( $text{'letsencrypt_cert'}, $cert,
$text{'letsencrypt_key'}, $key );
push(@grid, $text{'letsencrypt_chain'}, $chain) if ($chain);
my $details = &html_escape($text{'letsencrypt_show'})."
\n". &ui_grid_table(\@grid, 2); print &ui_details({ 'html' => 1, 'title' => &ui_tag('span', &html_escape($text{'letsencrypt_done'}), { 'data-second-print' => undef }), 'content' => $details, 'class' => 'inline inlined', }); print "
\n"; # Save the renewal schedule &save_renewal_only(\@doms, $webroot, $mode, $size, $in{'subset'}, $in{'use'}, $in{'directory_url'}, $in{'eab_kid'}, $in{'eab_hmac'}, 1); # Copy cert, key and chain to Webmin if ($in{'use'}) { print $text{'letsencrypt_webmin'},"