WCF and SSL Processing Load Balancers

How to Configure WCF Service to Receive Unencrypted Credentials through F5 Networks’ BIG-IP and Similar Devices

RELATED: "Security for WCF" and "Going Mobile with WCF"

This month s column explores how to pass non-Windows credentials through load-balancing devices that process SSL, such as F5 Networks popular BIG-IP devices. For more information on load balancing, see "Load Balancing and Scaling Your WCF Services."

A common problem encountered when deploying WCF services is getting them to play nicely in a load-balanced environment that relies on devices that process SSL requests and forward unencrypted requests to back-end servers. A popular implementation of this scenario involves F5 Networks BIG-IP load-balancing devices. As a follow up to last month s column when I talked about load-balancing issues with WCF, this article will focus solely on configuring your WCF clients and services to work well with SSL processing devices.

 

The Problem Statement

Public-facing WCF services are often accessed over HTTPS, which means that SSL is relied on to protect message transfer. Without an intermediary, WCF clients and services simply agree on a suitable binding with the appropriate credentials and transport security enabled. Figure 1 shows an example where a WCF client sends messages using BasicHttpBinding with a security mode of TransportWithMessageCredential so the transport can protect message transfer while the message contains the actual credentials to be authenticated.


Figure 1: SSL processing without an intermediary.

When an intermediary is introduced to process SSL encryption, such as a load-balancing device, messages are forwarded to the service without SSL encryption. Figure 2 illustrates this scenario, where the client establishes a secure point-to-point channel with the SSL load balancer. Presumably, this SSL load balancer is behind the firewall and messages are then forwarded to the service intact, but without the SSL encryption. This means that the service must somehow configure a binding that can receive the desired credential without protection something that WCF explicitly disallows (in most cases). The configuration shown in Figure 2, for example, would not be supported, because it asks for a UserName to be received in the message, but neither transport protection nor message protection with a service certificate are present.


Figure 2: SSL processing load balancers forwarding unencrypted messages over HTTP.

There are a few possible answers to this problem:

  • Send messages without credentials (of course, in that case you don t need to read this article).
  • The SSL processing load balancer can be configured to establish a new SSL session with the target service. This is not always easy to configure (depending on the load balancer); more importantly, it defeats the purpose of using an SSL load balancer to improve efficiency behind the firewall.
  • WCF services can be configured for basic authentication and receive credentials in the clear over HTTP. This can work; however, it precludes passing credentials in the message, and the use of more interesting credentials (such as issued tokens).
  • WCF services can be configured to fake the presence of transport security so that the runtime will allow receiving message credentials without transport or message protection.

I ll explain how to achieve the last two bulleted items in the remainder of this article.

 

Basic Authentication

One way to pass credentials through an SSL load balancer to a WCF service is to use basic authentication. As shown in Figure 3, clients establish an SSL session with the load balancer and pass username and password credentials encoded in the Authenticate HTTP header. The load balancer then forwards the Authenticate HTTP header to the WCF service over an unsecured HTTP channel.


Figure 3: Basic authentication through an SSL load balancer.

The client uses BasicHttpBinding configured for Transport security mode, and Basic credentials as shown here:



 

   

     

   

 

 

Set the proxy s UserName credentials to pass credentials:

MessageManagerServiceClient proxy =

   new MessageManagerServiceClient("secureBasic");

proxy.ClientCredentials.UserName.UserName = "username";

proxy.ClientCredentials.UserName.Password= "password"; 

The result is that the proxy will initialize the Authenticate HTTP header and send messages to the load balancer over a secure SSL channel. The load balancer is then responsible for forwarding the message and the Authenticate HTTP header to the downstream service, after processing the SSL encryption for a request.

The WCF service is configured for BasicHttpBinding with TransportCredentialOnly security mode and a credential type of Basic. This sends the Authenticate HTTP header in the clear, but the assumption here is that the SSL load balancers and WCF services are behind a firewall and that a decision has been made that this open communication can be trusted behind this firewall. The service configuration looks like this:



 

   

     

   

 

 

The same result cannot be directly achieved with WSHttpBinding, because it doesn t support TransportCredentialOnly.

 

Message Credentials

Other than the scenario just described, WCF specifically disallows sending credentials over an unsecured channel. This poses a challenge passing message credentials through an SSL load balancer because it requires forwarding messages unencrypted over HTTP. For example, the scenario shown in Figure 2 would not work because of the requirement to pass a UserName credential in the message over HTTP and without message security where the message itself is encrypted with the service certificate. Although the figure shows BasicHttpBinding as the chosen binding, the same problem exists for WSHttpBinding and related bindings. Consider the service endpoint and binding configuration shown in Figure 4.



 





 

   

     

   

 

 

Figure 4: Problems with the service endpoint and binding configuration.

The address specified is HTTP; thus, when the ServiceHost is initialized for this service type, the following error will be thrown:

