dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.34k stars 9.98k forks source link

Windows Authentication Error using Kestrel for .NET Core Web API, when accessed remotely. #20540

Open Yustos opened 4 years ago

Yustos commented 4 years ago

Error in Windows Authentication using Kestrel still reproduce (package Microsoft.AspNetCore.Authentication.Negotiate, guide https://docs.microsoft.com/ru-ru/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio#kestrel). Same error in related issue. But i collected logs and prepare example to reproduce bug.

Example in branch: https://github.com/Yustos/AspNetCoreSubdomain/commits/auth_trouble Reproduce steps:

You can see error 404 in browser and this topic error in log. Or you can try postman get query with NTLM auth - there will be 401 error and same error in server log.

In the fiddler all three negotiate requests exists, bug without success: № Result Protocol Host URL Body Caching Content-Type Process Comments Custom
7 401 HTTP example.com:5000 / 0 postman:119596
8 401 HTTP example.com:5000 / 0 postman:119596
9 401 HTTP example.com:5000 / 0 postman:119596

There is connection log, as you ask:
auth_log.txt

Remark: i replaced server name to example.com and username from domain to Yustos in attached log.

At last, i was tried HTTP.SYS auth (see commented code in example branch https://github.com/Yustos/AspNetCoreSubdomain/commit/d39dd1f729c34d7ea36f4e807303b1c7687c34a2) - it work fine.

I followed auth example prepare by this guide on windows (client and server): https://docs.microsoft.com/ru-ru/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio#kestrel Except setspn command: https://docs.microsoft.com/ru-ru/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio#windows-environment-configuration

Tratcher commented 4 years ago

The 404 is the exception handler trying to redirect to /Home/Error which doesn't seem to exist in this app.

The actual exception reported in the logs is:

System.InvalidOperationException: An anonymous request was received in between authentication handshake requests.
   at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

Which matches https://github.com/dotnet/aspnetcore/issues/20100 (closed as stale). There wasn't enough information before to debug it, but it looks like we have more now...

When you're doing this authentication are you using the machine's fully qualified active directory name? Using an alternate name isn't going to work unless you've set up proper SPNs.

From the logs: Request 1) Anonymous, authorization failed, Challenge 401 Negotiate Request 2) Authorization: Negotiate (kerberos(?) blob), incomplete handshake, 401 Negotiate (blob) Request 3) Authorization: Negotiate (blob), incomplete handshake, 401 Negotiate (blob) Request 4) Anonymous, exception as shown above.

So the main issue seems to be that the server thinks auth is still in progress but the client has given up. This is likely a misconfiguration related to SPNs or similar. It's odd that the client would send another anonymous request after three consecutive 401s.

@blowdart any idea how to get the associated schannel events to get a more specific explanation here?

Note Http.Sys may have worked because it uses kernel mode authentication which means SPNs are configured on the machine account. With Kestrel it's using user mode which means the SPNs must be configured on the user account instead.

Yustos commented 4 years ago

Hi, Chris! "When you're doing this authentication are you using the machine's fully qualified active directory name?" - yes. In client browser i used full server name http://example.com:5000/ and have auth error. When i use browser on server - all urls variants works fine: http://localhost:5000, http://example:5000 and http://example.com:5000.

For clear - 404 is strange, but not a root trouble. Problem is authentication.

Postman failed on auth without any additional potential requests (like js, css, favico and other).

To reduce any browser specific network activity i tried powershell:

PS C:\Users\Yustos> $url = "http://example.com:5000"
PS C:\Users\Yustos> $wc = New-Object System.Net.WebClient
PS C:\Users\Yustos> $wc.UseDefaultCredentials = $true
PS C:\Users\Yustos> $response = $wc.DownloadString($url)
Exception calling "DownloadString" with "1" argument(s): "The remote server returned an error: (401) Unauthorized."
At line:1 char:1
+ $response = $wc.DownloadString($url)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

And here is server log: auth_log_by_powershell.txt

Will fiddler logs help you? Thank you for advice!

PS: i tried to apply setspn, but failed:

