views:

1024

answers:

2

We have a WCF (Windows Communication Foundation) client and service application. We're using Windows Authentication with Kerberos.

The issue is that the service may be run under one of many accounts (maybe Network Service, maybe a specific user account -- depends on the IT group). This account is not not likely to change daily, but possibly on occassion (every few months maybe). Additionally, we deliver this client/service package to several groups, and each group may have its own account that they use to run the service on (this is to just let you know that we can't do a custom solution for a single team).

Now the reason the above paragraph is an issue is apparently if the service is not running in the SYSTEM or NETWORK SERVICE account, i.e., a user account, then the client must specify the name of the user account in the identity of its endpoint.

For more on this restriction see: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/feb6bc31-9a4b-4f8d-a887-ef6d2c7abe41 and http://www.vistax64.com/indigo/146204-using-localhost-v-s-environment-machinename.html

Now this seemingly makes it tough to deal with the situation where the IT department changes the account that the service runs on. What is the pattern for handling this, if there is one? How have other people handled this? One solution I've thought of is that the admin sends out an email when the user account of the service has changed, which has a weblink to an application that updates the client or a config file, so the client refers to the new user account. But that seems hackish.

Admittedly, this is a lot like the URI of the endpoint moving. Except, I think there's a lot more expectation on behalf of people that changing the URI is something the client should have to know about, but changing the account the service is running on is something that should be relatively transparent to the client.

BTW, this is required to be hosted on IIS 7.0, if that matters.

+3  A: 

I think you can set NegotiateServiceCredential property to True so your binding uses SPNego instead of hard-cold Kerberos. When is set to true, the client does not need to specify the SPN and it can connect to a server running a non-machine account.

Note that since the client no longer requests a specific SPN, it can no longer detect if is connected to a hijacked impersonator of the service, but this is usually a minor concern unless you are really paranoid about security.

Also, as a side rant: the fact that WCF requests as SPN the account name is basically a brain fart. It client should use the DsMakeSpn API to compose the SPN from the service name, host and port. The server should register that SPN for itslef at start up or let an administrator do it using setspn.exe. This is the way they do it by all traditional (well behaved) services in the Kerberos/ActiveDirecotry/Windows environment.

Update

On second though I don't see anything specifying that the client must use the account name as SPN. Looks more like a documentation oversight, instead of documenting the proper way they just recommended what is basically a bad practice. Or maybe just the forum advice is bad, since I did not dig to see what does the MSDN binding spec actually says about the SPN to use...

I don't have a WCF environment handy to test with, but perhaps you can configure the clients to requests a proper SPN like YourService/server:port and you also register the same SPN on the server side. Either manually as an exercise left to the admins, or automatically from your service when it starts up, and unregister it at shut down. The proper way to do it is to let the admins do it, but in reality that is such a pain that most services register the SPN themselves and you can probably follow this practice too. To register an SPN, your service calls DsWriteAccountSpn. The write has to propagate to the AD and be replicated between AD servers, and this at least one reason why having the service auto-register/auto-unregister the SPN is a questionable practice.

If you want to learn more about the wonderfool world of SPNs and how they can wreck your day, you can read up on How Service Publication and Service Principal Names Work.

Update

I'm pretty sure you can use any SPN you like. Most exmaples out there use the account name as 'UPN' (User Principal Name) instead of SPN, but that is just for the convenience of samples as using a true SPN would run into administrative issues setting up the SPN under an user account (again, why admins should do it...). From Overriding the Identity of a Service for Authentication, relevant emphasized:

By default, when a service is configured to use a Windows credential, an element that contains a <userPrincipalName> or <servicePrincipalName> element is generated in the WSDL. If the service is running under the LocalSystem, LocalService, or NetworkService account, a service principal name (SPN) is generated by default in the form of host/<hostname> because those accounts have access to the computer's SPN data. If the service is running under a different account, Windows Communication Foundation (WCF) generates a UPN in the form of <username>@<domainName>. This occurs because Kerberos authentication requires that a UPN or SPN be supplied to the client to authenticate the service.

You can also use the Setspn.exe tool to register an additional SPN with a service's account in a domain. You can then use the SPN as the identity of the service. To download the tool, see Windows 2000 Resource Kit Tool : Setspn.exe. For more information about the tool, see Setspn Overview.

Remus Rusanu
Thanks Remus. This looks good. I'm going to look into your links and methods and see if they do the job. AGain thanks!
Kang Su
I'm pretty sure this should work, see my latest update.
Remus Rusanu
It does seem to work! Although I'm seeing some weird behavior. What I did was went to the AD server and ran setspn and associated an arbitrary spn with the user account. Then I exposed that SPN from the server and consumed it from the client. That's all well and good.Well then I tried changing the exposed spn on the service, and tried consuming a random spn from the client. I thought that should fail. Well now, as long as I specify any spn it works. This is 10x better than the state I was in before, but it would be interesting to know what the cause of this behavior is. Thanks!
Kang Su
A: 

Set NegotiateServiceCredential and EstablishSecurityContext properties to False.

Alex G