Password reset by email
Purpose. Until this, a forgotten admin-console password had no recovery short of editing the database: no route or screen sets another user’s password. Now the sign-in page offers Forgot your password?, and the account is mailed a link that sets a new one.
Behaviour.
- Forgot your password? on the admin sign-in opens
/forgot-password. The page asks for the sign-in address and always answers the same: if that address has an admin account, a reset link is on its way.POST /auth/password-resetanswers202before it looks the address up or sends anything, so neither the body nor the time taken says whether the account exists. - The link is
<admin panel>/reset-password?token=…, mailed fromnoreply@<the server's first domain>. It works once, for 30 minutes. The token is 32 random bytes; only its SHA-256 is stored (password_reset_token), so a database dump holds no working link. - Setting the new password (at least 10 characters, typed twice) signs the account out of every console session and of every mailbox it had opened on someone’s behalf, deletes any other outstanding link, and clears the address’s failed-sign-in count. It is audited (
auth.password_reset.requested,auth.password_reset.completed). - Two-factor sign-in is untouched. A reset changes the password only; an account with TOTP still needs its authenticator or a recovery code (Admin UI → Account). Somebody who can read the inbox cannot get past the second factor with it.
- Throttled twice over. At most one link per account every two minutes (a second request inside the window sends nothing, and still answers
202), and five requests per client IP address per 15 minutes (429, which says nothing about any account).
Configuration. None. The noreply@ mailbox is a hidden system mailbox, created on the first request in the domain setup created first; it is not listed, not counted against the licence, and refused at webmail sign-in; its password is random and never shown. Mail leaves through the normal outbound path, relay included.
Limits & edge cases.
-
Under
tls.mode=nonethe link’s origin comes from the request’sHostheader (adminOriginFor), because there is no hostname to build it from; a request with a forgedHostgets a link to that host mailed to the account.noneneeds a loopback bind or the explicit public-bind escape, so this is a development configuration; with TLS on, the origin is alwayshttps://postmaster.<mail host's domain>. -
Admin-console accounts only. A mailbox owner’s obvious address is the mailbox they cannot get into; resetting those needs a second address per mailbox and is not built. An administrator still resets a mailbox password on the mailbox’s page.
-
A server that cannot send mail cannot send the link. Outbound mail has to work (port 25 or a relay);
mailctl doctorchecks it. A failed send is logged by the API (password reset: sending the link failed), never shown to the person asking. -
The link still arrives when the licence has stopped outbound mail. From the licence’s
send_suspendedstage Postfix refuses every recipient not hosted here (Licensing → What each stage refuses), except mail fromnoreply@<first domain>: that system mailbox is the one sender the stop lets through, so a lapsed server can still mail an administrator a link. If a mailbox of your own already holdsnoreply@in the first domain, the server sends from it but does not exempt it, and at that stage the link reaches only addresses on this server. -
If an alias already holds
noreply@<first domain>, no link can be sent from it and requests are dropped with a warning in the API log. A mailbox of that name is used as the sender as it is. -
An inactive account is never mailed, and a link minted before an account was deactivated is refused.
-
The limits are per API process and in memory for the IP throttle; a restart resets that count, not the per-account cooldown, which is in the database.