Kerberos – a guide

Originally posted on 2019-10-12 on the old blog

There are many guides out there on how to implement Kerberos for application x-y-x.

But there are surprisingly few going into some of details that are important for the understanding what you are doing exactly.

So with this article I am going to hopefully help you understand how Kerberos works in a general sense, and that is not as difficult to implement as you might think (as long as you have the all the pieces of the puzzle).

What is Kerberos?

Kerberos is a modern authentication protocol for strong and secure authentication in a network and indeed across any network.
It is safe enough to be used across the the internet, but is rarely used in a configuration like that because the setup requirements makes it impractical.

Using Kerberos requires pre-existing knowledge of you client endpoints which is simply not practical in a public facing site.
But it can technically be used with a internet roaming (pre-configured) client, as long as the client can find and talk to the KDC and app server.

Kerberos it is in active developed by Massachusetts Institute of Technology.
More information can be found at its official site at MIT here.

How does Kerberos work?

The exact workings of Kerberos can be rather complicated.
But in general authentication to a service using Kerberos is between 3 devices; the client, the Key distribution center (KDC) and the application server.

Here is the (very simplified) process to access a resource on the app server using Kerberos:

* Client sends a authentication request (AS_REQ).
* KDC replies with to the (successful) request with a encrypted access ticket "TGT" (AS_REP).
* Client requests a service ticket for the app server from the KDC using the TGT ticket (TGS_REQ).
* KDC replies with a service ticket encrypted with the app server's secret key (TGS_REP).
* Client requests access to a resource on the app server using the service ticket (app server decrypts it using its own key) (AP_REQ).
* Optional: App Server replies to client to prove it the server the client is expecting (when mutual authentication is required) (AP_REP).

Going into the details would be a article all on its own, but the Kerberos Consortium has an excellent tutorial available here for those that are curious.

Kerberos in practice

Armed with the knowledge above we can go into the practical workings of Kerberos. And while I am going to focus on the implementation in a Microsoft Active Directory environment, I will start by killing off some myths.

For simplicity sake i will refer to Microsoft Active Directory as "AD DS".

Myth: Kerberos authentication can only be used by clients in a a AD DS enviroment

This is perhaps one of the most important misconceptions to get rid of.
As you might have noticed above Kerberos is a protocol under the MIT licence, and as such it has been implemented in various ways (AD DS, MIT Kerberos, Heimdal and so on).
Per krb5-1.13 onwards; interoperability between the implementations should be quite good, since support for a lot of the extensions has been added to the original source (ref.: Kerberos Features).

The only real requirements for using Kerberos from the client side is that it has a Kerberos client installed, and that it can communicate with the KDC to get it's tickets.
Windows and MacOS comes with a Kerberos client pre-installed, and most Linux distros has a pre-compiled version available in their repositories.
But basically it can work on any OS as long as you can compile the source for it.

There is of course also the requirements that the application or service can negotiate the protocol properly as well.
For example the Chromium project properly supports negotiating the protocol in HTTP scenarios.

Fun fact: You can add your Windows workgroup computer to a Kerberos realm using the ksetup tool. Though if it is a AD DS realm I recommend joining the computer properly to the domain.

Myth: Kerberos is an outdated authentication protocol

This is one i heard the other day.
When I asked someone to look into implementing Kerberos on a MS Exchange server (to move away from NTLM), I was told that this was unnecessary work:
“Kerberos is an outdated method that will be replaced by a more modern authentication method when moving to Office365.”

Granted Kerberos is not entirely suited for authentication for end user services using Azure AD across the internet, but it is in no way or form an outdated protocol.
Inside a on-premise AD DS it is by far the most seamless and secure SSO solution you can implement (maybe with the exception of using certificates, but the added complexity there can be immense).

Concepts for successful use of Kerberos

There are three primary concepts to understand in a Kerberos setup; realm, principal and ticket.

Realm

A realm indicates the boundaries in which the authentication is handled.
In AD DS this is for all intents and purposes basically what we are talking about when we talk about a domain.
In authentication scenarios this is often written as for example @ANITBLOG.NO.

Note: In Kerberos the realm name is case sensitive, in most situations it is common to simply write it in upper case.

Ticket

There are a lot of parts to a ticket, but for practical purposes a ticket is what identifies principals.

The tickets are encrypted with with the secrets (passwords) of the various principals.
A ticket is reusable and it is not possible for a KDC to control its use after it has been transmitted (you can only really deny the creation of new ones).
Therefore a ticket has several properties indicating it's use, and a predefined lifetime to limit potential abuse.

By default the lifetime of a ticket is 10 hours but this can be changed (usually shortened) depending on requirements.

Principals

Principals are the identifier for entities in a authentication database.
In short they are what identifies a user, host or service.
This is probably the part that trips people up the most during configuring Kerberos.

First a thing to note: In a AD DS you have probably touched upon the property "userPrincipalName" (UPN).
This is not the same as a Kerberos principal.
UPN are configured as an alternative logon username and is us usually configured to the user's mail address (description of the property can be found at MS Docs).
It has no bearing on the Kerberos authentication process.
USER principal

