Share via


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!

Originally posted by Morgan Skinner on 04 December 2009 here https://blogs.msdn.com/morgan/archive/2010/04/15/setting-up-wcf-with-a-load-balancer-using-ssl-in-the-middle.aspx

Comments

  • Anonymous
    August 07, 2011
    Awesome . I wasted one week. After your suggestion it worked with out any issues. Thanks a lot.