I have an ASP.Net site hosted in IIS 10 on Windows Server 2019 DataCenter in Azure. This site has only Windows Authentication enabled.
When I access the site directly (http://mysite-backend.example.com) I'm prompted for credentials, after which all works as expected. When I access the site via the App Gateway (configured with private IP) (https://mysite.example.com) I'm prompted for credentials, after which I'm again prompted for requests for additional resources (hosted on the same site, with no special rules for any subfolders). I'm repeatedly prompted until I click Cancel at which point I see a 401 for the resource's request in Chrome's Network log.
One of the resources that I get this issue with is a GET request for favicon.png
. If I access the resource directly (https://mysite.example.com/favicon.png) all works, but when I access the site's page causing a request for this resource to be sent by the browser as part of the page's resources, I see the error. This implies that the resource is accessible; but something about how it's handled within the "transaction" is causing issues.
I've tried with different browsers, including with a fresh install / from a different client device, to ensure there's nothing cached causing issues. Additionally I have Disable Cache
checked in the F12 Network settings to ensure I'm doing complete requests / fair comparisons between the two sites.
The backend pool only has 1 node. The backend settings say to use the backend pool's hostname instead of the frontend's - so from the web server perspective the requests look similar.
In the web server's Security event log I'm seeing failed logons (event id 4625
) with my test username; confirming that the request is making it to the backend server with the right username (and I've been very careful to ensure I'm consistently entering the correct password / have done it many times to remove the chance of human error / have tried both using and also overwriting the saved password). If I try too many times my test account gets locked out (i.e. in AD the lockedout
attribute is set to true
); after which all requests get 401 responses (understandably).
I've checked the App Gateway logs to confirm that it's not blocking anything. I also setup a custom WAF rule for this site and set it to detection only
mode to ensure that it can't block requests; just in case something was getting blocked but not logged.
I enabled failed request tracing for status 401 on the backend server; that shows the 401 as coming from:
-MODULE_SET_RESPONSE_ERROR_STATUS
ModuleName: IIS Web Core
Notification: AUTHENTICATE_REQUEST
HttpStatus: 401
HttpReason: Unauthorized
HttpSubStatus: 2
ErrorCode: Access is denied. (0x80070005)
ConfigExceptionInfo
Note: The resources which are blocked (i.e. get 401 responses) is generally consistent when I'm testing on the same device within a given time period; but if I test a different day or on a different device I may see different resources getting blocked. The first time I test on any device (mostly) I see two xhr POST
requests to /subfolder###/Search
pages fail, then after a few tests on that device other resources seem to become tainted. This feels like they're the main cause - but something then causes an issue with the session.
I'm trying to get access to the backend site's sourcecode to better understand what those Search endpoints have coded in them to understand if that may be causing the issue.
In the meantime, any ideas anyone here may have would be very much appreciated... Thanks in advance.
Update: calling the Search endpoint directly also results in an HTTP 200 / valid response:
$resp = Invoke-RestMethod -Method Post -Uri 'https://mySite.example.com/subfolder###/Search' -Form @{recordsToTake=25;recordsToSkip=0;sortColumn='CategorieName';sortDirection='Ascending'} -Credential $cred -StatusCodeVariable sc;"Status $sc";$resp
NTLM ("Windows Authentication") is not supported by the App Gateway (MS Docs).
It seems that generally reverse proxies don't implement NTLM support due to security concerns (MS YARP NTML Discussion).
There are some alternate reverse proxies which can handle this; e.g. nginx plus (the commercial nginx offerring).