The user principal name identifier of an AD object is based on it's sAMAccountName.

For for a user with the sAMAccountNamejohnsmith”, the full principal name would then be “johnsmith@ANITBLOG.NO”.

For computer objects in AD DS, they get a “$” added to the sAMAccountName automatically.
So in that case the user principal name for the computer account would be “mycomputer$@ANITBLOG.NO”.

The user principal name is what would be used during the user identification part of the authentication process (AS_REQ/AS_REP).

Service Principal

The “service princpal name” (SPN) is what identfies a service in the Kerberos authentication process (TGS_REQ/TGS_REP).

In AD DS this is defined in the AD-object attribute _servicePrincipalName_.
This is a multi string attribute. And can contain several identifiers.

When the KDC gets a request for a service ticket it will look for a SPN matching the request.
If it finds a match it will send back a service ticket encrypted with the credentials of the AD-object the that has the SPN assigned.
If the KDC is unable to find a SPN matching the request, then the authentication fails.

The above is extremely important to note and is why Kerberos often fails!
For Kerberos to work the correct SPN must be found!

Service principals are usually written as <service>/<host> so for a http (or https) server in my example realm the SPN would be something like http/webserver.anitblog.no.
The full service principal name would then be: http/webserver.anitblog.no@ANITBLOG.NO.

Also remember that this is reliant on name resolution, so if you access the server with only the NetBIOS name you would also need a entry for http/webserver too!

Herein also lies another stumbling block; the DNS name resolution.
If you use a A/AAAA record to reach your server all is well, the SPN gets resolved correctly.
However if you use a CNAME then you might get issues.

If in our case webserver.anitblog.no is a alias for iishost.anitblog.no, then the SPN that will be requested will most likely be for http/iishost.anitblog.no instead, which might fail depending on configured SPN!

Usually the simplest is to avoid CNAME records and use A/AAAA records instead.
Depending on the use case you might simply add the SPN needed to the host server.

Or in case of load balancing; you would use a service account with the correct SPN running the service itself, and have the DNS record resolve to the load balancer.

The balancer then redirects the traffic to the back-end servers which can decrypt the service tickets using the service account credentials.
Usually no authentication should be handled on the balancer itself, but there might be scenarios where this is needed.
But that would be out of scope for this guide.

The “HOST” SPN

Note that there is a special type of service called HOST. This is actually a alias for several known service types.

The alias is defined in the sPNMappings attribute on the Directory Service object found at:

"CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=EXAMPLE,DC=COM". 

In general unless you have a very good reason you should avoid messing with this attribute.
But it is useful to be aware of this.

Also if a specific entry is made for one of the services HOST is an alias for, then the specific entry will be used and the HOST alias is ignored.

The SPN search pattern is basically something like this (using http/ as an example):
KDC looks for "http/<host>" -> "http/<host>" found on object -> All good!

KDC looks for "http/<host>" -> "http/<host>" not found -> substituting "http/<host>" with "HOST/<host>" -> "HOST/<host>" found -> All Good!

KDC looks for "http/<host>" -> "http/<host>" not found -> substituting "http/<host>" with "HOST/<host>" -> "HOST/<host>" not found -> Fail!  

Working with service principal names

So now we know a bit on the authentication process for Kerberos.
Lets take a closer look at SPN that are required for all this to work.

Modifying SPN

As noted earlier the SPN are defined in the AD attribute servicePrincipalName this means that any method of editing AD attributes of a object is valid for for modifying SPN.

The most commonly used way is to use the tool setspn.exe but it is by no means the only method (here is a reference for the tool).

You can also use the Active Directory PowerShell cmdlets, for example by using parameter -ServicePrincipalName.
Or you can modify it using the PowerShell accelerator [ADSI].

You can of course also edit it using GUI tools like “AD Users and Computers”, “ADSI Edit” or even ldp.exe.

Using setspn.exe is pretty straight forward like this:

This will assign the SPN "http/webhost.anitblog.no" to the AD object  "iishost" 

setspn.exe -s http/webhost.anitblog.no iishost

Adding SPN using the PowerShell cmdlets:

This will assign the SPN “http/webhost.anitblog.no” to the AD computer “iishost”

Set-ADComputer -Identity “iishost$” -ServicePrincipalNames (@{Add=“http/webhost.anitblog.no”})

Note that we are using the sAMAccountName here so we add a $ at the end of the identity.

While only showing for the cmdlet ADComputer, process is similar on ADUserand ADServiceAccount.

ADObject cmdlets require a bit more effort to use but they could allow you to make a script that works regardless of AD object type.

Here we first need to find the object using a Get-ADObject filter.
Then we pipe it to Set-ADObject using the parameter -Add to add our SPN to the servicePrincipalName attribute.

Get-ADObject -Filter "sAMAccountName -eq 'iishost$'" | Set-ADObject -Add @{servicePrincipalName="http/webhost.anitblog.no"}

Using ADSI can be a bit difficult but nonetheless possible as well:

Create the ADSI object

[ADSI]$ADObject = (([adsisearcher]'sAMAccountName=iishost$').FindOne()).Path

Append SPN to the object

