Description
The Kerberos support documented for the ML Java API at the below link requires that the user issue “kinit” to create and cache a TGT with the Kerberos Key Distribution Center. This restricts any Java application to be run interactively by the user ``
https://docs.marklogic.com/guide/java/intro#id_70914
If the requirement is to run the Java application as a scheduled task or unattended then no TGT will be present in the cache and the application will fail with the following exception.
Exception in thread "main" com.marklogic.client.FailedRequestException: Unable to obtain password from user
The usual method to deal with this, which does not require running “kinit” manually is to use a KeyTab instead. We have a KB article which documents how to do this with the XCC/J library (https://help.marklogic.com/Knowledgebase/Article/View/455/15/authenticating-xccj-applications-using-a-kerberos-keytab )but this will not work with the Java API code as there is an explicit call to reset Kerberos configuration within the
HTTPKerberosAuthInterceptor.KerberosLoginConfiguration class
options.put("refreshKrb5Config", "true");
I’ve written a small update to this class that will allow using a KeyTab if you set the system property “com.marklogic.krb5.keytab.path” and specify a valid Kerberos KeyTab path to use, e.g.
// private DatabaseClient client;
private static String appServerHostName = "mlsrv.marklogic.local";
private static String kdcPrincipalUser = "[email protected]";
private static int appServerHostPort = 8011;
private static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
System.setProperty("com.marklogic.krb5.keytab.path","/tmp/user.keytab");
LOG.info("trying to connect using the Kerberos Auth Context");
DatabaseClient client = DatabaseClientFactory.newClient(appServerHostName,
appServerHostPort, new DatabaseClientFactory.KerberosAuthContext(kdcPrincipalUser));
client.newServerEval().xquery("1+1");
}
Output with Debug enabled showing Keytab being used if keytab property is set.
[user1@mlsrv ~]# java -jar ./KerberosAPI.jar
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator true KeyTab is /tmp/user.keytab refreshKrb5Config is false principal is [email protected] tryFirstPass is false useFirstPass is false storePass is false clearPass is false
principal is [email protected]
Will use keytab
Commit Succeeded
If no keytab property is set, then the Kerberos functionality remains as it is and the TGT is acquired from the cache if available following kinit.
[user1@mlsrv ~]# java -jar ./KerberosAPI.jar
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Debug is true storeKey false useTicketCache true useKeyTab false doNotPrompt true ticketCache is null isInitiator true KeyTab is null refreshKrb5Config is true principal is [email protected] tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Acquire TGT from Cache
Principal is [email protected]
Commit Succeeded
The changes to the HTTPKerberosAuthInterceptor.KerberosLoginConfiguration class are small, only the lines highlighted below so I don’t know if you want to consider adding them as is or if you would like me to create a Pull Request on the Java API GitHub site?
options.put("refreshKrb5Config", "true");
if (System.getProperty("com.marklogic.krb5.keytab.path") == null) {
options.put("useTicketCache", "true");
options.put("doNotPrompt", "true");
} else {
options.put("useKeyTab", "true");
options.put("keyTab", System.getProperty("com.marklogic.krb5.keytab.path"));
options.put("storeKey", "true");
}