
I had to recently modify a web service container (Apache Axis 2) to support the WS-Security Kerberos token profile.
To achieve this, I had to go from knowing absolutely nothing about Kerberos, to being a near expert in it and its related technologies. This involved having to do my own builds of the Apache WS-Security implementation, getting a complete understanding of the WS-Security Kerberos token profile for SOAP messages, learning how to configure and setup Key Distribution Centers (KDCs) such as Active Directory and Apache Directory and finally learning the ins and outs of the Java JAAS/GSS APIs to implement all this.
Part of this process involved me having to get a complete understanding of how the Kerberos algorithm works, including what on earth Service Principal Names (SPNs) are.
Googling around for this, and trawling through the Sun Java forums was only enough to give me bits and pieces of info. Even talking to so called "experts" in the Sun forums was not enough to answer these questions. The ironic thing about this is, that the answers are extremely simple, once you understand the Kerberos algorithm.
Note that this blog is specific to Windows domains running Active Directory. I will do a blog in the future about setting up Apache Directory Server as your KDC.
1. What is a SPN?
A service that is being secured by Kerberos, is required to have an identity within the realm that the system exists. On a Windows network, your service must have an identity in Active Directory (if AD is the domain controller), in the same way a user logging in to your client application must also have an identity in AD (user account).
In Kerberos, when a client identifies the service they are wanting to talk to, a Kerberos naming convention is used to identify that service. This convention is different from how AD would identify that service's user account. Whereas AD would know the account by a simple username, the Kerberos standard includes information such as the machine and the domain that the service runs on.
An SPN, is simply a way of telling AD that the Kerberos identity x maps to the AD account y. Then when Kerberos requests are made to AD, it can translate the Kerberos identities to the correct AD accounts.
2. What does a Kerberos SPN look like?
This is the part where it is quite confusing. The actual format for SPNs is supposed to be something like:
< service type >/< instance name >:< port number >/< service name >
However, not all this information is needed. I never specify the port number, as there is only ever one web services container running on the target machine. Additionally, I don't usually specify the realm as my stuff only runs within one realm anyway, and the JAAS library appears to append this information anyway.
For me, the best answer to this question is "Something that works!". Hence I use one of the following :
< service name >/< instance name >
< service name >/< instance name > @ < realm name >
These have worked fine for me with both an AD domain and an Apache Directory domain.
E.g. mywebservice/testmachine or mywebservice/testmachine@example.com
Note that in some cases, computers are also given Kerberos identities to prevent spoofing of machines. The format for a computer on a network is:
host / < machine name >.realm.com@REALM.COM
3. How do I set up SPNs in Active Directory?
Most of the info I found on the net pointed to the ktpass tool for setting up Kerberos SPNs. I had real trouble getting this to work correctly in Active Directory, specifically with the encryption types being incompatible.
Then I found the setspn.exe program that comes with Active Directory, and it sorted all my problems out.
Setspn.exe lets you set your Kerberos to AD mappings up and also will list the SPNs for a given AD account. It is simple to use, and you don't have to worry about DesMd5 vs ArcFourHmac encryption errors.
The following command adds an SPN for a user account:
setspn -A servicename/machine ad-service-account-name
e.g. setspn -A datawebservice/testserver datawebservice
where datawebservice is the AD identity/account for the web service.
Use the -l parameter to list the SPNs for a given account.
4. Sidestep SPNs.
I also found that it is possible to simply ignore SPNs when using the Java GSS API. When specifying a GSS server name to create a context with, simply specify it as an NT_USERNAME instead of an NT_HOSTBASED_SERVICE.
e.g. GSSName serverName = manager.createName( "datawebservice", GSSName.NT_USER_NAME);
Note that if you are having to interop with .NET services, you may not be able to do this. Additionally, .NET Kerberos applications may not be as flexible in their Kerberos naming rules.
7 comments:
Great article. What did you have to do to the apache WS-Security libraries, though & do you have any sample policy files, etc you could share with us?
Thanks,
Hi John, I'll put a blog together about performing Kerberos with Axis-2/WS-Security. I've been extending Axis-2 for these purposes, as it doesn't actually have any Kerberos support at the moment - and the code changes are around 1000 lines of code. I'll get it up in the next two days.
Cool - I'll look forward to that, chanks
Thanks for your blog. I got it to run with the example. However once I changed to my own realm and use kinit it keeps trying to use example.com.
Browsing the LDAP only shows my domain. If I do a ldapsearch for example it doesn't exist as expected.
The MonitorRequest comes in on MYREALM but the AuthenticationService is using example.com?
I've deleted the example working directory in /var/lib/apacheds/default/partitions and remove all mention of example from my server.xml.
I've check my /etc/hosts file and /etc/krb5.conf and they all use my domain and realm not example.
The saslRealms and partitionConfigurations in server.xml only have my realm.
jvm 1 | [12:49:47] DEBUG [org.apache.directory.server.kerberos.kdc.MonitorRequest] - Received Authentication Service (AS) request:
jvm 1 | messageType: initial authentication request (10)
jvm 1 | protocolVersionNumber: 5
jvm 1 | clientAddress: 192.168.2.114
jvm 1 | nonce: 1223380187
jvm 1 | kdcOptions: FORWARDABLE RENEWABLE_OK
jvm 1 | clientPrincipal: carl@MYREALM.COM
jvm 1 | serverPrincipal: krbtgt/MYREALM.COM@MYREALM.COM
jvm 1 | realm: MYREALM.COM
jvm 1 | [12:49:47] DEBUG [org.apache.directory.server.core.authn.AuthenticationService] - Testing if entry name = 'ou=users,dc=example,dc=com' exists
To recap I've checked;
/var/lib/apacheds/default/partitions
/etc/hosts
/etc/krb5.conf
/var/lib/apacheds/default/conf/server.xml
/opt/apacheds-1.5.1/conf/apacheds.conf
/var/lib/apacheds/default/conf/apacheds.conf
I'm on RHEL5
It seems like I'm just missing a source somewhere. Any ideas?
BTW: I load the LDIF from a directory which only has one file with only my domain and realm in it.
I've narrowed it down. Kerberos is now working in my realm and on my domain.
However once I change the PartitionConfiguration suffix to mine it keeps trying to use dc=example instead of my dc.
Wow,Lovely blog!
Post a Comment