WCF: Delegation at Message Level security

 

Basics:

Review this article to get familiar with basic settings needed for WCF delegation.

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: schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue

Response: schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue

Request 2: schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT

Response: schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT

Request 3: tempuri.org/IService1/GetData

Response: 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: schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue

Step 2: schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue

Step 3: tempuri.org/IService1/GetData

 

 

When NegotiateServiceCredential set to true along with EstablishSecurityContext as true

======

Step 1: schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue

Step 2: schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue

Step 3: schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT

Step 4: 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="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: schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue

Response: schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue

Request 2: tempuri.org/IService1/GetData

c:SecurityContextToken [ u:Id=uuid-fa4f216a-18cf-4204-b4a0-11a62fe765a4-1 xmlns:c=schemas.xmlsoap.org/ws/2005/02/sc ]

Response: tempuri.org/IService1/GetDataResponse

  

Solution:

1. There is only one solution: 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.