The provided URI scheme 'http' is invalid; expected 'https'.

Parameter name: context.ListenUriBaseAddress

Essentially, the runtime is expecting an HTTPS endpoint to protect the UserName credential. The same problem will occur for any non-Windows credential.

When an HTTP binding is configured for any form of Transport security (Transport, TransportWithMessageCredential), the channel stack uses HttpsTransportBindingElement as the transport channel, as opposed to HttpTransportBindingElement. The security capabilities of the former include transfer protection (encryption and signing) and server authentication. The latter doesn t have these security capabilities. Thus, even if you customize the binding to use HttpTransportBindingElement, ServiceHost initialization will fail because the transport channel cannot provide signing and encryption.

For example, the code in Figure 5 initializes BasicHttpBinding for TransportWithMessageCredential and UserName credentials, initializes a CustomBinding to access the collection of channels, then swaps HttpsTransportBindingElement for HttpTransportBindingElement.

BasicHttpBinding basicBinding = new

   BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);

basicBinding.Security.Message.ClientCredentialType =

 BasicHttpMessageCredentialType.UserName;

CustomBinding customBinding = new CustomBinding(basicBinding);

HttpsTransportBindingElement transport =

   customBinding.Elements.Find();

int index = customBinding.Elements.IndexOf(transport);

customBinding.Elements.Remove();

customBinding.Elements.Insert(index, new HttpTransportBindingElement());

Figure 5: Initialize BasicHttpBinding, initialize a CustomBinding; then swap HttpsTransportBindingElement for HttpTransportBindingElement.

When the ServiceHost is initialized, the following error is presented:

The 'BasicHttpBinding'.'http://tempuri.org/' binding for the

   'IMessageManagerService'.'http://www.thatindigogirl.com/

 samples/2008/01' contract is configured with an

   authentication mode that requires transport level

   integrity and confidentiality. However the transport

   cannot provide integrity and confidentiality.

There is an effective workaround for this to use HTTP transport, but have that transport channel assert to the runtime (lie) that it can provide transfer protection. This is done by supplying a custom ISecurityCapabilities implementation for the HTTP transport channel.

 

Asserting Security Capabilities

As mentioned, the WCF runtime expects a secure channel to be present if non-Windows credentials are used for authentication. This precludes using HTTP protocol unless you use a custom HTTP transport channel that can lie to the runtime about its capabilities. All channels have the opportunity to report their security capabilities by returning an instance of ISecurityCapabilities when the channel s GetProperty method is called.

In this case, we want to supply a custom HttpTransportBindingElement that can return security capabilities indicating transfer protection is present (even though it is not). The goal is to produce a binding configuration equivalent to BasicHttpBinding or WSHttpBinding receiving a non-Windows credential, such as a UserName credential, without SSL encryption. The closest configuration to this is to configure a binding with TransportWithMessageCredential security mode, then replace the HttpsTranportBindingElement with a custom version of HttpTransportBindingElement.

The custom implementation of HttpTransportBindingElement must return an instance of ISecurityCapabilities. In Figure 6, a CustomBinding based on WSHttpBinding is modified to swap HttpsTransportBindingElement with a custom type, AssertEncryptionHttpBindingTransport. This type extends HttpTransportBindingElement and overrides GetProperty to return a custom instance of ISecurityCapabilities, AssertEncryptionSecurityCapabilities.

WSHttpBinding wsHttpBinding = new

   WSHttpBinding(SecurityMode.TransportWithMessageCredential);

wsHttpBinding.Security.Message.ClientCredentialType =

   MessageCredentialType.UserName;

CustomBinding customBindingWS = new CustomBinding(wsHttpBinding);

HttpsTransportBindingElement transportWS = customBindingWS.Elements.Find();

int indexWS = customBindingWS.Elements.IndexOf(transportWS);

customBindingWS.Elements.Remove();

customBindingWS.Elements.Insert(indexWS, new

   AssertEncryptionHttpTransportBindingElement());

