06 February 2017

Kerberos Debugging in Java

Working with Kerberos can easily cause a lot of trouble. Troubleshooting can take several hours.
In this blog I'll show you what will help you best when using Kerberos with Java for example to secure a Hadoop cluster.

When Kerberos is not working as expected it is important to understand why. Enabling Kerberos debug logging is a very valuable resource to understand what is happening.
To enable Kerberos debugging you need to set the following JVM property:
Now read your log file very carefully. This will help you to understand what is missing.

Usually you will define your Kerberos configuration within your C:\Windows\krb5.ini or /etc/krb5.conf file. Make sure that your hostname mapping to your Kerberos realm is correct in here.
There are also a few other JVM properties that are usually not required, but can be useful to override/define your configuration at application startup:
Kerberos is very sensitive to DNS configuration.

Here are some more shell commands that are very helpful to test if Kerberos is working in general (outside of your Java application):
# Login with a specific keytab file
kinit -k -t /path/to/your/keytab

# List all local available tokens. After kinit there should be at least your tgt token.

# Request a ticket for a specific service. Check if the service is registered correctly at your Kerberos server.
kvno service/hostname@domain

Here is a sample configuration for your krb5.ini file:
default_realm = HORTONWORKSHA.COM
dns_lookup_kdc = false
dns_lookup_realm = false
ticket_lifetime = 86400
renew_lifetime = 604800
forwardable = true
default_tgs_enctypes = rc4-hmac
default_tkt_enctypes = rc4-hmac
permitted_enctypes = rc4-hmac
udp_preference_limit = 1
kdc_timeout = 3000

  kdc = talend.cloudera57
  admin_server = talend.cloudera57
  kdc = hdp24masternode
  admin_server = hdp24masternode

.hortonworksha.com = HORTONWORKSHA.COM
hortonworksha.com = HORTONWORKSHA.COM
.clouderaha.com = CLOUDERAHA.COM
clouderaha.com = CLOUDERAHA.COM

And here is another example for a JAAS configuration file (first for a normal user and second for a technical service account):
alice { 
    com.sun.security.auth.module.Krb5LoginModule required

sts { 
    com.sun.security.auth.module.Krb5LoginModule required

If you want to define the location for your cached tickets, set the following system property accordingly:
# Windows Style
set KRB5CCNAME="C:\Users\jbernhardt\krb5cc_jbernhardt"

# Linux Style
export KRB5CCNAME="/home/jbernhardt/krb5cc_jbernhardt"

Sample Java Code:
package test;

import java.io.IOException;
import java.net.URL;
import java.security.Principal;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

public class JaasLoginTest {

    public static void main(String argv[]) {
        URL conf = JaasLoginTest.class.getClassLoader().getResource("jaas.conf");
        System.setProperty("java.security.auth.login.config", conf.toString());
        System.setProperty("java.security.krb5.realm", "TEST.TALEND.DE");
        System.setProperty("java.security.krb5.kdc", "");
        System.setProperty("sun.security.krb5.debug", "true");
        // Only needed when not using the ticket cache
        CallbackHandler callbackHandler = new CallbackHandler() {
            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                for (Callback callback : callbacks) {                
                    if (callback instanceof NameCallback) {
                    if (callback instanceof PasswordCallback) {

        try {
            LoginContext lc = new LoginContext("alice", callbackHandler);
//            LoginContext lc = new LoginContext("sts", callbackHandler);
            Subject subject = lc.getSubject();
            Set<Principal> principals = subject.getPrincipals();
            Set<Object> credentials = subject.getPrivateCredentials();
            System.out.println("OK: " + principals);
            System.out.println("OK: " + credentials);
        } catch (LoginException e) {

Useful Links