Description
environment:
DC: windows server 2012R2
server1: windows server 2016, ASP.NET core 2.2.4 web API on IIS with windows authentication enabled
server2: windows server 2012R2, ASP.NET core 2.2 web api on IIS with windows authentication enabled
client: any browsers
client -> server1 -> server2.
the ASP.NET core app in server1 will impersonate the authenticated user then call server 2 web api.
i have configured app pools on both servers to use service account, and have configured SPN accordingly, added delegation setting for the service account of server1.
sample code of how i do impersonate:
var windowsIdentity = (WindowsIdentity)this.HttpContext.User.Identity;
await (WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "http://winsvr12r2-1.jiangya1.lab:4000/api/values"))
{
string user_name = WindowsIdentity.GetCurrent().Name;
try
{
var response = await _client.SendAsync(requestMessage);
var responseString = await response.Content.ReadAsStringAsync();
result = ($"Called API as: {WindowsIdentity.GetCurrent().Name}.\n\n" +
$"{responseString}\n");
}
catch (Exception ex)
{
result = ex.ToString();
}
}
}));
return result;
expected scenario: the impersonation process would succeed,and server2 gets requests from the initial credential.
real scenario: server1 would throw following exception:
System.Net.Http.HttpRequestException: This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server ---> System.Net.Sockets.SocketException: This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at WebApplication2.Controllers.PassThroughController.<>c__DisplayClass5_0.<<Get>b__0>d.MoveNext() in C:\Users\jiangya\source\repos\WebApplication2\WebApplication2\Controllers\PassThroughController.cs:line 92
further troubleshooting:
while doing debugging, i found that if i turn off SocketsHttpHandler following this
and use HTTPClientHandler, the issue will get resolved. which seems indicate that ASP.NET core impersonation is not working with SocketsHttpHandler.
i also attached full callstack here:
working-callstack.txt
non-working-callstack.txt
I understand the new socket handler is designed to eliminate platform dependencies, however can you suggest how to make it work with impersonation?
non-working callstack with SocketsHttpHandler:
0:030> !\\**********\shared\Microsoft.NETCore.App\2.2.4\sos.clrstack
OS Thread Id: 0xe00 (30)
Child SP IP Call Site
000000d9e19fbf78 00007ffca5a60160 Interop+Winsock.GetAddrInfoExW(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fbf78 00007ffc343c65a4 Interop+Winsock.GetAddrInfoExW(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fbf20 00007ffc343c65a4 DomainBoundILStubClass.IL_STUB_PInvoke(System.String, System.String, Int32, IntPtr, System.Net.Sockets.AddressInfoEx ByRef, System.Net.Sockets.AddressInfoEx* ByRef, IntPtr, System.Threading.NativeOverlapped ByRef, LPLOOKUPSERVICE_COMPLETION_ROUTINE, IntPtr ByRef)
000000d9e19fc040 00007ffc8aaafba8 System.Net.NameResolutionPal.GetAddrInfoAsync(System.Net.DnsResolveAsyncResult)
000000d9e19fc110 00007ffc8aaac27b System.Net.Dns.HostResolutionBeginHelper(System.String, Boolean, Boolean, System.AsyncCallback, System.Object)
000000d9e19fc190 00007ffc8aaacea8 System.Net.Dns.BeginGetHostAddresses(System.String, System.AsyncCallback, System.Object)
000000d9e19fc1e0 00007ffc8ab1bcc2 System.Net.Sockets.MultipleConnectAsync.StartConnectAsync(System.Net.Sockets.SocketAsyncEventArgs, System.Net.DnsEndPoint)
000000d9e19fc250 00007ffc8ab08f23 System.Net.Sockets.Socket.ConnectAsync(System.Net.Sockets.SocketType, System.Net.Sockets.ProtocolType, System.Net.Sockets.SocketAsyncEventArgs)
000000d9e19fc2b0 00007ffc343c5d98 System.Net.Http.ConnectHelper+d__2.MoveNext()
000000d9e19fc3a0 00007ffc343c5759 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fc410 00007ffc8a95726e System.Net.Http.ConnectHelper.ConnectAsync(System.String, Int32, System.Threading.CancellationToken)
000000d9e19fc4d0 00007ffc8a95afae System.Net.Http.HttpConnectionPool+d__44.MoveNext()
000000d9e19fc6e0 00007ffc343c51e9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fc750 00007ffc8a95950e System.Net.Http.HttpConnectionPool.CreateConnectionAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fc850 00007ffc343c4d29 System.Net.Http.HttpConnectionPool.GetConnectionAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fc9f0 00007ffc8a95a662 System.Net.Http.HttpConnectionPool+d__39.MoveNext()
000000d9e19fcaf0 00007ffc343c3ff9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fcb60 00007ffc8a959225 System.Net.Http.HttpConnectionPool.SendWithRetryAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcc00 00007ffc8a9593ee System.Net.Http.HttpConnectionPool.SendWithProxyAuthAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcc60 00007ffc8a9457a2 System.Net.Http.AuthenticationHelper+d__16.MoveNext()
000000d9e19fcd60 00007ffc343c3cf9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fcdd0 00007ffc8a9443fb System.Net.Http.AuthenticationHelper.SendWithAuthAsync(System.Net.Http.HttpRequestMessage, System.Uri, System.Net.ICredentials, Boolean, Boolean, Boolean, System.Net.Http.HttpConnectionPool, System.Threading.CancellationToken)
000000d9e19fce90 00007ffc8a95946c System.Net.Http.HttpConnectionPool.SendAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fcef0 00007ffc8a95c79c System.Net.Http.HttpConnectionPoolManager.SendAsyncCore(System.Net.Http.HttpRequestMessage, System.Uri, Boolean, Boolean, System.Threading.CancellationToken)
000000d9e19fcfd0 00007ffc8a95c93a System.Net.Http.HttpConnectionPoolManager.SendAsync(System.Net.Http.HttpRequestMessage, Boolean, System.Threading.CancellationToken)
000000d9e19fd030 00007ffc8a958cea System.Net.Http.HttpAuthenticatedConnectionHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd060 00007ffc8a95f388 System.Net.Http.RedirectHandler+d__4.MoveNext()
000000d9e19fd100 00007ffc343c2729 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd170 00007ffc8a95efba System.Net.Http.RedirectHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd1f0 00007ffc8a95ead2 System.Net.Http.SocketsHttpHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd240 00007ffc8a93a259 System.Net.Http.HttpClientHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd290 00007ffc8a93ce42 System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
000000d9e19fd2e0 00007ffc8a936954 System.Net.Http.HttpClient.SendAsync(System.Net.Http.HttpRequestMessage, System.Net.Http.HttpCompletionOption, System.Threading.CancellationToken)
000000d9e19fd350 00007ffc343bf2fd Error: Failed to load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\coreclr.dll
WebApplication2.Controllers.PassThroughController+c__DisplayClass5_0+b__0>d.MoveNext()
000000d9e19fd3c0 00007ffc343bf109 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd430 00007ffc343bf098 WebApplication2.Controllers.PassThroughController+c__DisplayClass5_0.b__0()
000000d9e19fd4a0 00007ffc8ba09446 System.Security.Principal.WindowsIdentity+c__DisplayClass60_0`1 .b__0()
000000d9e19fd4d0 00007ffc8ba09517 System.Security.Principal.WindowsIdentity+c__DisplayClass64_0.b__0(System.Object)
000000d9e19fd510 00007ffc8d3c2019 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000d9e19fd590 00007ffc8ba07993 System.Security.Principal.WindowsIdentity.RunImpersonatedInternal(Microsoft.Win32.SafeHandles.SafeAccessTokenHandle, System.Action)
000000d9e19fd5e0 00007ffc8ba077df System.Security.Principal.WindowsIdentity.RunImpersonated
000000d9e19fd630 00007ffc343be842 WebApplication2.Controllers.PassThroughController+d__5.MoveNext()
000000d9e19fd690 00007ffc343be5d9 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start
000000d9e19fd700 00007ffc343be558 WebApplication2.Controllers.PassThroughController.Get()
working callstack with HttpClientHandler:
0:014> k
Child-SP RetAddr Call Site
00 000000b3`80a7e140 00007ffc`a5a5ec39
ws2_32!WinsockThreadpool_CreateWorkContext+0x9b
01 000000b3`80a7e180 00007ffc`a5a59684 ws2_32!WinsockThreadpool_SubmitWork+0x45
02 000000b3`80a7e1d0 00007ffc`a5a58009 ws2_32!NSQUERY::LookupServiceNextAsync+0x154
03 000000b3`80a7e250 00007ffc`a5a56b67 ws2_32!WSALookupServiceNextAsyncW+0x169
04 000000b3`80a7e2b0 00007ffc`a5a57070 ws2_32!NSSUBJOB::LookupAsyncAndHandleResultsQDNS4F+0xb7
05 000000b3`80a7e340 00007ffc`a5a5717e ws2_32!NSSUBJOB::TrySubmitQDNS4F+0x6c
06 000000b3`80a7e390 00007ffc`a5a56814 ws2_32!NSSUBJOB::TrySubmit+0x8e
07 000000b3`80a7e3f0 00007ffc`a5a55f06 ws2_32!NSJOB::SubmitChild+0x44
08 000000b3`80a7e440 00007ffc`a5a56329 ws2_32!NSJOB::ForEachChild+0xd6
09 (Inline Function) --------`-------- ws2_32!NSJOB::ForEachChild+0x12
0a 000000b3`80a7e490 00007ffc`a5a57201 ws2_32!NSJOB::TrySubmit+0x89
0b 000000b3`80a7e4f0 00007ffc`a5a56814 ws2_32!NSSUBJOB::TrySubmit+0x111
0c 000000b3`80a7e550 00007ffc`a5a55f06 ws2_32!NSJOB::SubmitChild+0x44
0d 000000b3`80a7e5a0 00007ffc`a5a56329 ws2_32!NSJOB::ForEachChild+0xd6
0e (Inline Function) --------`-------- ws2_32!NSJOB::ForEachChild+0x12
0f 000000b3`80a7e5f0 00007ffc`a5a55913 ws2_32!NSJOB::TrySubmit+0x89
10 000000b3`80a7e650 00007ffc`a5a6145c ws2_32!NSMASTERJOB::TrySubmit+0x53
11 000000b3`80a7e690 00007ffc`99109ac1 ws2_32!GetAddrInfoExW+0x12fc
12 000000b3`80a7e8d0 00007ffc`991099d1 webio!WapDnsResolveGeneric+0xd9
13 000000b3`80a7e9c0 00007ffc`99108e3e webio!WapStartDnsIoContext+0x31
14 000000b3`80a7e9f0 00007ffc`99108a6e webio!WaStartDnsQuery+0x21e
15 000000b3`80a7eab0 00007ffc`99108fa4 webio!WapTcpStartDnsQuery+0x38e
16 000000b3`80a7eb40 00007ffc`991090b7 webio!WaTcpConnectRequest+0x54
17 000000b3`80a7eb90 00007ffc`99102f44 webio!WapHttpConnect+0x107
18 000000b3`80a7ec20 00007ffc`99102da8 webio!WaQueueHttpRequest+0x144
19 (Inline Function) --------`-------- webio!WapStartQueueRequest+0x1c
1a (Inline Function) --------`-------- webio!WapGetConnectionCompletionRoutine+0x5c
1b 000000b3`80a7ecc0 00007ffc`99102c88 webio!WapStartGetConnection+0xf8
1c 000000b3`80a7ed10 00007ffc`99102b16 webio!WapStartGetEndpoints+0x44
1d 000000b3`80a7ed40 00007ffc`99105008 webio!WapAsynchronousSendHttpRequest+0x266
1e (Inline Function) --------`-------- webio!WapSendHttpRequest+0x2f
1f 000000b3`80a7ee20 00007ffc`9f001c65 webio!WebSendHttpRequest+0x38
20 (Inline Function) --------`-------- winhttp!WEBIO_SENDER::SendRequest+0x21d
21 000000b3`80a7ee80 00007ffc`9f007528 winhttp!WEBIO_REQUEST::SendRequest+0x415
22 000000b3`80a7ef70 00007ffc`9f007b60 winhttp!HTTP_USER_REQUEST::_SysSendRequest+0x1028
23 000000b3`80a7f170 00007ffc`9f00783b winhttp!HTTP_USER_REQUEST::_SendRequestWithDrainComplete+0x2d0
24 000000b3`80a7f200 00007ffc`9f0023fb winhttp!HTTP_USER_REQUEST::_SendRequestWithProxyInitialized+0x6b
25 000000b3`80a7f230 00007ffc`9efe3a57 winhttp!HTTP_USER_REQUEST::OnProxyResolvedOnSend+0xbf
26 000000b3`80a7f280 00007ffc`9efe3710 winhttp!UserRequestProxyCompletion::OnProxyResolved+0x27
27 000000b3`80a7f2b0 00007ffc`9efe351e winhttp!WxProxyManager::WxProxyFailoverCompletionRoutine+0x90
28 000000b3`80a7f2e0 00007ffc`a60dec59 winhttp!RefCountWorkItemThread+0x1e
29 000000b3`80a7f310 00007ffc`a60c328a ntdll!RtlpTpWorkCallback+0x129
2a 000000b3`80a7f3f0 00007ffc`a4e98364 ntdll!TppWorkerThread+0x4aa
2b 000000b3`80a7f7f0 00007ffc`a60f7091 KERNEL32!BaseThreadInitThunk+0x14
2c 000000b3`80a7f820 00000000`00000000 ntdll!RtlUserThreadStart+0x21