Compare commits

...

15 Commits

Author SHA1 Message Date
Ilia Ross
463fcea730 Fix to require trusted proxies for SSL client cert headers
This PR tightens handling of proxied SSL client certificate headers so they are only honored when Webmin is configured to trust SSL headers and the direct TCP peer matches an explicit `trusted_proxies` entry.

The change preserves legacy forwarded-IP behavior for `trust_real_ip`, but prevents ambiguous legacy configs with no trusted proxy from accepting spoofable `X-SSL-Client-*` headers as authentication identity. During postinstall, such legacy configs now default to `no_trust_ssl=1`.
2026-06-30 14:23:01 +02:00
Jamie Cameron
a94ff2c49a Merge branch 'master' of github.com:webmin/webmin
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
Close inactive / close-inactive (push) Has been cancelled
2026-06-29 20:18:06 -07:00
Jamie Cameron
ec78452e7b Add missing icons 2026-06-29 20:17:23 -07:00
Ilia Ross
a7325b9087 Update changelog for 2.652 2026-06-30 01:47:25 +02:00
Ilia Ross
cc4bb35dee Update tests to recognize hex numeric HTML entities
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
2026-06-29 23:28:48 +02:00
Ilia Ross
aa2282778e Fix to recognize hex numeric HTML entities
https://forum.virtualmin.com/t/char-redered-as-x25e6/137494/6?u=ilia
2026-06-29 23:28:28 +02:00
Jamie Cameron
1c9d57f5dd Safe mode in the custom commands module just allows execution of commands
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
Close inactive / close-inactive (push) Has been cancelled
2026-06-28 17:17:21 -07:00
Jamie Cameron
4ffa02d636 New version bump 2026-06-28 15:39:34 -07:00
Ilia Ross
57ad5fa535 Update labels
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
2026-06-28 23:02:39 +02:00
Ilia Ross
a3f5ea3381 Update changelog
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
Close inactive / close-inactive (push) Has been cancelled
2026-06-28 02:40:46 +02:00
Ilia Ross
2579cf54d9 Fix output 2026-06-27 23:45:31 +02:00
Ilia Ross
fa06c02be5 Update changelog
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
https://forum.virtualmin.com/t/cant-create-letsencrypt-cert-with-webmin-2-650/137484/14?u=ilia
2026-06-27 23:26:28 +02:00
Ilia Ross
d02f0b6cb5 Fix Let's Encrypt Certbot PEM path parsing
ⓘ Prevent Webmin from swallowing Certbot's key-path output when extracting PEM paths, while preserving IPv6 cert-name support and adding regression coverage.
2026-06-27 22:59:21 +02:00
Ilia Ross
81d44f8491 Fix live activation of Linux bond interfaces
ⓘ Create and configure missing bond devices with ip link, attach partner interfaces before assigning addresses, avoid legacy module auto-creation when ip is available, and add regression coverage.

Ref.: https://github.com/webmin/webmin/pull/2777
2026-06-27 18:16:38 +02:00
Ilia Ross
6135c01d57 Update changelog
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
Close inactive / close-inactive (push) Has been cancelled
2026-06-26 23:30:00 +02:00
23 changed files with 246 additions and 50 deletions

View File

@@ -1,5 +1,16 @@
## Changelog ## Changelog
#### 2.652 (July, 2026)
* Fix to recognize hex numeric HTML entities to work in various elements
#### 2.651 (June 28, 2026)
* Fix Certbot-backed certificate requests and renewals to correctly parse PEM paths after issuance
* Fix live activation of Linux bond interfaces [#2777](https://github.com/webmin/webmin/pull/2777)
* Update the Authentic theme to the latest version with various improvements and fixes:
- Fix search-result all-items delete in File Manager
- Fix search-result delete ordering in File Manager
- Fix to speed up search-result deletion cleanup in File Manager
#### 2.650 (June 25, 2026) #### 2.650 (June 25, 2026)
* Add new Systemd Services and Units module * Add new Systemd Services and Units module
* Add new GRUB 2 Boot Loader module * Add new GRUB 2 Boot Loader module
@@ -32,8 +43,9 @@
* Fix PHP-FPM monitor on EL systems when using `/etc/php.ini` as the config file * 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 RPC-only accounts to block browser/module access before module ACL checks
* Fix reflected XSS in Webmin status messages * 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 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: * 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 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 - Add support for saving live stats history for up to 24 hours without performance impact

2
custom/safeacl Normal file
View File

@@ -0,0 +1,2 @@
edit=0
cmds=*

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
grub2/images/icon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
kea-dhcp/images/icon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

View File

@@ -1542,16 +1542,25 @@ if ($headerhost) {
$headerhost = undef if (!&check_ipaddress($headerhost) && $headerhost = undef if (!&check_ipaddress($headerhost) &&
!&check_ip6address($headerhost)); !&check_ip6address($headerhost));
} }
# If trusted_proxies is configured, header-supplied client IP and SSL # If trusted_proxies is configured, header-supplied client IP is only
# client info are only honored when the direct TCP peer is in that list. # honored when the direct TCP peer is in that list. Proxied SSL client
# Otherwise drop them so an attacker reaching miniserv directly cannot # cert headers carry authentication identity, so only honor those from
# spoof X-Forwarded-For or X-SSL-Client-* to bypass auth. # an explicitly trusted proxy.
if ($config{'trust_real_ip'} && $config{'trusted_proxies'} ne '' && my @trusted_proxies = split(/\s+/, $config{'trusted_proxies'} || "");
!&ip_match($acptip, $localip, my $trusted_proxy = @trusted_proxies &&
split(/\s+/, $config{'trusted_proxies'}))) { &ip_match($acptip, $localip, @trusted_proxies);
my $trust_ssl_client_headers = $config{'trust_real_ip'} &&
!$config{'no_trust_ssl'} && $trusted_proxy;
if ($config{'trust_real_ip'} && @trusted_proxies && !$trusted_proxy) {
print DEBUG "handle_request: peer $acptip not in trusted_proxies; ". print DEBUG "handle_request: peer $acptip not in trusted_proxies; ".
"ignoring forwarding and SSL client headers\n"; "ignoring forwarding headers\n";
$headerhost = undef; $headerhost = undef;
}
if (!$trust_ssl_client_headers) {
print DEBUG "handle_request: ignoring SSL client headers from ".
"peer $acptip\n"
if ($header{'x-ssl-client-dn'} ||
$header{'x-ssl-client-verify'});
delete $header{'x-ssl-client-dn'}; delete $header{'x-ssl-client-dn'};
delete $header{'x-ssl-client-verify'}; delete $header{'x-ssl-client-verify'};
} }

View File

