1
votes

We are developing many WCF services that are hosted in IIS. (IIS 6.0 running on Windows Server 2003 SP2). These services are set up for REST. For an environment (DEV, CERT, PROD), we typically have many services per IIS server. Each service has its own login account that's assigned via the Application Pool.

This works fine, but if we enable Windows Authentication on the virtual directory (to allow user context passing, not for impersonation or delegation), we get security errors in specific cases. If we connect to the service in C# code using ServiceEndpoint it works, however when we connect to the services via browser or non Wcf code (e.g. HttpWebRequest, java, et al) we get the security errors.

If I change the authentication in IIS from Negotiate, NTLM to just NTLM, then it works. Really what's happening is we're disabling Kerberos, and since these services talk to other servers in the network, we start to get other issues where services can't connect to remote servers (typically SQL Servers).

Now here's the weird part, if we give the machine a DNS alias (CNAME record) and use the URL with the alias it works. e.g.

http://dnsalias/service/myservice.svc/foo WORKS!
but
http://machinename/service/myservice.svc/foo  FAILS :(

We cannot give all these machines DNS aliases (that has been our solution to this point) because we're starting to use VMs extensively and spinning them up and down. So we only have machine names, we don't want to start scripting DNS aliases on machine spin up.

Now I know the SPN issue, however, since we have multiple services hosted on the same website (Default website typically), we can only create 1 service principal name per server/account. Since we host multiple services per server each mapped to its own account, this isn't a solution.

setspn -a HTTP//WCFServer.domain.com customDomainAccount

Another wrinkle, we don't define endpoints in config files, only in code, here is the code to get a REST binding.

protected internal static Binding GetWebHttpBinding()
        {
            // Create a new binding with Authentication enabled
            var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);

            // Set defaults
            SetTimeouts(binding);
            SetReaderQuotas(binding.ReaderQuotas);
            binding.MaxReceivedMessageSize = MaxReceivedMessageSize;
            binding.MaxBufferSize = 65536;

            // Set the Credential type to Windows to allow single sign-on
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

            // Need Streamed Response since Transport security does not allow streamed requests
            binding.TransferMode = TransferMode.StreamedResponse;

            return binding;
        }
3

3 Answers

1
votes

Ok, much much later, I'm back with the answer. For the environment described, the only answer is to use NTLM authentication. This has been confirmed with a call to Microsoft support.

0
votes

I had the understanding that the machine name is a DNS entry already. The raw name/or lookup of a machine is actually its IP address. So when you state 'machinename' in your url that does not work, isn't that already a DNS entry? If you ping 'machinename' what is the IP that shows responding to the ping; that should be the IP of the machine.

To me it sounds like you are creating a DNS lookup for a DNS lookup. Can you clarify on this a bit?

I can confirm that magic happens for me too, once I get a DNS lookup on the domain for an IP of a WCF service or site I am working with, so I understand where you are coming from.

I don;t know what your client code looks like, but take a look at the following link to match up. You have to build up a Network Credential before calling the RESTful service. If you do this, does it solve the other issue with having to diasble kerberos?

0
votes

I was also getting similar "security errors" with Integrated Windows Authentication and WebHttpBinding trying to utilize WebHttpSecurityMode.TransportCredentialOnly .. but was requiring SSL so I was able to stick to WebHttpSecurityMode.Transport while also configuring HttpClientCredentialType.Windows.