Categories: Java
I recently wrote a simple Java app which used the Hive JDBC driver to connect to a Kerberos-protected Hive database. Running it gave:
Caused by: GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
Solving this took me a significant amount of time. Here is the answer..
When the JDBC driver sees the URL contains option “principal=xyz”, it knows that it needs to create a Kerberos service ticket from some Subject to the service specified by the principal option. However it is not the responsibility of the JDBC driver to create the subject - it just assumes that one is already bound to the thread’s security context.
So how does a suitable Subject get bound? Well one option is to write code to do it; a good example can be found in this Hive JIRA ticket. The constructor-method LoginContext(contextName, handler)
and method login
expect that system property java.security.auth.login.config
points to a file with one or more LoginContext entries in it, and one of those entries matches the specified contextName.
Here is an example of setting the path to a jaas config file:
java -Djava.security.auth.login.config=./some_jaas_config.conf ...
and here is an example of a config entry for kerberos authentication:
Client {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
StoreKey=true
useKeyTab=true
keyTab="./some_user.keytab"
principal="some_user@SOME_DOMAIN";
};
One possible solution to building an app around the Hive JDBC driver is thus: add the code from the example linked above, and then pass a config-file with the appropriate LoginContext definition. More details can be found in the jaas documentation.
The loginContext name “Client” is a common choice; many (but not all) applications use this name.
However there is an easier way, and one that works with existing apps that cannot be modified: JAAS has an option to define a fallback mechanism for obtaining credentials. And the default fallback is to look for a Kerberos ticket in the standard place (often “/tmp/krb5cc_{uid}
”) which kerberos tools like kinit
put it. So given
-Djavax.security.auth.useSubjectCredsOnly=false
the JVM will pick up an existing Kerberos ticket, and the app will run - without needing a jaas config-file at all. In general, that’s all that is needed. No authentication-specific code needs to be added to the simple JDBC-based app. Really trivial!
Of course this fails if there is no current Kerberos ticket. However flag useSubjectCredsOnly=false
also enables a “fallback” section in the JAAS config file: if there is no section specifically for the required subject-name, then the section for subject-name “com.sun.security.jgss.initiate
” will be used.
Thus with the options and JAAS config below, the JDBC Hive driver successfully connects without requiring a pre-existing Kerberos ticket:
-Djava.security.auth.login.config=./some_jaas_config.conf -Djavax.security.auth.useSubjectCredsOnly=false
where the config file has:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
StoreKey=true
useKeyTab=true
keyTab="./some_user.keytab"
principal="some_user@SOME_DOMAIN";
};