Closed OrvilleQ closed 2 weeks ago
This is because your proxy is returning a forbidden response which is the Stalwart response that the webadmin uses to detect a missing TOTP token.
@mdecimus Thanks for your reply. I do found some 403 response inside nginx's access.log
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:52 +0000] "GET / HTTP/2.0" 200 2137 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:53 +0000] "GET /output-fa0d16a5e0b2aecd.css HTTP/2.0" 200 61593 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:53 +0000] "GET /webadmin-338ec427211aa677.js HTTP/2.0" 200 58864 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:54 +0000] "GET /webadmin-338ec427211aa677_bg.wasm HTTP/2.0" 200 5599314 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:54 +0000] "GET /android-chrome-512x512-ffbecec3ff69a6ca.png HTTP/2.0" 200 104372 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /site.webmanifest HTTP/2.0" 200 263 "https://mai.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /logo.svg HTTP/2.0" 200 3523 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /android-chrome-192x192-6aba841e8fc78b83.png HTTP/2.0" 200 21505 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /favicon-16x16-9b86a4caa1876c9e.png HTTP/2.0" 200 812 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:56 +0000] "GET /favicon-32x32-33abe4bf0e53934c.png HTTP/2.0" 200 1784 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:56 +0000] "GET /favicon-cfb706b7132e0fd2.ico HTTP/2.0" 200 15406 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - 123 [27/Aug/2024:13:52:57 +0000] "POST /api/oauth HTTP/2.0" 403 548 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
To me it seems like the 403 response is came from the stalwart server rather than nginx?
EDIT: It seems like the POST request is blocked by modsecurity.
Log from Modsecurity.
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0
origin: https://mail.masked.com
sec-ch-ua-mobile: ?0
content-type: text/plain;charset=UTF-8
accept: */*
sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"
sec-ch-ua-platform: "Windows"
referer: https://mail.masked.com/
content-length: 58
priority: u=1, i
authorization: Basic YWRtaW46eE5ENmJUV3FqZQ==
host: mail.masked.com
sec-fetch-mode: cors
sec-fetch-dest: empty
accept-encoding: gzip, deflate, br, zstd
accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
---jiBGgn7T---C--
{"type":"Code","client_id":"webadmin","redirect_uri":null}
---jiBGgn7T---F--
HTTP/2.0 403
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), usb=(), web-share=(), xr-spatial-tracking=()
Feature-Policy: accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; execution-while-not-rendered 'none'; execution-while-out-of-viewport 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; layout-animation 'none'; legacy-image-formats 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; navigation-override 'none'; payment 'none'; picture-in-picture 'none'; publickey-credentials-get 'none'; speaker-selection 'none'; sync-xhr 'none'; unoptimized-images 'none'; unsized-media 'none'; usb 'none'; screen-wake-lock 'none'; web-share 'none'; xr-spatial-tracking 'none';
Date: Tue, 27 Aug 2024 14:02:10 GMT
X-XSS-Protection: 1; mode=block
Connection: close
X-Powered-By:
X-Content-Type-Options: nosniff
Content-Type: text/html
Content-Length: 548
Server:
Server:
Content-Security-Policy: object-src 'none'; form-action 'self'; frame-ancestors 'self';
Strict-Transport-Security: max-age=31536000
Expect-CT:
X-AspNet-Version:
X-AspNetMvc-Version:
---jiBGgn7T---H--
ModSecurity: Warning. Matched "Operator `Within' with parameter `|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudev (16 characters omitted)' against variable `TX:content_type' (Value: `|text/plain|' ) [file "/usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "938"] [id "920420"] [rev ""] [msg "Request content type is not allowed by policy"] [data "|text/plain|"] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/255/153"] [tag "PCI/12.1"] [hostname "49.xxx.xxx.xxx"] [uri "/api/oauth"] [unique_id "172476733036.320579"] [ref "o0,10v394,24t:lowercase"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "81"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "49.xxx.xxx.xxx"] [uri "/api/oauth"] [unique_id "172476733036.320579"] [ref ""]
---jiBGgn7T---Z--
Seems like the POST request is using content_type "text/plain", which is not allowed by Modsecurity's rule 920420. The allowed content types by this rule are `|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudev (16 characters omitted).
I will check if there is a way to temporarily disable this rule. Also can this issue be considered as an improvement to be addressed later? @mdecimus
Something else is generating that content-type header. Stalwart does not return text/plain
unless you are accessing a text file such asrobots.txt
, mta-sts.txt
, etc.
Something else is generating that content-type header. Stalwart does not return
text/plain
unless you are accessing a text file such asrobots.txt
,mta-sts.txt
, etc.
It's not Stalwart return text/plain
, it's the login in interface POST /api/oauth
and /api/token
with text/plain
.
Maybe I should reopen this issue at https://github.com/stalwartlabs/webadmin?
What happened?
After running the install.sh script, I set up a reverse proxy and accessed the site using HTTPS. When I tried to log in with the admin account, it asked for a TOTP challenge, which I never set up. This issue does not occur when accessing the site directly via plain HTTP on port 8080.
I can't find any error inside the log file. but in the webui's log tacing page, there is an error called X-Forwarded-For header is missing.
Which is also wired. I enabled the Obtain remote IP from Forwarded header, and the request from nginx has X-Forwarded-For set corectly also. Not sure if this is related to the issue.
How can we reproduce the problem?
Version
v0.9.x
What database are you using?
None
What blob storage are you using?
None
Where is your directory located?
None
What operating system are you using?
Linux
Relevant log output
Code of Conduct