3 Simple Rules to Kerberos Authentication/Delegation SPNs

aka: "Kerbie Goes Bananas "

Before starting, go get the updated SetSPN if you're using Windows 2000 or 2003.

Kerb authentication via HTTP can be a complex and tricky sport... or so the experts would have you believe. (OK, I'm just trying to stir interest here. I promise I'll stop). If you're just using the default settings, it all tends to just work, but add host headers or change App Pool identity, and there are more moving parts that need attention.

This lot deals exclusively with HTTP SPNs.

Mild Disclaimer: The information below reflects my current understanding of how it works and/or is meant to work; YMMV. If you have a problem, yell.

It was pointed out to me that the rules below weren't really rules... So let's add a quick "You Must Learn" section here:

Those Rules I Mentioned:

  1. The SPN must match the site name
  2. The SPN must be registered against the App Pool identity (IIS6)
  3. The SPN must be registered against only one account

Now, on with the explanations...

1. What Should The SPN Look Like?

Easy to get this part wrong, so here's the short form:

service/hostname:port

service = HTTP (for web servers)
hostname = the friendly name of the website, as configured in DNS and the site properties
port = if not the default port for the service, specify it

You need to register every host name that clients will use to connect to the server - the hostname is the name users type into the browser.

Examples:
For an intranet site called intranetweb in the domain internal.domain.com:

HTTP/intranetweb
HTTP/intranetweb.internal.domain.com

For an MSCRM v3 site (it uses port 5555, I'm told) in the same network:

HTTP/mscrm:5555
HTTP/mscrm.internal.domain.com:5555

Update 17/6/2006: IE6 doesn't currently include the port number when requesting an SPN, so registering HTTP/mscrm should do the trick for now. I'll update this if that changes*.

Update 16/02/2007 : It changed shortly afterwards - see the following article: 908209 . I am guessing IE 7 does this right, so if you're on the latest version or the latest hotfix, you can use the port number. If in doubt, grab a network trace.

Note: Inconsistent behaviour has been seen around port numbers with various clients (eg, .Net, IE, etc); if the port doesn't seem to be working out for you, register an "un-ported" version as well, eg HTTP/mscrm . If I run across more info on that, I'll update.

You'll often register at two SPNs per site (one short name, one long name); sometimes more or less, but it's a good rule of thumb.

Note: If using DNS CNAMEs to associate the desired name with the real host name (A record), IE prior to update 911149 will request the SPN based on the target of the CNAME, not the alias itself.

The item that you actually need to set the SPN against is explained in Rule 2:

2. Register the SPN against the App Pool Identity!

Short version: Really straightforward! While decrypting the Kerberos ticket, the IIS worker process (assuming IIS6) will be running as the relevant Application Pool's process identity, so that identity is what the SPN needs to be registered against .

Out of the box, user-created Application Pools run as NetworkService , which means that they're considered to be acting as the computer account .

So, if your app is running on a single box and the App Pool identity is NetworkService , you need to register the SPN against the computer account in AD.

On the other hand...

If you're load balancing one app across a number of boxes and want to do Kerb to it, the App Pool will need to run as a domain user account (with the app SPN registered against it), because of Rule #3. You can't have the same SPN registered against loads of accounts .

If your App Pool runs as a user account , you'll need to register the SPN for the application against that user account .

It works like this:

SETSPN -A service/hostname:port accountname

Or if you're using Windows 2008 or later, SETSPN -S service/hostname:port accountname. SetSPN -S is just better.

So:

SETSPN -A HTTP/mscrm:5555 mscrmsvc              (last part is the CRM App Pool's account)
SETSPN -A HTTP/mscrm.internal.domain.com:5555 mscrmsvc

For an App Pool running as NetworkService on the default port:

SETSPN -A HTTP/intranetweb syd-inet-web01     (that last part's the machine name)

Important bits: the account you're registering against (!), and the port, if it's not the usual port for that protocol. It's part of what identifies a unique service instance.

3. Only One Account Gets The Service Principal Name (SPN)

This is really important - any given SPN should map to one account onlyMany SPNs can be registered against one account , though, and typically are.

If you register the same SPN against a user account and a computer account, you've broken it. If you register the same SPN against two user accounts, you've broken it . If you register an SPN once, and someone else registers the same SPN against another account at some point in the future... they've broken it . Be careful.

Duplicate SPNs are the bane of a Kerberos implementation. If you thought of an SPN as a Primary Key in a database, you wouldn't be far wrong - it's the one thing that must uniquely identify a service's account to Kerberos. So, keep 'em unique, and we won't have a problem here.

A Kerberos KDC (Key Distribution Center - which is any DC, in Windows 2000/2003) uses the SPN to look up a domain account, then uses information about the account that it finds to encrypt the ticket it sends back to the client. If there are duplicates, well, Bad Happen.

Finding Duplicates! Gotta Catch Them All!

If you suspect you've got duplicate SPNs registered, you can use one of the following techniques:

Search using LDIFDE

First, work out what fragment of your SPN is pretty likely to be unique. For an SPN like HTTP/www.example.com , I'd call the www.example.com part the interesting part, and it'll let me keep the results down to a manageable number, I hope...

LDIFDE against a Global Catalog (GC)  - search all registered SPNs for the forest:

ldifde -f spns.txt -s gcname -t 3268 -r "(ServicePrincipalName=*spnfragment*)" -l ServicePrincipalName

So, with a GC called mygc.mydomain.dom, I'd use:

ldifde -f spns.txt -s mygc.mydomain.dom -t 3268 -r"(ServicePrincipalName=*www.example.com*)" -l ServicePrincipalName

Then crack open the SPNS.txt file in Notepad, and you've got a list of accounts that have that string registered against them. Using LDAP to solve problems! How cool!

LDIFDE against a DC - search all registered SPNs in a domain (SPNs are forest-wide, so you might want to try the forest/GC technique above unless you're in a single domain)

ldifde -f spns.txt -s dcname -r "(ServicePrincipalName=*spnfragment*)" -l ServicePrincipalName

Look at specific accounts using SetSPN

SetSPN - search for an SPN against a specific account name (not useful if you're not sure you know which account has the SPN you're interested in)

SETSPN -L accountname

Just Run AuthDiag

AuthDiag - part of the IIS Diagnostic Toolkit .

AuthDiag is often good for just about any authentication situation. Try it first, and be amazed at the automation.

For more info elsewhere...

There are loads of resources around on Kerberos troubleshooting, and if you're doing Delegation or even just Kerb authentication, it's worth your while having at least a quick skim; it's not named after the triple-headed dog at the gates of Hell for nothing*...

Other Resources You'll Hopefully Never Need To Read

Somewhere, my nice, simple post turned into a slightly more lengthy article, but the intent's there*.