WCF service is unable to call service on another machine - wcf

Here's the situation setup: multiple WCF services are hosted on IIS on several virtual machines inside one sub-network. Let's imagine we have:
virtual machine A with IP address 10.0.8.100
virtual machine B with IP address 10.0.8.101
virtual machine C with IP address 10.0.8.102
A, B and C are within the same domain.
Machine A have WCF service responsible for authentication, and both machines B and C have services calling this auth service.
Used binding
<binding name="WsHttpBinding_IAuthenticationService"
receiveTimeout="00:10:00"
sendTimeout="00:10:00"
maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
<security mode="Message">
<message clientCredentialType="None" />
</security>
Everything works just fine until one day, when single call of auth service on machine A from both machines B and C fails with exception:
An unsecured or incorrectly secured fault was received from the other party.
See the inner FaultException for the fault code and detail.
Inner exception says:
An error occurred when verifying security for the message.
Meanwhile, this service can be accessed by its address in browser and I can successfully ping any machine from any other machine.
I know that this exception most frequently is caused by time skew more than WCF default of 5 mins. I have faced this issue before, but this is a different case, because time on all virtual machines is synced and time zone is chosen correctly.
Interesting tip: I've tried to relocate mentioned auth service from A to B, than call it from C and I did not get this exception.
I suspect that something was changed in the network layout/settings etc. and led to this situation, but I don't have any solid proof, because network administrators are out of reach at the moment.
Again - everything was working fine and no updates were delivered into this environment during the last month.
Where should I look for any clues about reasons of this situation?
Thanks.

It turned out that time skew was actually the reason of this exception. All virtual machines are configured to display time in AM/PM format, and the time skew was exactly 12 hour. This feature was not taken into account because we usually use 24-hour format.
Case closed.

Related

How to expose WCF service with Basic and Windows authentication options, so Negotiation works

Some clients need to be able to connect to our WCF SOAP services using Basic authentication, while others need to use Windows authentication. We normally host our services in IIS, although we do provide a less-developed Windows Service hosting option.
It's my understanding that it is not possible to configure one endpoint to support both Basic and Windows authentication. So we have two endpoints per service.
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicBinding" contract="SomeContract" bindingNamespace="http://www.somewhere.com/Something" />
<endpoint address="win" binding="basicHttpBinding" bindingConfiguration="WindowsBinding" contract="SomeContract" bindingNamespace="http://www.somewhere.com/Something" />
...
<bindings>
<basicHttpBinding>
<binding name="BasicBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
<binding name="WindowsBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
These are in the same Web Application in IIS. That Web Application has both Basic and Windows authentication enabled (else one of the above bindings wouldn't work).
When a client uses the Windows-authenticated endpoint (with "win" on the end of URL), this typically works fine. When the initial request doesn't contain any authentication information, a negotiation takes place between the client and IIS, they settle on Windows authentication and all goes well.
When a client uses the Basic-authenticated endpoint (without "win" on the end of URL), this works if they include the Authorization HTTP header with the correct encoded credentials in it. However, if they do not include any authentication information in the initial request, the negotiation ends up choosing Windows authentication. This gets the request past IIS security, but WCF then refuses the request, because it's going to a Basic-authenticated endpoint.
I am rather hazy on exactly what's happening in the negotiation. But it seems to me that IIS offers all authentication methods enabled for the Web Application (i.e. Basic and Windows), even though the particular WCF endpoint URL for the request only supports Basic.
I would like to know if there is anything we can do in IIS to make the negotiation come up with the right answer: that is, if the request is to a Basic-authenticated endpoint, tell the client to use Basic. Of course, we still want the negotiation to end up choosing Windows, when the request went to the Windows-authenticated endpoint.
If there isn't, then do you think we would be better off concentrating on our Windows Service-hosted version of the services? Or would that have similar problems somehow?
Final note: we do use Basic with HTTP for some internal uses, but we do know that this is an insecure combination. So we typically turn on HTTPS for production use; I've left that out here, for simplicity.
Yes, clientCredentialType="InheritedFromHost" solves the problem for me. This, new in .Net 4.5, means that one can now use the same endpoint URL for more than one authentication type. IIS settings control what authentication is allowed, meaning no longer possible to get IIS and WCF settings in conflict.

Kerberos authentication on a self-hosted WCF Data Service

We have a WCF Data Service which is self-hosted under a Windows service (not using IIS) which we are currently working to secure using SSL and Windows Authentication.
After some time playing around with netsh and server certificates, we now have the service secured with SSL and we have also enabled Windows Authentication on the webHttpBinding in our app.config - however we are now seeing some strange behaviour when attempting to authenticate certain users - some can log in fine, others have their credentials rejected and are prompted with HTTP 400 errors.
After some testing and digging around it would appear that we might be running into this problem, where the authentication header used by Kerberos may be greater than the maximum permitted header length (which I believe is 16k) for certain users - and although there is a documented workaround for IIS, there does not appear to be an equivalent setting we can use for a self-hosted service, or in our app.config - unless I'm missing something? We tried setting the maxReceivedMessageSize and maxBufferSize fields to their maximum values to see if that would make any difference, but apparently not.
Binding config:
<webHttpBinding>
<binding name="DataServicesBinding"
maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</webHttpBinding>
We've managed to work around this issue temporarily by setting the clientCredentialType in our binding to use Ntlm instead, but we'd like to get Kerberos working if possible for obvious reasons.
So, as it turns out, this was caused by our service not being configured with a SPN (Service Principal Name). This can be done using the setspn tool with Windows Server. (See this MSDN article for more information.)
Once the SPN was applied, Kerberos authentication started to work as expected.
Use wireshark to see what the client sends. Make sure that this input is correct and then come back.

WCF over MSMQ, 403 Service unavailable

I setup a WCF service to work over HTTP and MSMQ. It kind of works. The HTTP protocol works 100%. The problem is with net.msmq. When I check the queue, the messages have gone down by 1 which I assume means it's being processed. But at the same time, the service is no longer available. I receive a 403 service unavailable error from IIS. When I purge the queue and reset the site, the service is available again.
Message queue has full permissions for everyone and the service doesn't do anything (seriously, it's just a stub method) so it isn't a code problem.
The contract is marked as one way and the action is *.
Messages are going into the queue (sending 10 at a time) and at least 1 message is getting pulled (count goes down to 9).
The net.msmq listener service is running.
How can I fix this problem?
I had the same problem, because I forgot to specify the bindingConfiguration. I had the binding setup like
<bindings>
<netMsmqBinding>
<binding name="Msmq" exactlyOnce="true" >
<security mode="None" />
</binding>
</netMsmqBinding>
</bindings>
Once I realized the bindingConfiguration was missing from my endpoint, I added it in, and it started working correctly.
<endpoint address ="net.msmq://localhost/private/MyQueue.svc"
binding="netMsmqBinding"
contract="IService" bindingConfiguration="Msmq">