$ADObject.PutEx('3', 'servicePrincipalName', @('http/webhost.anitblog.no'))

Write info back to directory

$ADObject.SetInfo()

Working with ADSI is pretty low-level as you are working directly with .net classes.
You rarely will need to do this, but it should always be available on any Windows computer past Windows 7.

Some more details on the PutEx method can be found in MS Docs.
Also here is a quick reference the PutEx actions:
CLEAR = 1 UPDATE = 2 APPEND = 3 DELETE = 4

In general most methods will fail if you try to create duplicate SPN.
But some tools might misbehave so it can still happen....

Finding SPN

In any case it is useful to know some commands to check if an SPN has been set, so here are a few ways to do that.
If you are trying just to quickly check if a SPN is defined use you can use:

setspn.exe -Q <SPN>

Using PowerShell cmdlet Get-ADObject you can do it like this (this also support wildcards):

Get-ADObject -Filter 'servicePrincipalName -eq "http/webserver.anitblog.no"' -Properties Name,servicePrincipalName

And using [ADSISearcher]:

([adsisearcher]'servicePrincipalName=http/webserver.anitblog.no').FindAll()

If you are simply trying to find any duplicates while troubleshooting the most simple way is:

setspn.exe -X

If you are automating I would strongly recommend using either the PowerShell cmdlets or if the AD-cmdlets are not installed, use the accelerators (which should always be available from Win 7 and onwards). These will make it easier to handle errors since they will output native .net objects or exit codes you can use. If you were to use setspn.exe you would have to parse the string output.

Putting it all into use

Now we should have a decent grasp of the requirements on the KDC side of things so lets move on practicals.
I am not going to dive very deep into this, as that would warrant it's own article. But we will go over the basics.

Clients

On the client side; if your Windows client is in the domain, no further configuration is needed to use Kerberos.
All of this is handled in the background.

On MacOS or Linux (or Windows client in a workgroup) you will have to configure you Kerberos client to be able to get valid tickets from the Kerberos realm.
In most cases this involves invoking [kinit](https://web.mit.edu/Kerberos/krb5-latest/doc/user/user_commands/kinit.html), or using [ksetup](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ksetup) on Windows.
Of course it is possible to use other Kerberos implementations on Windows depending on requirements as well.

Also to note: Microsoft has expanded Kerberos with something called "Service4User" (S4U).
This is also often referred to as protocol transition.
This extension allows a service to contact a KDC on behalf of a client if they can provide the correct credentials through other means.
This can be by for example; username and password (basic, NTLM) or client certificate.
Details on the extension can be found here.

Servers

Servers have pretty much the same requirements as clients.
They need to have a working Kerberos client, so if your Windows Servers are joined to the domain, you are all good.

For other platforms you would usually need to create Kerberos keytabs to store their credentials, and use those with for example kinit.
In AD DS, keytab creation is done with [ktpass](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ktpass), for other platforms this is usually done with the tool [ktadd](https://web.mit.edu/Kerberos/krb5-latest/doc/admin/appl_servers.html).

VERY IMPORTANT!It cannot be stressed enough but a keytab is very much the equivalent of storing a username and password in a clear text file.And as such it must be protected in the same way (absolutely do not send it by e-mail for example).

For your services; as long as you assign the correct SPN to the account running your service (computer, user or service account).
Or the server running the service can access the credentials on behalf of the account with the SPN, things should just work™ (mostly depending on the service itself)....

Summary

So to summary this very long article; while Kerberos itself is a complex beast, using it needs not be so.

In your AD DS all the pieces are already in place, so the most important detail is simply to set the SPN correctly.

Second important part is to that pay attention to how the service names gets resolved by the DNS.
This is a vital part of how the client figures out just which service they are requesting access to (so be careful when using CNAME).

But before we leave I have a few points that needs to be covered.

IIS Negotiate

On a IIS server with Windows Authentication it is strongly recommended to only have Negotiate as the provider, unless you have a specific specific issue.
The HTTP Auth Negotiate method is implemented in most http clients (like in Chromium as mentioned earlier), and it will negotiate the use of strongest available authentication method.

Usually this means that Kerberos will first be attempted, and if that fails it will fall back to NTLM.

You can also force the use of Kerberos by using Negotiate:Kerberos if the service should only be available when authenticated with Kerberos.

Kerberos encryption ciphers

In short Kerberos can work using several encryption types.
For a AD DS these are:
DES_CBC_CRC
DES_CBC_MD5
RC4_HMAC_MD5
AES128_HMAC_SHA1
AES256_HMAC_SHA1

Of these it is only recommended to use the AES ones.
The DES type are disabled by default in most versions of AD DS, but RC4 is still enabled by default.
It is strongly recommended that the RC4 cipher is disabled and that only AES types are allowed.
Here is Microsoft's information on this.

When you are working on disabling RC4 pay close attention if the domain is part of a forest.
In that case the domain trust must be marked as supporting AES as well.
Microsoft has a KB on this here.

Comments

While I have have only glanced by some points, I hope this will be a helpful primer on understanding and fully utilizing Kerberos.

As always; please comment if there is something that incorrect or unclear. I'll be happy to correct it.