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;
}