When updating to 1.0 I am no longer able to sign in to the WebUI via any means, getting the “Unknown client id” error as described in Installation and the WebUI | Stalwart
I am hosting the WebUI behind a VPN via a different IP address than the publicly facing email services. Before the 1.0 upgrade this configuration worked just fine and actually has superior security to the newly mandated configuration, where I must expose the WebUI to the internet.
I agree that the new behavior is better for 95% of installations, but leaving an option to disable SSL termination for the WebUI is necessary to enable objectively more secure configurations such as mine.
STALWART_RECOVERY_MODE=1 is insufficient for this case as I want the email server to be functional AND be able to access the WebUI without SSL termination as a supported steady state.
Expected Behavior
There should be some configuration option to opt-in to the old behavior for users who know what they are doing (eg. STALWART_DISABLE_WEBUI_SSL=1).
Actual Behavior
There is no possible configuration where I can both serve email and connect to the mail server over a VPN.
Reproduction Steps
start stalwart with default configuration
allow email related ports through the firewall on the public interface
allow WebUI related ports through the firewall on the VPN interface
the WebUI rejects all connection attempts with “Unknown client id”
Relevant Log Output
none
Stalwart Version
v1.x
Installation Method
Built from source
Database Backend
PostgreSQL
Blob Storage
PostgreSQL
Search Engine
PostgreSQL
Directory Backend
SQL
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
You don’t need a “disable SSL” switch, and you don’t need to expose the WebUI publicly. The WebUI only has to be recognised as a first-party OAuth client on the address you open it on, and v1.0 gives you two ways to arrange that. The docs I found don’t describe a switch that turns off the check.
The WebUI is only treated as first-party on the server’s trusted hosts. These are the host of its public https URL and the hostnames of the configured JMAP, CalDAV, CardDAV and WebDAV services. Any other hostname or IP, or the plain HTTP listener on 8080, gives Unknown client id. To use your VPN address or name, do one of the following:
Add the VPN hostname as a service hostname.
Register an OAuthClient with firstParty enabled for it.
The docs say the first option works for a hostname. I’m not certain it works for a bare IP address. If it doesn’t, give the VPN address a hostname through VPN DNS or /etc/hosts.
If your problem is that the WebUI shouldn’t be reachable from the internet, you can also restrict /admin with the allowedEndpoints expression of the Http object. That lets you leave the other paths public while limiting the admin interface to the VPN source range. See How do I limit the admin interface to my internal network? and HTTP access control.
I don’t have or want to configure an OAuth server, the built in user management is what I am using. Adding the bare IP address to /etc/hosts and restarting stalwart had no effect on behavior. Please provide specific actions to resolve if you are going to claim to have a solution.
Sorry, the bot’s answer didn’t fit your setup. The check isn’t about which interface or network the WebUI is reached on: the server accepts the WebUI’s sign-in only when the browser opens it over HTTPS on one of the server’s own hostnames (the host of its public URL, or a JMAP/CalDAV/CardDAV/WebDAV service hostname). That’s why editing /etc/hosts on the server changed nothing, since what matters is the host the browser asks for.
So you can keep the WebUI off the internet and still sign in:
On the VPN side, make your mail hostname (say mail.example.com) resolve to the server’s VPN address. A hosts entry on your workstation or a split-DNS record on the VPN’s resolver both work.
Let the HTTPS listener (443 by default) through the firewall on the VPN interface only, and keep it closed on the public one.
Open https://mail.example.com/admin from the VPN. The certificate you already use for IMAP and SMTP covers that name, so TLS works and the sign-in is accepted.
If you also want the server itself to refuse /admin from anywhere outside the VPN, add a rule to allowedEndpoints on the Http object that tests remote_ip and path: Access control | Stalwart
Understood, with these instructions I was able to get further but am still am not fully able to login. I configured https on port 8080 as 443 is not available on this server and configured /etc/hosts on the requesting server.
When connecting to https_://mail.examile.com:8080/ using the steps you suggested I do successfully terminate TLS but unfortunately the login request is sent to https_://mail.examile.com, not https_://mail.examile.com:8080, so it naturally does not succeed. It seems a bit strange that the login request is not directed at window.location.host when using the built in auth.
The sign-in page address comes from the server’s public URL, which defaults to https://<hostname> on port 443, so the browser is sent there even though you opened the WebUI on 8080. Tell the server its public URL includes the port by setting this environment variable and restarting:
STALWART_PUBLIC_URL=https://mail.example.com:8080
Since HTTPS is only reachable on 8080 anyway, this doesn’t change anything for your public mail ports; it only affects the URLs Stalwart publishes for its HTTP endpoints (OAuth, JMAP, autoconfig).