Setting up WCF with a load balancer using SSL in the middle
I was onsite today with a customer who was having problems getting WCF to work, and after much wailing and gnashing we finally arrived at a solution so I thought I would blog it. We were getting an EndpointNotFoundException with the error message “There was no channel that could accept the message with action …”. Then we’d get a warning saying “The incoming message is not part of an existing security session”. We went down a load of blind alleys on this before we finally got a solution (thanks go to Zulfiqar for helping a load).
The customer had a couple of servers hosted behind a load balancer. The load balancer was doing the heavy lifting of dealing with the incoming SSL request, and passing this on to the WCF service over HTTP. It took a while for us to get to the right configuration so here it is.
On the server side I defined the service etc. as follows…
<wsHttpBinding>
<binding name="myBinding">
<security mode="None"/>
</binding>
</wsHttpBinding>
<behavior name="myBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
<applyAddressFilterModeBehavior/> <!-- Discussed later on -->
</behavior>
<service behaviorConfiguration="myBehavior"
name="TestService.MyService">
<endpoint binding="wsHttpBinding"
bindingConfiguration="myBinding"
contract="TestService.IMyService"/>
</service>
And the client was defined as follows…
<wsHttpBinding>
<binding name="myBinding" >
<security mode="Transport">
<transport clientCredentialType="None" />
<message establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
<endpoint address=https://MyServer/MyService.svc
binding="wsHttpBinding"
bindingConfiguration="myBinding"
contract="TestService.IMyService"
name="WSHttpBinding_IMyService" />
The one other thing we needed to do was to add the [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] attribute to the service, as this effectively allows us to call to https://myserver/service.svc and have this processed by https://myserver/service.svc. The one hitch in our case was that there were a number of services that would need this same code change, and my customer didn’t want to have to change all of them.
So, I cranked out some code to setup the address filter mode using a custom behavior. The code for that one is below…
public class ApplyAddressFilterModeBehavior : IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
{
ChannelDispatcher dispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
if (null != dispatcher)
{
foreach (EndpointDispatcher endpoint in dispatcher.Endpoints)
{
endpoint.AddressFilter = new MatchAllMessageFilter();
}
}
}
}
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}
}
The behavior is applied to the service by using another class which I defined within the same assembly…
public class ApplyAddressFilterModeBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(ApplyAddressFilterModeBehavior); }
}
protected override object CreateBehavior()
{
return new ApplyAddressFilterModeBehavior();
}
}
The behavior is added to the service in the config file by adding in a couple of elements…
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="applyAddressFilterModeBehavior"
type="Test.Behaviors.ApplyAddressFilterModeBehaviorElement, Test.Behaviors,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
With that defined all I then need to do is update the service behavior to add a reference to the above extension…
<behaviors>
<serviceBehaviors>
<behavior name="myBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
<applyAddressFilterModeBehavior/>
</behavior>
</serviceBehaviors>
</behaviors>
With all that little lot in place I can now call my service and I don’t get the errors that I had received before. Phew!
Comments
- Anonymous
May 16, 2013
Thanks a lot you gave complete and simple solution. Thank you once again -Viswanath ***