Webmin Virtualmin Password Recovery

I just mean that I extracted the URL from a cat of the mail file that arrived as a result of the password reset request and pasted it into my browser. (I have had it send the reset mail to my own real-world email address – that link did not work either, neither did other browsers.)

I am not sure what you mean by redirecting. The URL that the reset mail sends has :10000 in it even in the mail (though I start at :20000 to generate that mail, it does send me to :10000 to actually specify my email address) as I believe it should (since that’s where Webmin/Usermin really is). If I change it to :20000 manually it just ignores the extra stuff and takes me to the mail login screen. If I remove the :10000 it fails as I have not built a thisisatestdomain.com:80 website.

The:

ubuntu-24-04-2-live-server-amd64.iso

I mentioned is the file you get if you click the “Download 24.04.2 LTS” button here:

My setup is Proxmox. I upload the .iso to it, add a VM, point its ISO Image to the .iso (specify the RAM and so forth) and boot. The Ubuntu installer only asks the language, keyboard layout, whether you just want to install the normal server or a runtime, then it grabs an IP from your DHCP and asks whether it needs a proxy to connect. It goes out for a minute and checks that you can see ubuntu.com online (for stuff it might grab during the the install), asks whether you want to use the whole disk that it finds itself on, whether its preallocation of that is okay, asks for your login and server name choices, whether you want to buy Ubuntu Pro whether you want to install OpenSSH server, whether you want any packages added (no)… and then it does a bunch of stuff all by itself and it’s done. It probably takes a half an hour doing all that (I don’t watch, I just come back later.)

(I typically do Linux this way because I don’t spin them up very often, and generally want the current version.)

Then I ssh in, and continue as described before (run the virtualmin install, make a domain and user with the Virtualmin web interface, install the plugin, and test).

This is my hosts file (having some duplicate pointing somewhere else is exactly the sort of thing I would do, but not this time):

It’s the “3D” before the ID in the URL that I’m sent. If I remove that it works rightly.

(The 3D before the ID is there if I have it email to a real address and look at it in Outlook, and it applies if I click the link directly.)

I don’t know what to do about that, but that is the issue.

in that screenshot, there is no 3D BEFORE “id” :smiley: Sorry if I’m being too picky.

FYI, speaking in general, I think when an email is in the quoted-printable format, “=3D” is a representation of an equal sign – those three characters, when the email message is displayed, are translated/replaced with “=”. So its possible you cannot take the literal text from that email message on disk – it has to be rendered into its displayable version.

check and see how Outlook DISPLAYS that message (or Thunderbird, whatever is easiest for you)

You are right about the “=” being translated literally into “=3D” somewhere along the way. It is not reinterpreted as just “=” by Outlook or Usermin’s webmail interface though.

In a test writing to log at this point in the script, the URL is still correct (sans “=3D”) still when it is part of $msg here:

&mailboxes::send_text_mail($virtual_server::config{‘from_addr’} ||
&mailboxes::get_from_address(),
$emailto,
undef,
$subject,
$msg);

So it must be something to do with however send_text_mail works (whatever that actually calls).

(Very technically I said “The 3D before the ID” not “before the id”, indicating that I meant the ID proper and not the lowercase characters id. I do concede that there is some ambiguity there.)

I have a work-around for this, which is to replace send_text_mail with Send Mail. This did require running:

cpan Mail::Sendmail

At the command line to install it.

I replaced this section of /usr/share/webmin/virtualmin-password-recovery/email.cgi:

# Send email
&foreign_require("mailboxes", "mailboxes-lib.pl");
$msg = join("\n", &mailboxes::wrap_lines($msg, 70))."\n";
my $emailto = $user ? $user->{'recovery'} :
           $dom ? $dom->{'emailto'} :
                  $owner->{'acl'}->{'email'};
my $subject = $user ? $text{'email_subject3'} :
           $dom ? $text{'email_subject'} :
                  $text{'email_subject2'};
