Delegation - WCF Gotcha #2

Welcome to the Jungle

If you've done a little web development or written a few basic web services, you are probably familiar with the concept of impersonation. When impersonation is turned on, .NET will make certain API calls return the identity of the caller, instead of the account that the service is actually running as (for example, DOMAIN\SmithJ instead of MACHINE\Network Service). Impersonation, however, is mostly sugar coating. The web service doesn't actually execute as if it was that user, it just makes your application think that it is. When you connect to a database server on a remote machine or read a file from a file share, you perform those operations as the service account.

The advantage of this approach is that security is easy to configure. You don't have to worry about granting tons of permissions on your servers. You simply trust the service to make the proper security checks on its own and give the service all the permissions it might need... but that ease of configuration comes at a cost. If you have a single account that the web service is executing under, which tends to be happen, that account ends up with permission to do everything. But what if the developers working on the web service forget to put in a security check? These god mode services often open up huge security holes [1].

How do we fix this problem? Enter delegation. Delegation takes impersonation to the next level, allowing a remote machine to execute with the same credentials as the caller. With delegation, you will be able to take advantage of all of the wonderful security features of each of your systems automatically. File system security will no longer be bypassed, database security will no longer be bypassed, and you can hop from service to service to your heart's content, without losing the caller identity or the ability to manage user permissions and policies [2].

The downside of delegation is that it adds a significant amount of complexity to the authentication process. For one, developers generally do not have permission to turn on delegation. Delegation settings are not in the IIS control panel or in the standard control panel. Delegation is turned off by default, and since it is tied closely to Active Directory and Kerberos, it can only be enabled by domain administrators. Additionally, delegation can be difficult to troubleshoot. It is entirely possible for firewalls, proxies, and a plethora of registry options to completely screw it all up. That said, the benefits can really be worth all the hassle, but be prepared for a potentially bumpy ride with the infrastructure team.

Here are the basics you should know.

1)      Delegation must be enabled on every machine that will be passing credentials around. If you are talking from your web box to a SQL server, delegation must be turned on in Active Directory for BOTH machines.

2)      Turning delegation on doesn't actually make it work. After turning it on, you have to set up SPN for the service accounts that your services are running under [3]. Each SPN is specific to the service that is being called. For web services over http or web sites, it should be host/machinename (and an additional for the full domain name host/machine.mydomain.com). For SQL, you will want MSSQLSVC/machinename:1433. This can be done in the active directory admin tools or with the command line tool from Microsoft setspn. The nice thing about setspn is that you can use "setspn -l machinename" to list the SPNs that are configured for a machine, even if you don't have active directory admin rights, which can be extremely useful if you want to troubleshoot things.

3)      After delegation is configured, you need to tell the WCF proxy on the client machine to allow it. When credentials are passed to the server, they have an allowed impersonation level associated. Unless you specifically set the client to allow delegation (allowedImpersationLevel=delegation), credentials will not flow. This can be done in the binding configuration for your service. Consider disabling NTLM in the client configuration as well (allowNtlm=false). In the server configuration, select windows authentication and use a binding that supports delegation, like wsHttpBinding [4].

4)      If things still don't work, check your SPNs again with "setspn -l". You may need to explicitly set the SPN in the client configuration with the <identity> node.

If you follow the steps above, delegation should work with WCF. Fortunately, WCF gives you control over a lot of parameters that you haven't had an easy way to control in the past (like the SPN of the remote machine). But, keep in mind that when you need to call your service from non-WCF applications, you may not have all these options available.

As a final note, delegation does not work out of the box with every binding. For example, the MSMQ binding does not currently support delegation. However, that doesn't mean that you are out of luck if you want to use other bindings. There is a feature of Windows called "protocol transition" that gives your service the ability to act as whoever it needs to act on the fly without the normal Kerberos ticket granting process or the need to call the LogonUser api with a password. Because of the large amount of risk associated with giving a service the ability to run as anyone it chooses, protocol only works when running "constrained delegation." Constrained delegation forces you to choose exactly which services your service can communicate with. That way, you don't have to worry about someone pulling down the CEO's email from your web box. Still, since protocol transition bypasses the normal Kerberos processes, you should use it carefully.[5]


[1] It is worth mentioning that security checks aren't always the only reason you might need the original caller's identity. Sometimes services expose different amounts or portions of data depending on the caller and many times the application behind the service isn't something you have source code for, making delegation the only way you can really call into the application. Additionally, since most major vendors support Windows Authentication, using delegation can give you a unified way to manage user credentials.

[2] Beware of approaches that explicitly pass the identity of your user around. Developers are often tempted to solve the problem by forwarding the caller's identity as a parameter to the web service, but this can open up the same security holes as well, since it bypasses a lot of security provided by the Kerberos protocol. Don't reinvent the wheel if you can avoid it. If you spend the time to build, debug, and secure it you will probably end up with something that looks a lot like Kerberos anyway.

[3] As an FYI, service accounts will make things easier to setup due to some special permissions they have. By service account, I mean the local machine service accounts, think Network Service. You can use domain accounts, but that requires additional setup and the use of a UPN identity inside your configuration files instead of an SPN.

[4] I have seen delegation working with basicHttpBinding when hosted in IIS with asp.net compatibility and SSL enabled... so it is possible to do without wsHttpBinding even if it's not explicitly supported.

[5] As a result, protocol transition with constrained delegation is similar to using a service account with your own user store, except that credentials can be managed in active directory and you get to use the actual identity of the caller when you are granting permission to resources.

Linked By