WCF: Delegation at Message Level security
Basics:
Review this article to get familiar with basic settings needed for WCF delegation.
https://blogs.msdn.com/b/saurabs/archive/2012/08/28/wcf-learning-impersonation-and-delegation.aspx
Basic Message Level security can be easy to set up delegation, as indicated from below diagram:
What about delegation between boxes with Load Balancer in place ?
Key points:
1. For Load balancer scenario, we have to be careful because WCF message security deals with "SecurityContextToken" creation and usage.
Steps:
Request 1: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue
Response: https://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
Request 2: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT
Response: https://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT
Request 3: https://tempuri.org/IService1/GetData
Response: https://tempuri.org/IService1/GetDataResponse
Problem:
Because of creation of SCT token, we need to direct the actual call to the same server who issued the SCT to client.
In case we send the SCT token issued from Server 1 to server 2 because of round robin distribution of request via LB, we end up in failure.
Approaches attempted and reason for failure:
1. To avoid the SCT creation we can set these two property inside message security tag. This will help us get what we call - ONE SHOT PROXY
a) EstablishSecurityContext
b) NegotialServiceCredential
2. Message Security with no Negotiation needs to set the NegotiateServiceCredential = false.
In the case of Windows credentials, setting this property to false causes an authentication based on KerberosToken.
This requires that the client and service be part of a Kerberos domain. This mode is interoperable with SOAP stacks that implement the Kerberos token profile from OASIS. Setting this property to true causes a SOAP negotiation that tunnels SPNego exchange over SOAP messages. This mode is not interoperable.
Since we set the value to true in the config file, we end up validating the server identity and creating a unique message id .
When NegotiateServiceCredential set to true, EstablishSecurityContext as false.
============
Step 1: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue
Step 2: https://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
Step 3: https://tempuri.org/IService1/GetData
When NegotiateServiceCredential set to true along with EstablishSecurityContext as true
======
Step 1: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue
Step 2: https://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
Step 3: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT
Step 4: https://tempuri.org/IService1/GetData
When EstablishSecurityContext is false, we still end up in getting a unique SCT token as a part of SSPI Negotiation (NegotiateServiceCredential set to true).
<t:RequestedSecurityToken>
<c:SecurityContextToken u:Id="uuid-570f65bc-7197-4d56-b388-61a445d317b4-1" xmlns:c="https://schemas.xmlsoap.org/ws/2005/02/sc">
<c:Identifier>urn:uuid:20ac8eb5-73f3-46d9-ba43-8c7ea9e8c56b</c:Identifier>
</c:SecurityContextToken>
</t:RequestedSecurityToken>
Client end up in using the same Context token it received as a part of SSPI Negotiation.
So the true solution is to set both Establish Security context and Negotiate Service Credentials to FALSE.
3. But limitation with this approach is, it does not support client to set TokenImpersonationLevel as "DELEGATION" without which client really can't get a Delegatable token from DC.
Above approach is good, if we just need to authenticate or impersonate the cred to next hop.. and no requirement to do delegation.
Failure Code:
public KerberosSecurityTokenProvider(string servicePrincipalName, TokenImpersonationLevel tokenImpersonationLevel, NetworkCredential networkCredential)
{
if (servicePrincipalName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("servicePrincipalName");
if (tokenImpersonationLevel != TokenImpersonationLevel.Identification && tokenImpersonationLevel != TokenImpersonationLevel.Impersonation) <-------------------------------------
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("tokenImpersonationLevel",
SR.GetString(SR.ImpersonationLevelNotSupported, tokenImpersonationLevel)));
}
this.servicePrincipalName = servicePrincipalName;
this.tokenImpersonationLevel = tokenImpersonationLevel;
this.networkCredential = networkCredential;
}
Stack Trace:
Server stack trace:
at System.IdentityModel.Selectors.KerberosSecurityTokenProvider..ctor(String servicePrincipalName, TokenImpersonationLevel tokenImpersonationLevel, NetworkCredential networkCredential)
at System.ServiceModel.ClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement, Boolean disableInfoCard)
at System.ServiceModel.ClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
at System.ServiceModel.Security.SecurityProtocol.AddSupportingTokenProviders(SupportingTokenParameters supportingTokenParameters, Boolean isOptional, IList`1 providerSpecList)
at System.ServiceModel.Security.SecurityProtocol.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
3. Another approach is to user "KerberosOverTransport" security mode via custom binding. But again it does not allow client to set the Token Impersonation level as Delegation.
4. If we try to move to - "TransportWithMessageCredential" security mode, SSL handshake will happen and further we will still end up in creating the SCT token.
Request 1: https://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue
Response: https://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
Request 2: https://tempuri.org/IService1/GetData
c:SecurityContextToken [ u:Id=uuid-fa4f216a-18cf-4204-b4a0-11a62fe765a4-1 xmlns:c=https://schemas.xmlsoap.org/ws/2005/02/sc ]
Response: https://tempuri.org/IService1/GetDataResponse
Solution:
1. Enable Sticky session on LB to make sure specific client are routed to same machine all the time and we can live with default Message security or even TransportWithMessageCred..
2. Switch to pure transport security and windows authentication.