if ($mode == 1 && !$immediate) {
        $subject = &text('email_linkfor', $subject);
        }
&mailboxes::send_text_mail($virtual_server::config{'from_addr'} ||
                             &mailboxes::get_from_address(),
                           $emailto,
                           undef,
                           $subject,
                           $msg);

# Tell the user
if ($mode == 1 && !$immediate) {
        print "<p>",&text('email_donelink', "<tt>$emailto</tt>"),"</p>\n";
        &popup_footer();
        }
elsif ($dom) {
        print "<p>",&text('email_done', "<tt>$emailto</tt>",
                                        "<tt>$dom->{'dom'}</tt>"),"<p>\n";
        print &text('email_return', "/"),"<p>\n";

        &popup_footer();
        &webmin_log("email", undef, $dom->{'dom'},
                    { 'email' => $emailto,
                      'virt' => 1 });
        }
elsif ($owner) {
        print "<p>",&text('email_done2', "<tt>$emailto</tt>",
                                         "<tt>$owner->{'name'}</tt>"),"<p>\n";
        print &text('email_return', "/"),"<p>\n";

        &popup_footer();
        &webmin_log("email", undef, $owner->{'name'},
                    { 'email' => $emailto,
                      'vm2' => 1 });
        }
elsif ($user) {
        print "<p>",&text('email_done3', "<tt>$emailto</tt>",
                                         "<tt>$user->{'user'}</tt>"),"<p>\n";
        print &text('email_return', $url),"<p>\n";

        &popup_footer();
        &webmin_log("email", undef, $user->{'user'},
                    { 'email' => $emailto,
                      'usermin' => 1 });
        }

With this:

# Send email
use Mail::Sendmail;

my $address = $user ? $user->{'user'} :
               $dom ? $dom->{'dom'} :
                      $owner->{'name'};  # Assuming 'name' is the relevant field for the owner
my $subject = "$address Password Recovery";
my $emailto = $user ? $user->{'recovery'} :
              $dom ? $dom->{'emailto'} :
                     $owner->{'acl'}->{'email'};

# Use the domain associated with the user or domain for the "From" address
my $from_domain = $user ? $userd->{'dom'} : $dom ? $dom->{'dom'} : 'default-domain.com';  # Fallback to a default domain if extraction fails
my $from_email = "noreply\@$from_domain";  # Set the "From" email address
my $from_name = "No Reply";  # Set the friendly name
my %mail = (
    To      => $emailto,
    From    => "$from_name <$from_email>",
    Subject => $subject,
    Message => $msg,
);


# Send using sendmail
if (sendmail(%mail)) {
    if ($mode == 1 && !$immediate) {
        print "<p>",&text('email_donelink', "<tt>$emailto</tt>"),"</p>\n";
        &popup_footer();
    }
    elsif ($dom) {
        print "<p>",&text('email_done', "<tt>$emailto</tt>", "<tt>$dom->{'dom'}</tt>"),"<p>\n";
        print &text('email_return', "/"),"<p>\n";
        &popup_footer();
        &webmin_log("email", undef, $dom->{'dom'}, { 'email' => $emailto, 'virt' => 1 });
    }
    elsif ($owner) {
        print "<p>",&text('email_done2', "<tt>$emailto</tt>", "<tt>$owner->{'name'}</tt>"),"<p>\n";
        print &text('email_return', "/"),"<p>\n";
        &popup_footer();
        &webmin_log("email", undef, $owner->{'name'}, { 'email' => $emailto, 'vm2' => 1 });
    }
    elsif ($user) {
        print "<p>",&text('email_done3', "<tt>$emailto</tt>", "<tt>$user->{'user'}</tt>"),"<p>\n";
        print &text('email_return', $url),"<p>\n";
        &popup_footer();
        &webmin_log("email", undef, $user->{'user'}, { 'email' => $emailto, 'usermin' => 1 });
    }
} else {
    die "Error sending email: $Mail::Sendmail::error\n";
}