ServiceHost host = new

   ServiceHost(typeof(MessageManager.MessageManagerService);

host.AddServiceEndpoint(typeof(MessageManager.IMessageManagerService),

   customBindingWS, "http://localhost:8000/MessageManagerService ");

host.Open();

Figure 6: Exposing an HTTP endpoint that asserts SSL encryption through a custom binding.

The implementation of AssertEncryptionHttpTransportBindingElement and AssertEncryptionSecurityCapabilities are shown in Figure 7.

public class AssertEncryptionHttpTransportBindingElement:

   HttpTransportBindingElement

{

   public override BindingElement Clone()

   {

       return new AssertEncryptionHttpTransportBindingElement();

   }

   public override T GetProperty(BindingContext context)

   {

       if (typeof(T) == typeof(ISecurityCapabilities))

       {

           return (T)(object)new AssertEncryptionSecurityCapabilities();

       }

       return base.GetProperty(context);

   }

}

public class AssertEncryptionSecurityCapabilities: ISecurityCapabilities

{

   #region ISecurityCapabilities Members

   public ProtectionLevel SupportedRequestProtectionLevel

   {

       get { return ProtectionLevel.EncryptAndSign;  }

   }

   public ProtectionLevel SupportedResponseProtectionLevel

   {

       get { return ProtectionLevel.EncryptAndSign;  }

   }

   public bool SupportsClientAuthentication

   {

       get { return false; }

   }

   public bool SupportsClientWindowsIdentity

   {

       get { return false; }

   }

   public bool SupportsServerAuthentication

    {

       get { return true; }

   }

   #endregion

}

Figure 7: Custom implementations of HttpTransportBindingElement and ISecurityCapabilities.

Although the scheme supported by AssertEncryptionHttpTransportBindingElement is HTTP (not HTTPS), AssertEncryptionSecurityCapabilities returns ProtectionLevel.EncryptAndSign when the runtime asks for the supported protection level for requests and responses. In addition, server authentication is asserted, although a service certificate is not present in this scenario. Because this custom ISecurityCapabilities implementation lies about its capabilities to the runtime, the ServiceHost will allow you to configure an HTTP endpoint for the UserName credential.

With this implementation, the UserName credential can be passed securely to the SSL load balancer and forwarded to the service over HTTP in the clear.

Note: The sample code illustrates this with a router service that processes the SSL request and forwards messages to the service over HTTP (see end of article for download details). Remember that this type of configuration should be carefully considered as it bypasses standard security practices for passing credentials.

 

Addressing Concerns

In scenarios where messages are passed through an intermediary (in this case, the SSL load balancer) to the target service, WS-Addressing features may require your attention. BasicHttpBinding does not rely on WS-Addressing; however, WSHttpBinding and other HTTP bindings do.

There are a few possible scenarios to consider:

  • The client sends messages directly to load balancers.
  • The client sends messages to the service and is unaware of load balancers that will intercept the message.
  • The client sends messages directly to load balancers, but specify a logical address that matches the service.

If clients send messages directly to load balancers, the WS-Addressing To header will be set to that of the load balancer, not that of the service. The default behavior is for a proxy to initialize both the logical address (the To header) and the physical address (where the message will be sent) to the same value as the address attribute of the . For example, this client endpoint:

will result in sending messages to the same address as the To header shown in Figure 8.



 

   

https://localhost/BigIP/Service.svc/UserName

   

http://www.thatindigogirl.com/samples/2008/01/ IMessageManagerService/

 SendMessage

 

 

   

     test

   

 

 

Figure 8: Sending messages to the same address as the To header.

By default, for the service to accept this message, the To header should match the physical address of the service: http://localhost/MessageManagerService. If not, an exception similar to the following is thrown:

The message with To https://localhost/BigIP/Service.svc/

 wsHttp cannot be processed at the receiver, due to an

 AddressFilter mismatch at the EndpointDispatcher. Check

   that the sender and receiver s EndpointAddresses agree. 

To allow messages directed to the load balancers, the service must either configure a different listen URI shown here programmatically and declaratively by setting the address to match the expected To header (the load balancer) and setting the listen URI property of the endpoint to match the physical address of the service:

host.AddServiceEndpoint(typeof(MessageManager.

 IMessageManagerService), customBindingWS,

   "https://localhost/BigIP/Service.svc/wsHttp", new

 Uri("http://localhost:8000/MessageManagerService/wsHttp"));

 

or disable address filtering, shown here applied as a service behavior:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.

 PerCall, AddressFilterMode=AddressFilterMode.Any)]

public class MessageManagerService : IMessageManagerService

The former is preferable, because that at least ensures a known To header.

If the client sends messages to the service, and those messages are intercepted by SSL load balancers, the To header will match the service and no adjustments are necessary in the service configuration.

It also is possible for the client to send messages with a separate logical and physical address. The To header is specified in the address property for the client endpoint, and the physical address where messages should be sent is specified in a client via behavior:



 

   

     

   

 





 

 

This is not the ideal situation, because the client should really be unaware of the redirection, and be able to generate proxy and configuration directly from WSDL. Either of the first two options is commonly implemented.

 

Conclusion

Configuring services to receive credentials over unsecure channels is not advisable under most circumstances. It is, however, a very popular scenario to use SSL processing load balancers like BIG-IP to handle SSL decryption and forward messages within a trusted environment. The entire point is to optimize communications across the server farm. The approach I explained in this article gives you a few ideas for handling the situation using basic authentication or passing a non-Windows credential using message security.

The sample code also includes a mechanism for configuration of the custom binding discussed in this article, declaratively. This is done by creating custom binding elements, which I will explain in my next column.

Download the samples for this article at http://www.dasblonde.net/downloads/aspprojan09.zip.

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish