mirror of
https://github.com/webmin/webmin.git
synced 2026-08-21 14:30:41 +01:00
Compare commits
6 Commits
2.650
...
dev/fix-ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
301722b572 | ||
|
|
2579cf54d9 | ||
|
|
fa06c02be5 | ||
|
|
d02f0b6cb5 | ||
|
|
81d44f8491 | ||
|
|
6135c01d57 |
@@ -1,5 +1,8 @@
|
||||
## Changelog
|
||||
|
||||
#### 2.651 (June 28, 2026)
|
||||
* Fix Certbot-backed certificate requests and renewals to correctly parse PEM paths after issuance
|
||||
|
||||
#### 2.650 (June 25, 2026)
|
||||
* Add new Systemd Services and Units module
|
||||
* Add new GRUB 2 Boot Loader module
|
||||
@@ -32,8 +35,9 @@
|
||||
* Fix PHP-FPM monitor on EL systems when using `/etc/php.ini` as the config file
|
||||
* Fix RPC-only accounts to block browser/module access before module ACL checks
|
||||
* Fix reflected XSS in Webmin status messages
|
||||
* Fix authentication state handling for SSL certificate logins and proxied keep-alive requests
|
||||
* Fix path validation in File Manager, package delete helpers, and Apache virtual host files
|
||||
* Fix authentication state handling for SSL certificate logins and proxied keep-alive requests
|
||||
* Update session handling to improve security, which will require users to re-authenticate after upgrading
|
||||
* Update the Authentic theme to the latest version with various improvements and fixes:
|
||||
- Add zooming to stats history graphs by holding shift and scrolling in the dashboard
|
||||
- Add support for saving live stats history for up to 24 hours without performance impact
|
||||
|
||||
@@ -7,9 +7,9 @@ my ($cgi) = @_;
|
||||
my @cust = grep { &can_run_command($_) } &list_commands();
|
||||
if ($cgi eq 'edit_cmd.cgi') {
|
||||
# Custom command editor
|
||||
return 'none' if (!&custom_can_edit_commands());
|
||||
my ($cmd) = grep { !$_->{'edit'} && !$_->{'sql'} } @cust;
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) :
|
||||
$access{'edit'} ? 'new=1' : 'none';
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) : 'new=1';
|
||||
}
|
||||
elsif ($cgi eq 'form.cgi') {
|
||||
# Custom command form
|
||||
@@ -18,9 +18,9 @@ elsif ($cgi eq 'form.cgi') {
|
||||
}
|
||||
elsif ($cgi eq 'edit_file.cgi') {
|
||||
# File editor editor
|
||||
return 'none' if (!&custom_can_edit_commands());
|
||||
my ($cmd) = grep { $_->{'edit'} } @cust;
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) :
|
||||
$access{'edit'} ? 'new=1' : 'none';
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) : 'new=1';
|
||||
}
|
||||
elsif ($cgi eq 'view.cgi') {
|
||||
# Custom command form
|
||||
@@ -29,9 +29,9 @@ elsif ($cgi eq 'view.cgi') {
|
||||
}
|
||||
elsif ($cgi eq 'edit_sql.cgi') {
|
||||
# SQL query
|
||||
return 'none' if (!&custom_can_edit_commands());
|
||||
my ($cmd) = grep { $_->{'sql'} } @cust;
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) :
|
||||
$access{'edit'} ? 'new=1' : 'none';
|
||||
return $cmd ? 'id='.&urlize($cmd->{'id'}) : 'new=1';
|
||||
}
|
||||
elsif ($cgi eq 'sqlform.cgi') {
|
||||
# SQL query form
|
||||
|
||||
@@ -6,6 +6,34 @@ use WebminCore;
|
||||
&init_config();
|
||||
%access = &get_module_acl();
|
||||
|
||||
# custom_has_full_webmin_access()
|
||||
# Returns 1 if the current Webmin user has access to all modules.
|
||||
sub custom_has_full_webmin_access
|
||||
{
|
||||
return $custom_full_webmin_access_cache
|
||||
if (defined($custom_full_webmin_access_cache));
|
||||
local %acl;
|
||||
&read_acl(\%acl, undef, [ $base_remote_user ]);
|
||||
local %global_access = &get_module_acl($base_remote_user, "");
|
||||
return $custom_full_webmin_access_cache = 0
|
||||
if ($global_access{'_safe'} || $global_access{'rpc'} == 0);
|
||||
return $custom_full_webmin_access_cache = 1
|
||||
if ($acl{$base_remote_user,'*'});
|
||||
foreach my $m (&get_all_module_infos()) {
|
||||
next if (!&check_os_support($m));
|
||||
return $custom_full_webmin_access_cache = 0
|
||||
if (!$acl{$base_remote_user,$m->{'dir'}});
|
||||
}
|
||||
return $custom_full_webmin_access_cache = 1;
|
||||
}
|
||||
|
||||
# custom_can_edit_commands()
|
||||
# Returns 1 if the current Webmin user can create or edit commands.
|
||||
sub custom_can_edit_commands
|
||||
{
|
||||
return $access{'edit'} && &custom_has_full_webmin_access();
|
||||
}
|
||||
|
||||
# list_commands()
|
||||
# Returns a list of all custom commands
|
||||
sub list_commands
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
require './custom-lib.pl';
|
||||
&ReadParse();
|
||||
|
||||
$access{'edit'} || &error($text{'edit_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'edit_ecannot'});
|
||||
if ($in{'new'}) {
|
||||
&ui_print_header(undef, $text{'create_title'}, "", "create");
|
||||
if ($in{'clone'}) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
require './custom-lib.pl';
|
||||
&ReadParse();
|
||||
|
||||
$access{'edit'} || &error($text{'file_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'file_ecannot'});
|
||||
if ($in{'new'}) {
|
||||
&ui_print_header(undef, $text{'fcreate_title'}, "", "fcreate");
|
||||
if ($in{'clone'}) {
|
||||
|
||||
@@ -20,7 +20,7 @@ if (!@drivers) {
|
||||
"../cpan/download.cgi?source=3&cpan=$pgneed&return=/$module_name/&returndesc=".&urlize($text{'index_return'})),"<p>\n";
|
||||
}
|
||||
|
||||
$access{'edit'} || &error($text{'edit_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'edit_ecannot'});
|
||||
if ($in{'new'}) {
|
||||
&ui_print_header(undef, $text{'sql_title1'}, "");
|
||||
if ($in{'clone'}) {
|
||||
|
||||
@@ -10,7 +10,7 @@ require './custom-lib.pl';
|
||||
|
||||
# Build links
|
||||
@links = ( );
|
||||
if ($access{'edit'}) {
|
||||
if (&custom_can_edit_commands()) {
|
||||
push(@links,&ui_link("edit_cmd.cgi?new=1",$text{'index_create'}));
|
||||
push(@links,&ui_link("edit_file.cgi?new=1",$text{'index_ecreate'}));
|
||||
push(@links,&ui_link("edit_sql.cgi?new=1",$text{'index_screate'}));
|
||||
@@ -66,7 +66,7 @@ elsif ($config{'display_mode'} == 0) {
|
||||
$html .= &ui_table_row(&html_escape($a->{'desc'}),
|
||||
&show_parameter_input($a, $formno));
|
||||
}
|
||||
if ($access{'edit'}) {
|
||||
if (&custom_can_edit_commands()) {
|
||||
if ($c->{'edit'}) {
|
||||
$link = &ui_link("edit_file.cgi?id=$c->{'id'}",$text{'index_fedit'});
|
||||
}
|
||||
@@ -98,7 +98,7 @@ else {
|
||||
foreach $c (@cust) {
|
||||
@cols = ( );
|
||||
local @links = ( );
|
||||
if ($access{'edit'}) {
|
||||
if (&custom_can_edit_commands()) {
|
||||
local $e = $c->{'edit'} ? "edit_file.cgi" :
|
||||
$c->{'sql'} ? "edit_sql.cgi" :
|
||||
"edit_cmd.cgi";
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
require './custom-lib.pl';
|
||||
&ReadParse();
|
||||
|
||||
$access{'edit'} || &error($text{'save_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'save_ecannot'});
|
||||
if ($in{'delete'}) {
|
||||
$cmd = &get_command($in{'id'}, $in{'idx'});
|
||||
&delete_command($cmd);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
require './custom-lib.pl';
|
||||
&ReadParse();
|
||||
|
||||
$access{'edit'} || &error($text{'file_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'file_ecannot'});
|
||||
if ($in{'delete'}) {
|
||||
$edit = &get_command($in{'id'}, $in{'idx'});
|
||||
&delete_command($edit);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
require './custom-lib.pl';
|
||||
&ReadParse();
|
||||
|
||||
$access{'edit'} || &error($text{'save_ecannot'});
|
||||
&custom_can_edit_commands() || &error($text{'save_ecannot'});
|
||||
if ($in{'delete'}) {
|
||||
$cmd = &get_command($in{'id'}, $in{'idx'});
|
||||
&delete_command($cmd);
|
||||
|
||||
@@ -258,7 +258,44 @@ if (&has_command("ip") && $a->{'virtual'} ne '' && !$a->{'up'}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!&has_command("ifconfig") && &has_command("ip")) {
|
||||
if (&has_command("ip") && $a->{'bond'} && $a->{'up'} && !$old) {
|
||||
# Create the bond before assigning addresses to it.
|
||||
my $bcmd = "ip link add ".quotemeta($a->{'name'})." type bond";
|
||||
if (defined($a->{'mode'}) && $a->{'mode'} ne '') {
|
||||
$bcmd .= " mode ".quotemeta(&bond_mode_name($a->{'mode'}));
|
||||
}
|
||||
if ($a->{'miimon'}) {
|
||||
$bcmd .= " miimon ".quotemeta($a->{'miimon'});
|
||||
}
|
||||
if ($a->{'updelay'}) {
|
||||
$bcmd .= " updelay ".quotemeta($a->{'updelay'});
|
||||
}
|
||||
if ($a->{'downdelay'}) {
|
||||
$bcmd .= " downdelay ".quotemeta($a->{'downdelay'});
|
||||
}
|
||||
my $out = &backquote_logged("$bcmd 2>&1");
|
||||
&error("Failed to create bond device : $out") if ($?);
|
||||
foreach my $slave (grep { $_ ne '' } split(/\s+/, $a->{'partner'})) {
|
||||
$bcmd = "ip link set dev ".quotemeta($slave)." down";
|
||||
$out = &backquote_logged("$bcmd 2>&1");
|
||||
&error("Failed to bring down bond slave : $out") if ($?);
|
||||
$bcmd = "ip link set dev ".quotemeta($slave)." master ".
|
||||
quotemeta($a->{'name'});
|
||||
$out = &backquote_logged("$bcmd 2>&1");
|
||||
&error("Failed to add bond slave : $out") if ($?);
|
||||
$bcmd = "ip link set dev ".quotemeta($slave)." up";
|
||||
$out = &backquote_logged("$bcmd 2>&1");
|
||||
&error("Failed to bring up bond slave : $out") if ($?);
|
||||
}
|
||||
if ($a->{'primary'}) {
|
||||
$bcmd = "ip link set dev ".quotemeta($a->{'name'}).
|
||||
" type bond primary ".quotemeta($a->{'primary'});
|
||||
$out = &backquote_logged("$bcmd 2>&1");
|
||||
&error("Failed to set bond primary interface : $out") if ($?);
|
||||
}
|
||||
}
|
||||
|
||||
if (($a->{'bond'} || !&has_command("ifconfig")) && &has_command("ip")) {
|
||||
# For a real interface, activate or de-activate the link
|
||||
if ($a->{'virtual'} eq '' && $a->{'up'} && (!$old || !$old->{'up'})) {
|
||||
# Bring up
|
||||
@@ -646,6 +683,19 @@ local $out = &backquote_logged("$cmd 2>&1");
|
||||
&error($out) if ($?);
|
||||
}
|
||||
|
||||
# bond_mode_name(mode)
|
||||
# Convert Webmin's numeric bonding mode to the name expected by ip(8).
|
||||
sub bond_mode_name
|
||||
{
|
||||
my ($mode) = @_;
|
||||
my @modes = ("balance-rr", "active-backup", "balance-xor", "broadcast",
|
||||
"802.3ad", "balance-tlb", "balance-alb");
|
||||
if ($mode =~ /^\d+$/ && defined($modes[$mode])) {
|
||||
return $modes[$mode];
|
||||
}
|
||||
return $mode eq "activebackup" ? "active-backup" : $mode;
|
||||
}
|
||||
|
||||
# Tries to unload the module
|
||||
# unload_module(name)
|
||||
sub unload_module
|
||||
|
||||
@@ -377,7 +377,7 @@ else {
|
||||
$err && &error("<pre>$err</pre>");
|
||||
}
|
||||
else {
|
||||
if ($in{'bond'}) {
|
||||
if ($in{'bond'} && !&has_command("ip")) {
|
||||
if (($gconfig{'os_type'} eq 'debian-linux') &&
|
||||
($gconfig{'os_version'} >= 5)) {}
|
||||
else {&load_module($b);}
|
||||
@@ -389,4 +389,3 @@ else {
|
||||
"bifc", $b->{'fullname'}, $b);
|
||||
}
|
||||
&redirect("list_ifcs.cgi?mode=boot");
|
||||
|
||||
|
||||
@@ -617,4 +617,47 @@ is_deeply(\@commands,
|
||||
[ "ip addr del 10\\.211\\.55\\.25\\/24 dev enp0s5 2>&1" ],
|
||||
"Linux active virtual interface is removed when saved down");
|
||||
|
||||
@commands = ( );
|
||||
{
|
||||
no warnings 'redefine';
|
||||
no warnings 'once';
|
||||
local *main::has_command = sub {
|
||||
return $_[0] eq "ip" ? "/sbin/ip" :
|
||||
$_[0] eq "ifconfig" ? "/sbin/ifconfig" : undef;
|
||||
};
|
||||
local *main::active_interfaces = sub {
|
||||
return ( );
|
||||
};
|
||||
local *main::backquote_command = sub {
|
||||
return "";
|
||||
};
|
||||
main::activate_interface({ 'name' => 'bond0',
|
||||
'fullname' => 'bond0',
|
||||
'virtual' => '',
|
||||
'bond' => 1,
|
||||
'partner' => 'eth0 eth1',
|
||||
'mode' => '1',
|
||||
'primary' => 'eth0',
|
||||
'miimon' => '100',
|
||||
'updelay' => '200',
|
||||
'downdelay' => '200',
|
||||
'address' => '10.0.0.2',
|
||||
'netmask' => '255.255.255.0',
|
||||
'address6' => [ ],
|
||||
'netmask6' => [ ],
|
||||
'up' => 1 });
|
||||
}
|
||||
is_deeply(\@commands,
|
||||
[ "ip link add bond0 type bond mode active\\-backup miimon 100 updelay 200 downdelay 200 2>&1",
|
||||
"ip link set dev eth0 down 2>&1",
|
||||
"ip link set dev eth0 master bond0 2>&1",
|
||||
"ip link set dev eth0 up 2>&1",
|
||||
"ip link set dev eth1 down 2>&1",
|
||||
"ip link set dev eth1 master bond0 2>&1",
|
||||
"ip link set dev eth1 up 2>&1",
|
||||
"ip link set dev bond0 type bond primary eth0 2>&1",
|
||||
"ip link set dev bond0 up 2>&1",
|
||||
"cd / ; ip addr add 10\\.0\\.0\\.2/24 dev bond0 2>&1" ],
|
||||
"Linux active bond interface is created before assigning an address");
|
||||
|
||||
done_testing();
|
||||
|
||||
50
t/webmin-letsencrypt-paths.t
Normal file
50
t/webmin-letsencrypt-paths.t
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/perl
|
||||
# Regression tests for Certbot output path parsing used by Webmin SSL.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use File::Basename qw(dirname);
|
||||
use File::Spec;
|
||||
|
||||
our %config;
|
||||
our $module_config_directory = "/etc/webmin/webmin";
|
||||
|
||||
sub has_command { return undef; }
|
||||
|
||||
my $script = File::Spec->rel2abs(
|
||||
File::Spec->catfile(dirname(__FILE__), '..',
|
||||
'webmin', 'letsencrypt-lib.pl'));
|
||||
do $script or die "failed to load $script: $@ $!";
|
||||
|
||||
my $certbot_out = <<'EOF';
|
||||
Successfully received certificate.
|
||||
Certificate is saved at: /etc/letsencrypt/live/test.example/fullchain.pem
|
||||
Key is saved at: /etc/letsencrypt/live/test.example/privkey.pem
|
||||
This certificate expires on 2026-09-25.
|
||||
EOF
|
||||
|
||||
is(main::get_letsencrypt_output_pem_path($certbot_out),
|
||||
'/etc/letsencrypt/live/test.example/fullchain.pem',
|
||||
'certbot output path stops at the first PEM path');
|
||||
|
||||
my $ipv6_out = <<'EOF';
|
||||
Successfully received certificate.
|
||||
Certificate is saved at: /etc/letsencrypt/live/2001:db8::1/fullchain.pem
|
||||
Key is saved at: /etc/letsencrypt/live/2001:db8::1/privkey.pem
|
||||
EOF
|
||||
|
||||
is(main::get_letsencrypt_output_pem_path($ipv6_out),
|
||||
'/etc/letsencrypt/live/2001:db8::1/fullchain.pem',
|
||||
'IPv6 certificate names can still contain colons');
|
||||
|
||||
my $wrapped_out = <<'EOF';
|
||||
Certificate is saved at: /etc/letsencrypt/live/wrapped.example/
|
||||
fullchain.pem
|
||||
EOF
|
||||
|
||||
is(main::get_letsencrypt_output_pem_path($wrapped_out),
|
||||
'/etc/letsencrypt/live/wrapped.example/fullchain.pem',
|
||||
'wrapped PEM paths are normalized');
|
||||
|
||||
done_testing();
|
||||
@@ -52,6 +52,7 @@ $newgid = int($config{'base_gid'} > $access{'lowgid'} ?
|
||||
# Process the file
|
||||
&batch_start() if ($in{'batch'});
|
||||
&lock_user_files();
|
||||
$full_webmin_access = &useradmin_has_full_webmin_access();
|
||||
$lnum = $created = $modified = $deleted = 0;
|
||||
print "<pre>\n";
|
||||
$pft = &passfiles_type();
|
||||
@@ -601,6 +602,15 @@ print "</pre>\n";
|
||||
# Check access control restrictions for a user
|
||||
sub check_user
|
||||
{
|
||||
if (!$full_webmin_access) {
|
||||
if ($_[0]->{'user'} eq 'root') {
|
||||
return $text{'usave_eedit'};
|
||||
}
|
||||
if ($_[0]->{'uid'} <= 0) {
|
||||
return &text('usave_elowuid', 1);
|
||||
}
|
||||
}
|
||||
|
||||
# check if uid is within range
|
||||
if ($access{'lowuid'} && $_[0]->{'uid'} < $access{'lowuid'}) {
|
||||
return &text('usave_elowuid', $access{'lowuid'});
|
||||
|
||||
@@ -58,6 +58,7 @@ $err = &check_username_restrictions($in{'user'});
|
||||
&lock_user_files();
|
||||
@ulist = &list_users();
|
||||
@glist = &list_groups();
|
||||
$full_webmin_access = &useradmin_has_full_webmin_access();
|
||||
if ($in{'old'} ne "") {
|
||||
# Get old user info
|
||||
($ouser_hash) = grep { $_->{'user'} eq $in{'old'} } @ulist;
|
||||
@@ -151,6 +152,12 @@ elsif ( $in{'uid_def'} eq '2' ) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!$full_webmin_access && $in{'user'} eq 'root') {
|
||||
&error($text{'usave_eedit'});
|
||||
}
|
||||
if (!$full_webmin_access && $in{'uid'} <= 0) {
|
||||
&error(&text('usave_elowuid', 1));
|
||||
}
|
||||
$in{'real'} =~ /^[^:]*$/ || &error(&text('usave_ereal', $in{'real'}));
|
||||
if ($in{'shell'} eq "*") { $in{'shell'} = $in{'othersh'}; }
|
||||
if ($access{'shells'} ne "*") {
|
||||
|
||||
@@ -30,6 +30,27 @@ do "md5-lib.pl";
|
||||
@random_password_chars = ( 'a' .. 'z', 'A' .. 'Z', '0' .. '9' );
|
||||
$disable_string = $config{'lock_prepend'} eq "" ? "!" : $config{'lock_prepend'};
|
||||
|
||||
# useradmin_has_full_webmin_access()
|
||||
# Returns 1 if the current Webmin user has access to all modules.
|
||||
sub useradmin_has_full_webmin_access
|
||||
{
|
||||
return $useradmin_full_webmin_access_cache
|
||||
if (defined($useradmin_full_webmin_access_cache));
|
||||
local %acl;
|
||||
&read_acl(\%acl, undef, [ $base_remote_user ]);
|
||||
local %global_access = &get_module_acl($base_remote_user, "");
|
||||
return $useradmin_full_webmin_access_cache = 0
|
||||
if ($global_access{'_safe'} || $global_access{'rpc'} == 0);
|
||||
return $useradmin_full_webmin_access_cache = 1
|
||||
if ($acl{$base_remote_user,'*'});
|
||||
foreach my $m (&get_all_module_infos()) {
|
||||
next if (!&check_os_support($m));
|
||||
return $useradmin_full_webmin_access_cache = 0
|
||||
if (!$acl{$base_remote_user,$m->{'dir'}});
|
||||
}
|
||||
return $useradmin_full_webmin_access_cache = 1;
|
||||
}
|
||||
|
||||
# Search types
|
||||
$match_modes = [ [ 0, $text{'index_equals'} ], [ 4, $text{'index_contains'} ],
|
||||
[ 1, $text{'index_matches'} ], [ 2, $text{'index_nequals'} ],
|
||||
@@ -1004,6 +1025,11 @@ control permissions for this module are in the acl parameter.
|
||||
=cut
|
||||
sub can_edit_user
|
||||
{
|
||||
if (!&useradmin_has_full_webmin_access() && defined($_[1]->{'user'}) &&
|
||||
($_[1]->{'user'} eq 'root' ||
|
||||
(defined($_[1]->{'uid'}) && $_[1]->{'uid'} <= 0))) {
|
||||
return 0;
|
||||
}
|
||||
local $m = $_[0]->{'uedit_mode'};
|
||||
local %u;
|
||||
if ($m == 0) { return 1; }
|
||||
|
||||
@@ -447,7 +447,7 @@ ssl_letsrenew_days=days
|
||||
ssl_letsnotrenew=Only renew manually
|
||||
ssl_staging=Provider server
|
||||
ssl_staging0=Production
|
||||
ssl_staging1=Staging (test only)
|
||||
ssl_staging1=Test endpoint
|
||||
ssl_acmeopts=Provider settings
|
||||
ssl_acmeextra=Show advanced settings
|
||||
ssl_acmedir=Custom ACME directory URL
|
||||
@@ -1262,13 +1262,13 @@ letsencrypt_doing=Requesting a new certificate for $1, using the website directo
|
||||
letsencrypt_doingdns=Requesting a new certificate for $1, using DNS validation ..
|
||||
letsencrypt_doingcertbot=Requesting a new certificate for $1, using the Certbot webserver ..
|
||||
letsencrypt_failed=.. request failed : $1
|
||||
letsencrypt_done=.. request succeeded!
|
||||
letsencrypt_show=The new certificate and private key were written to the following files :
|
||||
letsencrypt_done=.. done
|
||||
letsencrypt_show=The new certificate and private key were written to the following files:
|
||||
letsencrypt_cert=SSL certificate
|
||||
letsencrypt_key=SSL private key
|
||||
letsencrypt_chain=Chained CA certificate
|
||||
letsencrypt_webmin=Configuring Webmin to use new cert and key ..
|
||||
letsencrypt_wdone=.. done!
|
||||
letsencrypt_wdone=.. done
|
||||
letsencrypt_eaccountkey=Failed to generate account key : $1
|
||||
letsencrypt_etiny=Failed to request certificate : $1
|
||||
letsencrypt_echain=Failed to download chained certificate : $1
|
||||
|
||||
@@ -131,6 +131,20 @@ return &software::missing_install_link(
|
||||
"certbot", $text{'letsencrypt_certbot'}, $rlink, $rmsg);
|
||||
}
|
||||
|
||||
# get_letsencrypt_output_pem_path(output)
|
||||
# Returns the first certbot PEM path from command output, or undef
|
||||
sub get_letsencrypt_output_pem_path
|
||||
{
|
||||
my ($out) = @_;
|
||||
if ($out =~ /((?:\/usr\/local)?\/etc\/letsencrypt\/(?:live|archive)\/[a-zA-Z0-9\.\_\-:\/\*]+\.pem)/ ||
|
||||
$out =~ /((?:\/usr\/local)?\/etc\/letsencrypt\/(?:live|archive)\/[a-zA-Z0-9\.\_\-:\/\r\n\* ]*?\.pem)/) {
|
||||
my $full = $1;
|
||||
$full =~ s/\s//g;
|
||||
return $full;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# request_letsencrypt_cert(domain|&domains|&ips, webroot, [email], [keysize],
|
||||
# [request-mode], [use-staging], [account-email],
|
||||
# [key-type], [reuse-key],
|
||||
@@ -387,14 +401,15 @@ if ($letsencrypt_cmd) {
|
||||
goto FAILED;
|
||||
}
|
||||
my ($full, $cert, $key, $chain);
|
||||
if ($out =~ /((?:\/usr\/local)?\/etc\/letsencrypt\/(?:live|archive)\/[a-zA-Z0-9\.\_\-:\/\r\n\* ]*\.pem)/) {
|
||||
if ($full = &get_letsencrypt_output_pem_path($out)) {
|
||||
# Output contained the full path
|
||||
$full = $1;
|
||||
$full =~ s/\s//g;
|
||||
}
|
||||
else {
|
||||
# Try searching common paths
|
||||
my @fulls = (glob("/etc/letsencrypt/live/$certname-*/cert.pem"),
|
||||
my @fulls = grep { -r $_ } (
|
||||
"/etc/letsencrypt/live/$certname/cert.pem",
|
||||
glob("/etc/letsencrypt/live/$certname-*/cert.pem"),
|
||||
"/usr/local/etc/letsencrypt/live/$certname/cert.pem",
|
||||
glob("/usr/local/etc/letsencrypt/live/$certname-*/cert.pem"));
|
||||
if (@fulls) {
|
||||
my %stats = map { $_, [ stat($_) ] } @fulls;
|
||||
|
||||
@@ -132,7 +132,7 @@ else {
|
||||
$mode eq 'certbot' ? 'letsencrypt_doingcertbot' :
|
||||
'letsencrypt_doing',
|
||||
"<tt>".&html_escape(join(", ", @doms))."</tt>",
|
||||
"<tt>".&html_escape($webroot)."</tt>"),"<p>\n";
|
||||
"<tt>".&html_escape($webroot)."</tt>"),"<br>\n";
|
||||
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
|
||||
\@doms, $webroot, undef, $size, $mode, $in{'staging'},
|
||||
undef, undef, undef, $in{'directory_url'},
|
||||
@@ -142,7 +142,20 @@ else {
|
||||
}
|
||||
else {
|
||||
# Worked, now copy to Webmin
|
||||
print $text{'letsencrypt_done'},"<p>\n";
|
||||
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'})."<p>\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 "<div data-x-br=\"\"></div>\n";
|
||||
|
||||
# Save the renewal schedule
|
||||
&save_renewal_only(\@doms, $webroot, $mode,
|
||||
@@ -183,15 +196,11 @@ else {
|
||||
|
||||
&webmin_log("letsencrypt");
|
||||
&restart_miniserv(1);
|
||||
print $text{'letsencrypt_wdone'},"<p>\n";
|
||||
print &ui_tag('span',
|
||||
&html_escape($text{'letsencrypt_wdone'}),
|
||||
{ 'data-second-print' => undef });
|
||||
print "<br><div data-x-br=\"\"></div>\n";
|
||||
}
|
||||
|
||||
# Tell the user what was done
|
||||
print $text{'letsencrypt_show'},"<p>\n";
|
||||
my @grid = ( $text{'letsencrypt_cert'}, $cert,
|
||||
$text{'letsencrypt_key'}, $key );
|
||||
push(@grid, $text{'letsencrypt_chain'}, $chain) if ($chain);
|
||||
print &ui_grid_table(\@grid, 2);
|
||||
}
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
Reference in New Issue
Block a user