My URL arrives unmolested. I also changed the from, reply-to, and subject. The original used:

webmin-noreply@the-real-hostname.com for the from and has no subject. sendmail defaulted that to “root@the-real-hostname.com” at first so I changed it to:

Friendly name: No Reply
From address: noreply@the-domain-you-are-requesting-the-password-change-of.com
Subject: requested@email-address.com Password Recovery

It looks to be beyond the scope of my abilities to sort out having a different subject for the first and second mail (much less altering the text of the messages) so I’m going to leave it like this for now unless someone has some insight.

your@email-address.com Password Request
your@email-address.com New Password

(etc) would probably be friendlier but that’s not critical.

The cause of the problem is now clear.

This is what @Ron_E_James_D.O read from the raw email:

https://thisisatestdomain.com:10000/virtualmin-password-recovery/email.cgi?id=3D02522c4fbf049a85511636a7a52d167a

As @Ron_E_James_D.O has correctly pointed out, if =3D is replaced by = then the URL works.

As @verne pointed out, the email needs to be viewed in an email viewer.

I looked at a sample password reset email in Thunderbird as plain text. There was no ‘=3D’. I looked at the source from Thunderbird and the ‘=3D’ was present.

But the raw email also stated “This is a multi-part message in MIME format” and also stated

Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

I also put the mail through a spam analysis. It was considered multipart/mixed without a non textual part.

Technically there is no bug because the raw email specifcally said its text part is quoted-printable.

Short of sending the raw email as just plain text without any MIME encoding, the problem cannot be fixed from the perspective of reading it raw.

It is difficult to argue there is a bug, since the raw email specifcally stated its text form was encoded.

But I do agree the points raised are valid. I think at the least Virtualmin should include in their documentation that the raw from of the mail is encoded and specifcally state that ‘=3D’ should be replaced by ‘=’ if using the raw email.

So I think @Ron_E_James_D.O should raise an issue in Github for the documentation to be improved.

As per request, redirection is the name of a web server feature to take a destination URL and return a response to look elsewhere. It is not applicable here.

By the way I have created a post for another way to spin up Virtualmin instances at A friendly introduction to virtualising Virtualmin on Ubuntu with Incus

I don’t understand what changing documentation would do? Is there some thing that I could have done (besides switching to sendmail) to replace =3D with just = that should be in some instruction somewhere?

If I send a test to Outlook 2019 using the original email.cgi, this is what I get:

your message shows the actual Content-Type header …

maybe are more experienced than I — my typical Outlook message never shows the header lines — I wonder if your way of viewing the message has Outlook NOT processing the quoted-printable encoding of the message?

An immediate explanation why a link does not work and is not expected to work from a raw email view.

No. There is nothing wrong.

You are looking at a raw view of the email in Outlook. If you switch to a normal view then ‘=3D’ won’t be present in Outlook as @verne stated.

When a raw email is processed to analyse links it will take into consideration the encoding and deal with ‘=3D’ according to agreed standards. You are looking at a view in Outlook that says give me the raw information and do not apply agreed standards.

1 Like

While you can go a source view of a web page in a browser and select/copy usable links this way, the same does not apply for an email.

Below is what I get using outlook.com, the sucessor to Hotmail.

The link worked and I could not even switch to a raw view.

I don’t know what you mean by the quoted statement above. You could get what you displayed as a normal view if you copied and pasted from a raw mail and sent it to youself. In which case Outlook is doing exactly as you requested: displaying a raw email not with its original encoding but an encoding of a raw email.

If you want to reproduce my result then don’t copy and paste the raw email to send it. Either send it direct or forward it without interference.

Some setting change I made along the way I guess. I tried outlook.com and also had it send to another address that uses Safari and got the 3D both ways:

