Locked out of the console by a certificate
When to use: a browser refuses postmaster.<your domain>, webmail.<your domain> or the mail hostname with a
certificate error (NET::ERR_CERT_DATE_INVALID, SEC_ERROR_EXPIRED_CERTIFICATE, a name mismatch) and offers no
“proceed anyway” link, or mailctl doctor reports a certificate … row as ✖.
Preconditions: SSH to the host. Nothing here needs the console, which is the point.
Why there is no way past it
Section titled “Why there is no way past it”The admin panel and the mail client send Strict-Transport-Security (six months). A browser that has seen it refuses
to let anybody click past a certificate error on those names until the header expires, so a certificate that lapses,
or one that does not name the host, locks that browser out instead of showing a warning. This is deliberate: the
warning is what an attacker in the middle would show too. Mail clients (IMAP, SMTP) do not read the header, but they
refuse a bad certificate on their own, so they are probably failing as well.
The fix is a valid certificate. As soon as one is back, every browser works again. Waiting does not fix it: a browser keeps the header for six months after it last saw it.
-
See what is wrong, on the host:
Terminal window mailctl doctorThe
certificate <name>rows say, per name, whether a browser would accept the certificate and when it expires:CERT_HAS_EXPIRED,ERR_TLS_CERT_ALTNAME_INVALID(the certificate does not name that host) orDEPTH_ZERO_SELF_SIGNED_CERT. To look by hand:Terminal window openssl s_client -connect 127.0.0.1:443 -servername postmaster.<your domain> </dev/null 2>/dev/null |openssl x509 -noout -subject -ext subjectAltName -dates -
Let’s Encrypt certificates (
tls.mode: acme). Caddy renews 30 days before expiry, so an expired one means renewal has been failing for weeks. Read why:Terminal window docker logs mailserver-caddy-1 2>&1 | grep -iE 'acme|obtain|renew|challenge' | tail -20The usual causes: port 80 closed by a firewall (the HTTP challenge needs it), the name’s DNS record no longer points at this host, or a Let’s Encrypt rate limit after many failures. Fix the cause. Caddy keeps retrying with back-off;
docker restart mailserver-caddy-1makes it try now, and it keeps the configuration it was running. -
Your own certificate (
tls.mode: byo). Copy the new chain and key into thecertsvolume, exactly as for a renewal (Bring your own TLS certificate → Steps 3), then restart the edge so it loads them:Terminal window docker run --rm -v mailserver_certs:/certs -v "$PWD":/in alpine \sh -c 'cp /in/fullchain.pem /certs/byo/fullchain.pem && cp /in/privkey.pem /certs/byo/privkey.pem && chown 2525:2525 /certs/byo/*.pem && chmod 640 /certs/byo/privkey.pem'docker restart mailserver-caddy-1cert-synchands the same certificate to Postfix and Dovecot within 30 s. The certificate must name the mail hostname,postmaster.andwebmail.: one that misses a name locks that name out the same way. -
Do not switch TLS off to get in.
tls.mode: noneis refused on a public address, and a browser that has seen the header would refuse the plain-HTTP console anyway.
Verification
Section titled “Verification”mailctl doctor shows every certificate … row ✔ with a future expiry, and https://postmaster.<your domain>/
opens in a browser that was locked out, with nothing cleared on its side.
If one browser still refuses after that, it has cached the bad certificate’s session: close it completely and open
it again. As a last resort, and only for that one browser, Chrome forgets the header at chrome://net-internals/#hsts
(Delete domain security policies); Firefox forgets it when you Forget About This Site in its history.
Rollback
Section titled “Rollback”Nothing here changes configuration. A certificate restored over SSH is the one the server was supposed to have.