>setspn -S HTTP/example.com Yustos
Checking domain DC=---,DC=---

Registering ServicePrincipalNames for CN=Yustos,OU=Computers,OU=Clients,DC=---,DC=---
        HTTP/example.com
Failed to assign SPN on account 'CN=Yustos,OU=Computers,OU=Clients,DC=---,DC=---',
 error 0x2098/8344 -> Insufficient access rights to perform the operation.

I suppose this is not important.

Yustos commented 4 years ago

Okay, i am collected fiddler logs for powershell with UseDefaultCredentials calls (HTTP-bodies excluded, tokens shortened). Success with HTTP.SYS: success_http_sys.txt

Fail with Kestrel negotiate: fail_kestrel_negotiate.txt

I am not an Active Directory administrator, so setspn unpermitted. And i believe, that i should not ask administrator to register SPN for every test scenario or dynamically generated urls. So my way is HTTP.SYS or other authentication mechanism.

Last thing is error code. In the attached failure-logs was 400 error after series of handshakes. But sometimes returned error 404 without any handshake: image Here details for 404:

GET http://example.com:5000/ HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; ru-RU) WindowsPowerShell/5.1.18362.628
Host: example.com:5000

HTTP/1.1 404 Not Found
Date: Sun, 05 Apr 2020 07:29:13 GMT
Server: Kestrel
Content-Length: 0
Cache-Control: no-cache
Pragma: no-cache
Expires: -1

Looks like is one-after-one 400, 404, 400, 404...

Well, confirm, please, that setspn is the strong requirement for Negotiate and close issue. Thank you!

Tratcher commented 4 years ago

Can you share the server logs for the 400 scenario?

Yes, SPNs are a strong requirement for Negotiate/Kerberos. When you run on the same machine it always defaults to NTLM which does not have the same SPN requirements. When it runs with HttpSys from a separate machine it's using kernel mode auth and the machine account which already has SPNs configured by default. When it's using Kestrel and user-mode auth from a remote machine it can't find any SPNs in the user account, tries to fall back to NTLM, but then gives up after a few round trips (not sure why yet).

@JunTaoLuo this would be a good situation for you to repro with your current setup.

Tratcher commented 4 years ago

The 404 is likely the same redirect to /home/error as before. The server logs would show that.

Yustos commented 4 years ago

I did test again and this was my false - 400/404 problem is valid when i use fiddler as proxy to client-side requests sniffing. This command will produce 400/404 errors:

Invoke-WebRequest -Uri "http://example.com:5000" -UseDefaultCredentials -Proxy http://localhost:8888

But this is stable 401:

Invoke-WebRequest -Uri "http://example.com:5000" -UseDefaultCredentials
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.
At line:1 char:1
+ Invoke-WebRequest -Uri "http://example.com:5000" -UseDefaultCredent ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
   eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Here is server log for 401 without proxy (single call): server.txt

Anyway, i can not use Kestrel negotiate with SPN's. I want to use dynamic subdomains and i have no opportunity to dynamically register SPN's in Active Directory. For my case HTTP.SYS and IIS works fine :(

Chris, thank you for explanation about SPN! I'm sorry for your time.

JunTaoLuo commented 4 years ago

FYI, the 401s we were seeing seem to be caused by the missing SPNs. I am able to reproduce the 401 errors on my local VMs when the SPNs are not configured and I am able to run your repro project successfully when I do have the SPN configured correctly. I think all the questions here have been resolved, please let us know if you have any other issues @Yustos

Tratcher commented 4 years ago

@JunTaoLuo did you ever see the InvalidOperationException?

analogrelay commented 4 years ago

Triage: @JunTaoLuo is going to take a quick look at a few other clients (Legacy Edge/Edgium, etc.) to see if we can repro the InvalidOperationException. We do require an SPN though so I don't think there's much we can do about that. Using HttpSys is probably a better option in this scenario.

analogrelay commented 4 years ago

@JunTaoLuo was able to identify that Edge Chromium does indeed produce this error when SPNs aren't configured. We're forwarding the details on to that team for further investigation.