Reputation: 141
I am attempting to perform Kerberos authentication from a Client App (Windows Forms) to a WCF Web Service on one Windows 2008 R2 server running under IIS, which in-turn calls another WCF service running on another Windows 2008 R2 server also running under IIS. I've seen this referred to this as a Kerberos Double-Hop Authentication.
When I locate the two web services on the same Windows 2008 R2 server then our double-hop authentication works fine. However, when we move the second WCF service to a different server the authentication fails between the two web services. I do not know what causes this problem which may be a configuration issue, or something in our server/network set-up. Client and servers all exist in the same domain.
Here is more detail of what I have done so far. I've tried many of the suggestions in other related topics/questions about this issue, but no joy so far.
Hopefully someone who has dealt with 'double hop' WCF Kerberos authentication before may recognise this issue and be able to assist me.
Many thanks
CLIENT APP CONFIG
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IMiddleService">
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="DelegationBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Delegation" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://SERVER1/KerberosMiddleService/MiddleService.svc"
behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IMiddleService" contract="KerberosMiddleService.IMiddleService"
name="WSHttpBinding_IMiddleService">
<identity>
<servicePrincipalName value="HTTP/SERVER1.int.mydomain.com"/>
<userPrincipalName value="MYDOMAIN\MY-HOST_ACCOUNT@int.mydomain.com"/>
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
MIDDLE SERVICE WEB CONFIG
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<compilation targetFramework="4.0"/>
<httpRuntime/>
<customErrors mode="Off"/>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IEndService">
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default"/>
</security>
</binding>
<binding name="WSHttpBinding_IEndService1">
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization impersonateCallerForAllOperations="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="DelegationBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Delegation" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://SERVER2/endservice/endservice.svc"
behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IEndService" contract="KerberosEndService.IEndService"
name="WSHttpBinding_IEndService">
</endpoint>
<endpoint address="http://SERVER1/kerberosendservice/endservice.svc"
behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IEndService1" contract="BT01_KerberosEndService.IEndService"
name="WSHttpBinding_IEndService1">
</endpoint>
</client>
<protocolMapping>
<add binding="wsHttpBinding" scheme="http"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
END SERVICE WEB CONFIG
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<customErrors mode="Off"/>
<compilation targetFramework="4.0"/>
<httpRuntime/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization impersonateCallerForAllOperations="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme="http"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
A SECTION OF THE WCF STACK TRACE
<ExceptionString>System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---&gt; System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
--- End of inner exception stack trace ---</ExceptionString><InnerException><ExceptionType>System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The request for security token could not be satisfied because authentication failed.</Message><StackTrace> at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)</StackTrace><ExceptionString>System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)</ExceptionString></InnerException></Exception></TraceRecord></DataItem></TraceData></ApplicationData></E2ETraceEvent><E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent"><System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system"><EventID>131075</EventID><Type>3</Type><SubType Name="Error">0</SubType><Level>2</Level><TimeCreated SystemTime="2015-03-02T21:04:14.4059347Z" /><Source Name="System.ServiceModel" /><Correlation ActivityID="{acfc80d6-b119-4f57-aaf2-65f1319b9fca}" /><Execution ProcessName="w3wp" ProcessID="1432" ThreadID="43" /><Channel/><Computer>SERVER1</Computer></System><ApplicationData><TraceData><DataItem><TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error"><TraceIdentifier>http://msdn.microsoft.com/en-NZ/library/System.ServiceModel.Diagnostics.ThrowingException.aspx</TraceIdentifier><Description>Throwing an exception.</Description><AppDomain>/LM/W3SVC/1/ROOT/KerberosMiddleService-6-130698038017642990</AppDomain><Exception><ExceptionType>System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The caller was not authenticated by the service.</Message><StackTrace> at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetNextOutgoingMessage(Message incomingMessage, T negotiationState)
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)</StackTrace><ExceptionString>System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---&gt; System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
Upvotes: 1
Views: 2983
Reputation:
Ok, you need to add another SPN for the end service as well:
HTTP/SERVER2.int.mydomain.com MYDOMAIN\MY-HOST_ACCOUNT)
HTTP/SERVER2 MYDOMAIN\MY-HOST_ACCOUNT)
It is best to specify both FQDN and Netbios name. Ensure that you don't have duplicate SPN's otherwise Kerberos authentication wont work. Add the SPN's as delegation targets to the domain account (this might not be needed since you are using the same domain account for both servers).
Since the MiddleService needs to impersonate/delegate to the EndService, you need to give the domain account the privileges to do so using the local security policies - Local Policies - User Rights Assignment:
Act as part of the operating system
This user right allows a process to impersonate any user without authentication. The process can therefore gain access to the same local resources as that user.
Impersonate a client after authentication
Assigning this privilege to a user allows programs running on behalf of that user to impersonate a client.
Remember to change your IIS application settings to use the application pool credentials so that the domain account is used for authentication.
Upvotes: 1