IIS Rest over HTTPS only

I've asked a similar question here:
How to enforce one method in WCF Rest to be called via https, while others can be called over http
And it doesn't look like it is possible on the code side. Is it possible to set up an entire service to be callable over HTTPS only? I've configured a service using the following binding:
<webHttpBinding>
<binding name="webBinding"
maxBufferSize="152428800" maxReceivedMessageSize="152428800"
receiveTimeout="00:10:00">
<readerQuotas maxStringContentLength="152428800"
maxArrayLength="152428800"
maxBytesPerRead="4096"/>
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
But when I try to call a simple service over http, the service returns the result happily, rather than returning some sort of exception. Do I need to configure IIS to only service https requests? Has anyone tried this?
Thanks!
Did you configure IIS to require SSL on your application's folder? (you can set it to allow ssl or make it mandatory)
You can always add an explicit endpoint to your service entry with a fully-qualified https address. Can't remember if IIS hosting always auto-adds the base addresses when you have an explicit address, but even if it does, you can make a simple extension of ServiceHostFactory to "eat" the default base addresses IIS supplies (reference your custom servicehostfactory in the Factory attribute of your .svc file). Then it'll only answer on the exact addresses you supplied in the config.
It's possible via configuration. This Blog Article is not your exact scenario (it's file transfer over https), but it shows sample config and code for configuring and consuming a https web service that should be useful.

WCF - Cannot Find the x.509 Certificate Using the Following Search Criteria

Ok, I have seen several questions related to this issue, and I have tried a lot of the ideas presented in them with no success. Here's my situation:
I'm hitting a web service over my company's intranet. I have used svcutil.exe to generate the client class for WCF. I was able to run the web service call with no problem when the service was in development and did not require authentication credentials, so I know the code works. At the time, the code was running over SSL. I imported the required certificate into the Trusted Root Certification Authorities store, and everything was fine.
We just moved to a stage environment, and the service was upgraded to require credentials to connect. I switched my connection to the new endpoint, and added code to authenticate. This is my first time working with wcf, so please bear with me on any obvious mistakes. My problem is that I cannot locate the certificate via code to pass to the service for authentication. I am basing this off of some online code examples I found.
Here is an example of my config generated by svcutil:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding
name="xxxSOAPBinding"
.... (irrelevant config settings)....
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://xxxServices_1_0_0"
binding="basicHttpBinding" bindingConfiguration="xxxSOAPBinding"
contract="xxxService" name="xxxService" />
</client>
</system.serviceModel>
And here is the code I am using to try to connect. The exception is thrown as soon as I attempt to locate the certificate:
using (var svc = new xxxServiceClient())
{
svc.ClientCredentials.UserName.UserName = "XXX";
svc.ClientCredentials.UserName.Password = "XXX";
svc.ClientCredentials.ClientCertificate
.SetCertificate(StoreLocation.LocalMachine, StoreName.Root,
X509FindType.FindBySubjectName, "xxx");
...
}
I have tried several different X509FindTypes, and matched them to the values on the cert with no success. Is there something wrong with my code? Is there another way I can query the cert store to validate the values I am passing?
The dev machine where I am running Visual Studio has had the cert imported.
Two silly questions:
are you sure your certificiate is installed at all?
is this a certificiate specifically for this staging machine?
Also, it seems a bit odd you're first of all setting username/password, and then also setting the credential. Can you comment out the username/password part? Does that make any difference?
Marc
Are you sure the the certificate has been imported to the local machine store, it could be in the CurrentUser store.
This may sound stupid, but are you certain the new cert for the staging service has been installed into your cert store? That's most likely your problem.
Also, since you didn't mention what exception is thrown, it's possible the problem is that you've set username/password credentials before setting clientcertificate credentials, when your binding does not indicate the use of username/password. Could be a problem there; they're mutually exclusive, IIRC.