I am perfectly fine switching it to sendmail.

(Sorry for the delayed reply. I got bogged down in trying to get the Roundcube password reset button to work.)

Suppose your raw email file has name email1.txt.

Suppose I take file email1.txt, view it as intended in an email client by double clicking on the file then the 3D will not be visible, despite being visible in the raw text.

Suppose I forward it as above in the email client. Still the 3D is not visible in an email client of where I forwarded it to.

Suppose I look at the contents of the raw file in an editor, select it, copy it, paste it into an email client and then send it then I will get your results. If you then look at the contents of the file of the received email, call it email2.txt, you will find a double encoding: an encoding of another encoding. This is what is expected. It has also interfered with the original email with regard to how it will display in an email client.

The Postfix provided sendmail command is commonly used to send content from a file as the text body without any further encoding. Strip the headers off the file and it may work as you will not be encoding another encoding..

The point is with your results you are not sending the email as you intend to view it. You are saying to an email client please do not send the email as it intended to be viewed. Instead encode it for sending it as an email client views it before it is processed for viewing.

To view through double clicking the file should be called email1.eml rather than email1.txt, but it can still be opened for viewing as an email no matter what the file name is.

Just strip the email headers, not the MIME headers.

As per RFC 2045 (RFC 2045: Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies), sending non US-ASCII in an email body requires MIME encoding.

“and the decimal value 61 (US-ASCII EQUAL SIGN) can be represented by “=3D”. This rule must be followed except when the following rules allow an alternative encoding.”

Hence this quote is misleading from Quoted-printable - Wikipedia; ‘All printable ASCII characters (decimal values between 33 and 126) may be represented by themselves, except = (decimal 61, hexadecimal 3D, therefore =3D ).’

First ‘=’ is a US-ASCII character.

Second the email does not need to be MIME encoded but it is. So this must be accepted. To ignore this is wrong.

Third if the email is MIME encoded then it is optional to encode ‘=’ as ‘=3D’ or not.

Those are the rules.

The sample below works as I have described: the ‘3D’ does not show in any email clients no matter how sent (as long as I do not copy and paste into an email client).

Email file below rernamed to email1.eml and edited to remove all headers above the Subject line. Also IP address edited and hostname edited in body of email.

Subject: Link For Virtualmin Password Recovery
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="bound1745144005"

This is a multi-part message in MIME format.

--bound1745144005
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

You are receiving this email due to a request for password recovery for
the Virtualmin virtual server hf from 123.123.123.123. To proceed with recovery,
click on this link :
https://example.com:10000/virtualmin-password-recovery/email.cgi?id=3Dd4ec0c34f391bfc4067bde79fa5ef2c6

If you did not make this request, please ignore this email.

--bound1745144005--

If I open this file in Thunderbird (for example with a double click) the ‘3D’ disappears:

If I send this email file from any server with a functioning Postfix installation, using command below, to any recipient with any email client then I get the same result: the ‘3D’ disappears from the email client view.

sendmail user@example.com < email1.eml

Heads up, everyone! Webmin 2.310+ will have built-in password recovery, e.g.:

The “Virtualmin Password Recovery” plugin can be removed in the future.

Advanced users can try development builds of Webmin right now on non-production servers. Password recovery is off by default but can be turned on under “Webmin ⇾ Webmin Configuration: Authentication” page.

@Jamie, maybe we should turn it on by default—most Webmin users don’t have emails set up anyway, so it won’t cause problems, and it’s a good way for users to naturally discover the new feature.

That could be confusing, as the button will appear but not be usable by any users.

Why not? If there’s no email linked to the user, it’ll just say so! Plus, all new virtual servers with the Webmin user feature turned on will have emails set up in miniserv.users automatically. So yeah, I think it’s all good.

What if we just turned it on for Virtualmin by default? We can do that in the postinstall for the next release..

Yeah, I think that’s a great way to go and get people using it.