load balancing WCF with wsHttpBinding and Message

2020-07-09 02:26发布

问题:

We have got a normal WCF service which has a binding that looks like this:

 <wsHttpBinding>
 <binding name="ServiceBinding" receiveTimeout="00:10:00" sendTimeout="00:10:00"
                bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                maxReceivedMessageSize="20971520"
                messageEncoding="Mtom" textEncoding="utf-8" useDefaultWebProxy="true"
                allowCookies="false">                   
                <security mode="Message">
                      <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        establishSecurityContext="false" />
                </security>
            </binding>
</wsHttpBinding>

this service sits in 2 servers behind a load balancer. As suggested here

http://msdn.microsoft.com/en-us/library/vstudio/hh273122(v=vs.100).aspx

I have set establishSecurityContext to false. When i run call the service i get intermittent issues related to invalid security context token. Even though i say not to establishSeurityContext it seems that WCF is doing all the normal handshake stuff.

At this point of time using Cert, BasicHttBinding or no security is not an option because of the requirement.

I have even got the infrastructure team to enable sticky sessions in the load balancer but nothing seems to work the way we expect it to.

Me and my team has done almost everything that has been said in the internet but nothing seems to work when there is a load balancer and this binding is working perfect when there is not load balancer.

Has any one got luck with this binding?

We are chasing Microsoft to send us the WCF expert but apparently folks are hard to get hold of.

How can i get this thing to work nicely with Load Balancer?

回答1:

You set negotiateServiceCredential="true". That means security context is created during initial exchange, but this context won't be used in subsequent calls (because of establishSecurityContext="false"). The negotiation process allows client to get server credentials safely. Then client uses these credentials to secure the message. That's why all "handshake stuff" happens.

This scenario is described in this article: "Message Security with a Windows Client".

The security negotiation is needed when you're using load balancer (because actual server's credentials depend on a machine that will serve request) unless you're using the same credentials for all service instances behind balancer. In the latter case you can set negotiateServiceCredential="false" and specify server credentials in config or code. For example, you can use clientCredentialType="Certificate" and use the same server certificate for all machines. Or you can clientCredentialType="Windows" and configure arbitrary SPN to a domain user, which is used to run all services behind balancer (I didn't try this scenario, see details below).

In your case negotiateServiceCredential="true". So possible issues are:

1) Sticky session may not work properly. You can test it by implementing simple WCF service with BasicHttBinding that returns

String.Format("{0}{1}", prefix, DateTime.Now);

Configure prefix="" in app settings on one server behind balancer and prefix="!!!!!!!!!!!!" on another. Then call this service in a loop many times and log results. You'll see if there's an issue with the sticky session.

2) If sticky session works correctly, ensure that your configuration works exactly for all servers when no balancing is used.

When you can't use sticky session, you should set negotiateServiceCredential="false". The negotiation is not used, so client should have server credentials are being configured explicitly in code or configuration. To use the Windows credential type without negotiation, the service's user account must have access to service principal name (SPN) that is registered with the Active Directory domain. See details and example in "Message Security with a Windows Client without Credential Negotiation"

To use arbitrary SPN you should configure your services to run under the same Windows domain account. To set arbitrary SPN to the account you can use setspn utility on a domain controller:

setspn a AcmeService/GlobalBank WS_Account

as described in Kerberos Technical Supplement for Windows

See also article about Message Security for description of various scenarios and corresponding settings.



回答2:

As mentioned, one option is to turn off both negotiateServiceCredential and establishSecurityContext. This will result in a performance penalty, which may or may not be meaningful for your scenario. If this method does not work too it is possible that due to kerberos/ntlm there is no "one shot" authentication. I suggest to turn on Fiddler (or wcf logs) for just one user operation (one proxy call) and see if it also makes just one XML call to the server or does it require multiple calls for authentication.

Another option is to ensure all servers use the same account which was also mentioned by Ivan.

There is a third option where you make the servers stateless by using a stateless security context token. Just set:

requireSecurityContextCancellation=false

In this way all clients will send the cookie to the server each time so it does not matter which server they got. I know that with certificates security this means all servers must use the same certificates, not sure what is the implicaiton in windows security. Here is a full example:

http://msdn.microsoft.com/en-us/library/ms731814(v=vs.90).aspx

Creating a custom SecurityStateEncoder might also be relevant in some scenarios:

http://msdn.microsoft.com/en-us/library/system.servicemodel.security.secureconversationservicecredential.securitystateencoder.aspx