Kerberos Authentication on Tomcat

I finally got around to testing out TomCat 8 and setting up Kerberos authentication for a “single sign-on” experience (i.e. it re-uses the domain logon Kerberos token to authenticate users). This was all done in a docker image, so the config files can be stashed and re-used by anyone with Docker.

First you need an account – on the account properties page, the DES encryption needs to be unchecked and the two AES ones need to be checked. The account then needs to have a service principal name mapped to it. That name will be based on the URL used to access the site. In my case, my site is http://lisa.example.com:8080 (SPNs don’t mind http/https or port numbers) so my SPN is HTTP/lisa.example.com … to set the SPN, run

setspn -A HTTP/lisa.example.com sAMAccountNameOfMyNewlyCreatedAccount

Then generate the keytab:

ktpass /out .\lisa.example.com.keytab /mapuser sAMAccountNameOfMyNewlyCreatedAccount@EXAMPLE.COM /princ HTTP/lisa.rushworth.us@EXAMPLE.COM /pass P@ssw0rdG03sH3r3

** Note about keytabs – there is a KVNO (key version number) associated with a keytab file. When security-related attributes on the account are changed, the KVNO is incremented. Aaaand you need a new keytab. This means you need to be able to get a new keytab if you plan on changing the account password, but it also means that tweaking account settings can render your keytab useless. Get the account all sorted (check off password never expires if that’s what you want, check off user cannot change password, etc) and then generate the keytab.

While you’re working on getting the SPN and keytab stuff sorted, get docker installed and running on your box. I use Docker CE (free) on my Windows laptop, and I’ve had to disable the firewall to allow access from external clients. I would expect a rule (esp one allowing anything to make an inbound connection to 8080/tcp!) would sort it, but I’ve always had the port show as filtered until the firewall is turned off. YMMV.

I create a folder for files mapped into docker containers (i.e. c:\docker) and sub-folders for each specific container. All of the files from TomcatKerberosConfigFiles are unzipped into that folder. The test website is named lisa.rushworth.us and is either set up in DNS or added to c:\windows\system32\drivers\etc\hosts on the client(s) that will access the site. And, of course, there’s a client machine somewhere logged onto the domain. You are going to need to tweak my config files for your domain.

In jaas.conf — I have debug on. Good for testing and playing around, bad for production use. Also you’ll need your SPN and keytab file name

principal="HTTP/lisa.example.com@EXAMPLE.COM"
keyTab="/usr/local/tomcat/conf/lisa.example.com.keytab"

In krb5.conf — the encryption is about the only thing you can keep. Use your hostnames and domain name (REALM). If you have multiple domain controllers, you can have more than one “kdc = ” line in the realms.

[libdefaults]
default_realm = EXAMPLE.COM
default_keytab_name = /usr/local/tomcat/conf/lisa.rushworth.us.keytab
default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
permitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
forwardable=true

[realms]
RUSHWORTH.US = {
kdc = exchange01.example.com:88
master_kdc = exchange01.example.com:88
admin_server = exchange01.example.com:88
}

[domain_realm]
example.com= EXAMPLE.COM
.example.com= EXAMPLE.COM

In web.xml – Roles may need to be sorted around (I’m not much of a TomCat person, LMGTFY if you want to do something with roles). Either way, the realm needs to be changed to yours

<realm-name>EXAMPLE.COM</realm-name>

Once Docker is running and the files are updated with your domain info, install the tomcat:8.0 image from the default repository. Start the container mapping all of the custom config files where they go:

docker run -detach --publish 8080:8080 --name tomcat8 --restart always -v /c/docker/tomcat8/tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml:ro -v /c/docker/tomcat8/lisa.example.com.keytab:/usr/local/tomcat/conf/lisa.example.com.keytab:ro -v /c/docker/tomcat8/krb5.conf:/usr/local/tomcat/conf/krb5.conf:ro -v /c/docker/tomcat8/jaas.conf:/usr/local/tomcat/conf/jaas.conf:ro -v /c/docker/tomcat8/web.xml:/usr/local/tomcat/webapps/examples/WEB-INF/web.xml:ro -v /c/docker/tomcat8/context.xml:/usr/local/tomcat/webapps/examples/WEB-INF/context.xml:ro -v /c/docker/tomcat8/logging.properties:/usr/local/tomcat/conf/logging.properties:ro -v /c/docker/tomcat8/spnego-r9.jar:/usr/local/tomcat/lib/spnego-r9.jar:ro -v /c/docker/tomcat8/login.conf:/usr/local/tomcat/conf/login.conf:ro -v /c/docker/tomcat8/testAuth.jsp:/usr/local/tomcat/webapps/examples/testAuth.jsp:ro tomcat:8.0

A couple of useful things about Docker — the container ID is useful

C:\docker\tomcat8>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4e06b32e1ca8 tomcat:8.0 "catalina.sh run" 12 minutes ago Up 12 minutes 0.0.0.0:8080->8080/tcp, 0.0.0.0:8888->8080/tcp tomcat8

But most commands seem to let you use the ‘friendly’ name you ascribed to the container. Running “docker inspect” will give you details about the container – including its IP address. I’ve found different images use different settings: some map to localhost on my box, some get an IP address within my DHCP range.

C:\docker\tomcat>docker inspect tomcat8 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",

Since this is an image that maps to localhost on my box, I need the lisa.example.com hostname to resolve to my laptop’s IP address. For simplicity, I did this by editing the c:\windows\system32\drivers\etc\hosts file.

Shell into the container:

docker exec -it tomcat8 bash

Update your packages and install the kerberos client utilities:

root@4e06b32e1ca8:/usr/local/tomcat/conf# apt-get update
root@4e06b32e1ca8:/usr/local/tomcat/conf# apt-get install krb5-user

Then test that your keytab is working:

root@4e06b32e1ca8:/usr/local/tomcat/conf# kinit -k -t ./lisa.example.com.keytab HTTP/lisa.example.com@EXAMPLE.COM
root@4e06b32e1ca8:/usr/local/tomcat/conf# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: HTTP/lisa.example.com@EXAMPLE.COM

Valid starting Expires Service principal
07/08/2017 18:27:38 07/09/2017 04:27:38 krbtgt/EXAMPLE.COM@EXAMPLE.COM
renew until 07/09/2017 18:27:38

Assuming you don’t get errors authenticating using the Kerberos client utilities, try accessing the TomCat site. I’ve added a testAuth.jsp file to the examples webapp – it shows the logon method, user name, and what roles they have:

09-Jul-2017 15:42:55.734 FINE [http-apr-8080-exec-1] org.apache.catalina.authenticator.SpnegoAuthenticator.authenticate Unable to login as the service principal
java.security.PrivilegedActionException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)

Verify that your SPN is set to the same name being used to access the site. I’m not sure why the configured service principal name doesn’t supersede the user-entered hostname. But I got nothing but auth failures until I actually entered the hostname into my hosts file and used an address that matches the service principal name.

Leave a Reply

Your email address will not be published. Required fields are marked *