@@ -258,7 +258,44 @@ if (&has_command("ip") && $a->{'virtual'} ne '' && !$a->{'up'}) {
return; 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 # For a real interface, activate or de-activate the link
if ($a->{'virtual'} eq '' && $a->{'up'} && (!$old || !$old->{'up'})) { if ($a->{'virtual'} eq '' && $a->{'up'} && (!$old || !$old->{'up'})) {
# Bring up # Bring up
@@ -646,6 +683,19 @@ local $out = &backquote_logged("$cmd 2>&1");
&error($out) if ($?); &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 # Tries to unload the module
# unload_module(name) # unload_module(name)
sub unload_module sub unload_module

View File

@@ -377,7 +377,7 @@ else {
$err && &error("<pre>$err</pre>"); $err && &error("<pre>$err</pre>");
} }
else { else {
if ($in{'bond'}) { if ($in{'bond'} && !&has_command("ip")) {
if (($gconfig{'os_type'} eq 'debian-linux') && if (($gconfig{'os_type'} eq 'debian-linux') &&
($gconfig{'os_version'} >= 5)) {} ($gconfig{'os_version'} >= 5)) {}
else {&load_module($b);} else {&load_module($b);}
@@ -389,4 +389,3 @@ else {
"bifc", $b->{'fullname'}, $b); "bifc", $b->{'fullname'}, $b);
} }
&redirect("list_ifcs.cgi?mode=boot"); &redirect("list_ifcs.cgi?mode=boot");

View File

@@ -617,4 +617,47 @@ is_deeply(\@commands,
[ "ip addr del 10\\.211\\.55\\.25\\/24 dev enp0s5 2>&1" ], [ "ip addr del 10\\.211\\.55\\.25\\/24 dev enp0s5 2>&1" ],
"Linux active virtual interface is removed when saved down"); "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(); done_testing();

View File

@@ -38,6 +38,9 @@ subtest 'html_escape' => sub {
is(main::html_escape('&amp;'), '&amp;amp;', 'default mode double-escapes &amp;'); is(main::html_escape('&amp;'), '&amp;amp;', 'default mode double-escapes &amp;');
is(main::html_escape('&amp;', 1), '&amp;', 'nodblamp preserves existing &amp;'); is(main::html_escape('&amp;', 1), '&amp;', 'nodblamp preserves existing &amp;');
is(main::html_escape('&#65;', 1), '&#65;', 'nodblamp preserves numeric entity'); is(main::html_escape('&#65;', 1), '&#65;', 'nodblamp preserves numeric entity');
is(main::html_escape('&#x25E6;', 1), '&#x25E6;', 'nodblamp preserves hex numeric entity');
is(main::html_escape('&#X25E6;', 1), '&#X25E6;', 'nodblamp preserves uppercase hex numeric entity');
is(main::html_escape('&#xZZ;', 1), '&amp;#xZZ;', 'nodblamp escapes invalid hex numeric entity');
# Note: nodblamp's lookahead matches any &<letters>; as an entity, so # Note: nodblamp's lookahead matches any &<letters>; as an entity, so
# made-up names like &x; are treated as entities and not re-escaped. # made-up names like &x; are treated as entities and not re-escaped.
is(main::html_escape('&x;', 1), '&x;', 'nodblamp preserves arbitrary &word; shape'); is(main::html_escape('&x;', 1), '&x;', 'nodblamp preserves arbitrary &word; shape');

View 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();

View File

@@ -1 +1 @@
2.650 2.651

View File

@@ -302,7 +302,7 @@ if (!defined $tmp) {
}; };
# Before escaping ampersand use negative lookahead to see if occurrence # Before escaping ampersand use negative lookahead to see if occurrence
# is not an HTML entity already to prevent double escaping (optionally) # is not an HTML entity already to prevent double escaping (optionally)
$tmp =~ s/&(?!(([a-zA-Z]+)|(#|#x)\d+);)/&amp;/g if ($nodblamp); $tmp =~ s/&(?!([a-zA-Z]+|#\d+|#[xX][0-9A-Fa-f]+);)/&amp;/g if ($nodblamp);
# Always escape all ampersands by default # Always escape all ampersands by default
# to make sure they are displayed per se # to make sure they are displayed per se
$tmp =~ s/&/&amp;/g if (!$nodblamp); $tmp =~ s/&/&amp;/g if (!$nodblamp);

View File

@@ -3,13 +3,13 @@ standard_url=URL of standard modules list,3,On webmin.com
third_url=URL of third party modules list,3,On webmin.com third_url=URL of third party modules list,3,On webmin.com
cron_mode=Show update times as,1,0-Simple interface,1-Cron time selector cron_mode=Show update times as,1,0-Simple interface,1-Cron time selector
warn_days=Days before password expiry to warn users,0,5 warn_days=Days before password expiry to warn users,0,5
line2=Let's Encrypt configuration,11 line2=ACME provider configuration,11
letsencrypt_cmd=Full path to Let's Encrypt client command,3,Find automatically letsencrypt_cmd=Full path to ACME client command,3,Find automatically
letsencrypt_directory_url=Custom ACME directory URL,3,Use Let's Encrypt production letsencrypt_directory_url=Custom ACME directory URL,3,Use default production server
letsencrypt_eab_kid=External Account Binding key ID,3,None letsencrypt_eab_kid=External Account Binding key ID,3,None
letsencrypt_eab_hmac=External Account Binding HMAC key,12 letsencrypt_eab_hmac=External Account Binding HMAC key,12
letsencrypt_algo=Encryption algorithm for Let's Encrypt private key,1,rsa-RSA,ecdsa-ECC letsencrypt_algo=Encryption algorithm for certificate private key,1,rsa-RSA,ecdsa-ECC
letsencrypt_dns_wait=Seconds to wait for Let's Encrypt DNS propagation,0,5 letsencrypt_dns_wait=Seconds to wait for ACME DNS propagation,0,5
letsencrypt_before=Command to run before Let's Encrypt request,0,60 letsencrypt_before=Command to run before ACME certificate request,0,60
letsencrypt_after=Command to run after Let's Encrypt request,0,60 letsencrypt_after=Command to run after ACME certificate request,0,60
letsencrypt_reuse=Re-use existing Let's Encrypt keys?,1,1-Yes,0-No letsencrypt_reuse=Re-use existing certificate keys?,1,1-Yes,0-No

View File

@@ -2,4 +2,4 @@ standard_url=URL de la llista de mòduls estàndard,3,A webmin.com
third_url=URL de la llista de mòduls de tercers,3,A webmin.com third_url=URL de la llista de mòduls de tercers,3,A webmin.com
cron_mode=Mostra els temps d'actualització com,1,0-Interfície simple,1-Selector de temps cron cron_mode=Mostra els temps d'actualització com,1,0-Interfície simple,1-Selector de temps cron
warn_days=Dies abans de l'expiració de la contrasenya per avisar els usuaris,0,5 warn_days=Dies abans de l'expiració de la contrasenya per avisar els usuaris,0,5
letsencrypt_cmd=Camí complet de l'ordre client de Let's Encrypt,3,Troba'l automàticament letsencrypt_cmd=Camí complet de l'ordre client ACME,3,Troba'l automàticament

View File

@@ -2,4 +2,4 @@ standard_url=URL der Standardmodul&#45;Liste,3,Von webmin.com
third_url=URL der Drittanbieter&#45;Webmin&#45;Modulliste,3,Von webmin.com third_url=URL der Drittanbieter&#45;Webmin&#45;Modulliste,3,Von webmin.com
cron_mode=Zeige Aktualisierungszeiten als,1,0-Einfache Anzeige,1-Cron&#45;Zeit&#45;Auswahl cron_mode=Zeige Aktualisierungszeiten als,1,0-Einfache Anzeige,1-Cron&#45;Zeit&#45;Auswahl
warn_days=Tage vor Ablauf des Passworts zu warnen an Benutzer,0,5 warn_days=Tage vor Ablauf des Passworts zu warnen an Benutzer,0,5
letsencrypt_cmd=Voller Pfad zum Let's Encrypt Client&#45;Befehl,0 letsencrypt_cmd=Voller Pfad zum ACME-Client&#45;Befehl,0

View File

@@ -3,6 +3,6 @@ standard_url=URL de la liste des modules standard,3,webmin.com
third_url=URL de la liste des modules non-standard,3,webmin.com third_url=URL de la liste des modules non-standard,3,webmin.com
cron_mode=Afficher les heures de mise à jour comme,1,0-Interface simple,1-Sélecteur de temps Cron cron_mode=Afficher les heures de mise à jour comme,1,0-Interface simple,1-Sélecteur de temps Cron
warn_days=Jours avant l'expiration du mot de passe pour avertir les utilisateurs,0,5 warn_days=Jours avant l'expiration du mot de passe pour avertir les utilisateurs,0,5
line2=Let's Encrypt configuration,11 line2=Configuration du fournisseur ACME,11
letsencrypt_cmd=Chemin complet de la commande client Let's Encrypt,3,Trouver automatiquement letsencrypt_cmd=Chemin complet de la commande client ACME,3,Trouver automatiquement
letsencrypt_dns_wait=Quelques secondes à attendre pour la propagation DNS de Let's Encrypt,0,5 letsencrypt_dns_wait=Quelques secondes à attendre pour la propagation DNS ACME,0,5

View File

@@ -3,6 +3,6 @@ standard_url=標準モジュールリストに使用する URL,3,webmin.com
third_url=サードパーティ製モジュールリストに使用する URL,3,webmin.com third_url=サードパーティ製モジュールリストに使用する URL,3,webmin.com
cron_mode=アップデート時刻の表示方法,1,0-シンプル,1-Cron 選択方式 cron_mode=アップデート時刻の表示方法,1,0-シンプル,1-Cron 選択方式
warn_days=ユーザのパスワード期限切れの通知(日前),0,5 warn_days=ユーザのパスワード期限切れの通知(日前),0,5
line2=Let's Encrypt の設定,11 line2=ACME プロバイダーの設定,11
letsencrypt_cmd=Let's Encrypt クライアントのパス,3,自動設定 letsencrypt_cmd=ACME クライアントのパス,3,自動設定
letsencrypt_dns_wait=Let's Encrypt DNS の更新待機時間(秒),0,5 letsencrypt_dns_wait=ACME DNS の更新待機時間(秒),0,5

View File

@@ -447,7 +447,7 @@ ssl_letsrenew_days=days
ssl_letsnotrenew=Only renew manually ssl_letsnotrenew=Only renew manually
ssl_staging=Provider server ssl_staging=Provider server
ssl_staging0=Production ssl_staging0=Production
ssl_staging1=Staging (test only) ssl_staging1=Test endpoint
ssl_acmeopts=Provider settings ssl_acmeopts=Provider settings
ssl_acmeextra=Show advanced settings ssl_acmeextra=Show advanced settings
ssl_acmedir=Custom ACME directory URL 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_doingdns=Requesting a new certificate for $1, using DNS validation ..
letsencrypt_doingcertbot=Requesting a new certificate for $1, using the Certbot webserver .. letsencrypt_doingcertbot=Requesting a new certificate for $1, using the Certbot webserver ..
letsencrypt_failed=.. request failed : $1 letsencrypt_failed=.. request failed : $1
letsencrypt_done=.. request succeeded! letsencrypt_done=.. done
letsencrypt_show=The new certificate and private key were written to the following files : letsencrypt_show=The new certificate and private key were written to the following files:
letsencrypt_cert=SSL certificate letsencrypt_cert=SSL certificate
letsencrypt_key=SSL private key letsencrypt_key=SSL private key
letsencrypt_chain=Chained CA certificate letsencrypt_chain=Chained CA certificate
letsencrypt_webmin=Configuring Webmin to use new cert and key .. 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_eaccountkey=Failed to generate account key : $1
letsencrypt_etiny=Failed to request certificate : $1 letsencrypt_etiny=Failed to request certificate : $1
letsencrypt_echain=Failed to download chained certificate : $1 letsencrypt_echain=Failed to download chained certificate : $1

View File

@@ -131,6 +131,20 @@ return &software::missing_install_link(
"certbot", $text{'letsencrypt_certbot'}, $rlink, $rmsg); "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_letsencrypt_cert(domain|&domains|&ips, webroot, [email], [keysize],
# [request-mode], [use-staging], [account-email], # [request-mode], [use-staging], [account-email],
# [key-type], [reuse-key], # [key-type], [reuse-key],
@@ -387,14 +401,15 @@ if ($letsencrypt_cmd) {
goto FAILED; goto FAILED;
} }
my ($full, $cert, $key, $chain); 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 # Output contained the full path
$full = $1;
$full =~ s/\s//g;
} }
else { else {
# Try searching common paths # 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")); glob("/usr/local/etc/letsencrypt/live/$certname-*/cert.pem"));
if (@fulls) { if (@fulls) {
my %stats = map { $_, [ stat($_) ] } @fulls; my %stats = map { $_, [ stat($_) ] } @fulls;

View File

@@ -132,7 +132,7 @@ else {
$mode eq 'certbot' ? 'letsencrypt_doingcertbot' : $mode eq 'certbot' ? 'letsencrypt_doingcertbot' :
'letsencrypt_doing', 'letsencrypt_doing',
"<tt>".&html_escape(join(", ", @doms))."</tt>", "<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( my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
\@doms, $webroot, undef, $size, $mode, $in{'staging'}, \@doms, $webroot, undef, $size, $mode, $in{'staging'},
undef, undef, undef, $in{'directory_url'}, undef, undef, undef, $in{'directory_url'},
@@ -142,7 +142,20 @@ else {
} }
else { else {
# Worked, now copy to Webmin # 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 the renewal schedule
&save_renewal_only(\@doms, $webroot, $mode, &save_renewal_only(\@doms, $webroot, $mode,
@@ -183,15 +196,11 @@ else {
&webmin_log("letsencrypt"); &webmin_log("letsencrypt");
&restart_miniserv(1); &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'}); &ui_print_footer("", $text{'index_return'});

View File

@@ -60,8 +60,12 @@ if (!-r $first_install_file || $miniserv{'login_script'} eq $record_login_cmd) {
$miniserv{'failed_script'} = $record_failed_cmd; $miniserv{'failed_script'} = $record_failed_cmd;
} }
# Disable trusting SSL certs unless already enabled # Disable trusting SSL certs unless already enabled. Legacy configs with
if (!$miniserv{'trust_real_ip'} && !defined($miniserv{'no_trust_ssl'})) { # trust_real_ip but no trusted proxy cannot safely authenticate from
# proxied SSL client cert headers.
my @trusted_proxies = split(/\s+/, $miniserv{'trusted_proxies'} || "");
if ((!$miniserv{'trust_real_ip'} || !@trusted_proxies) &&
!defined($miniserv{'no_trust_ssl'})) {
$miniserv{'no_trust_ssl'} = 1; $miniserv{'no_trust_ssl'} = 1;
} }