You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by rm...@apache.org on 2021/01/01 19:16:07 UTC

[ranger] branch ranger-2.2 updated: RANGER-3134:Remove global JAAS Configuration for Ranger auditing to SOLR

This is an automated email from the ASF dual-hosted git repository.

rmani pushed a commit to branch ranger-2.2
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.2 by this push:
     new d894ad8  RANGER-3134:Remove global JAAS Configuration for Ranger auditing to SOLR
d894ad8 is described below

commit d894ad84d620a8b8da19c4ed2f9c25485fe8496b
Author: Ramesh Mani <rm...@cloudera.com>
AuthorDate: Wed Dec 30 23:24:11 2020 -0800

    RANGER-3134:Remove global JAAS Configuration for Ranger auditing to SOLR
---
 .../audit/destination/SolrAuditDestination.java    |  69 ++++--
 .../ranger/audit/utils/AbstractKerberosUser.java   | 246 +++++++++++++++++++++
 .../audit/utils/InMemoryJAASConfiguration.java     | 138 ++++++++----
 .../apache/ranger/audit/utils/KerberosAction.java  |  89 ++++++++
 .../ranger/audit/utils/KerberosJAASConfigUser.java |  78 +++++++
 .../apache/ranger/audit/utils/KerberosUser.java    |  87 ++++++++
 .../patch/cliutil/DbToSolrMigrationUtil.java       |  15 +-
 .../main/java/org/apache/ranger/solr/SolrMgr.java  |  14 +-
 8 files changed, 668 insertions(+), 68 deletions(-)

diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java
index cf0ba77..5a7a7f4 100644
--- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java
@@ -26,7 +26,9 @@ import org.apache.ranger.audit.model.AuditEventBase;
 import org.apache.ranger.audit.model.AuthzAuditEvent;
 import org.apache.ranger.audit.provider.MiscUtil;
 import org.apache.ranger.audit.utils.InMemoryJAASConfiguration;
-import org.apache.ranger.audit.utils.SolrAppUtil;
+import org.apache.ranger.audit.utils.KerberosAction;
+import org.apache.ranger.audit.utils.KerberosUser;
+import org.apache.ranger.audit.utils.KerberosJAASConfigUser;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.HttpClientUtil;
@@ -62,6 +64,7 @@ import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.login.LoginException;
 
 import java.util.Arrays;
 import java.util.Optional;
@@ -80,6 +83,7 @@ public class SolrAuditDestination extends AuditDestination {
 	public static final String PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG = "java.security.auth.login.config";
 
 	private volatile SolrClient solrClient = null;
+	private volatile KerberosUser kerberosUser = null;
 
 	public SolrAuditDestination() {
 	}
@@ -96,14 +100,25 @@ public class SolrAuditDestination extends AuditDestination {
 	public void stop() {
 		LOG.info("SolrAuditDestination.stop() called..");
 		logStatus();
-		try {
-			if (solrClient != null) {
+
+		if (solrClient != null) {
+			try {
 				solrClient.close();
+			} catch (IOException ioe) {
+				LOG.error("Error while stopping slor!", ioe);
+			} finally {
+				solrClient = null;
+			}
+		}
+
+		if (kerberosUser != null) {
+			try {
+				kerberosUser.logout();
+			} catch (LoginException excp) {
+				LOG.error("Error logging out keytab user", excp);
+			} finally {
+				kerberosUser = null;
 			}
-		} catch (IOException ioe) {
-			LOG.error("Error while stopping slor!", ioe);
-		} finally {
-			solrClient = null;
 		}
 	}
 
@@ -264,7 +279,7 @@ public class SolrAuditDestination extends AuditDestination {
 				docs.add(document);
 			}
 			try {
-				final UpdateResponse response = SolrAppUtil.addDocsToSolr(solrClient, docs);
+				final UpdateResponse response = addDocsToSolr(solrClient, docs);
 
 				if (response.getStatus() != 0) {
 					addFailedCount(events.size());
@@ -345,12 +360,19 @@ public class SolrAuditDestination extends AuditDestination {
 					LOG.warn("No Client JAAS config present in solr audit config. Ranger Audit to Kerberized Solr will fail...");
 				}
 			 }
+
 			 LOG.info("Loading SolrClient JAAS config from Ranger audit config if present...");
-			 InMemoryJAASConfiguration.init(props);
-			} catch (Exception e) {
-				LOG.error("ERROR: Unable to load SolrClient JAAS config from Audit config file. Audit to Kerberized Solr will fail...", e);
-		}
-        finally {
+
+			 InMemoryJAASConfiguration conf = InMemoryJAASConfiguration.init(props);
+
+			 KerberosUser kerberosUser = new KerberosJAASConfigUser("Client", conf);
+
+			 if (kerberosUser.getPrincipal() != null) {
+				this.kerberosUser = kerberosUser;
+			 }
+		} catch (Exception e) {
+			LOG.error("ERROR: Unable to load SolrClient JAAS config from Audit config file. Audit to Kerberized Solr will fail...", e);
+		} finally {
 			 String confFileName = System.getProperty(PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG);
 			 LOG.info("In solrAuditDestination.init() (finally) : JAAS Configuration set as [" + confFileName + "]");
 		}
@@ -464,6 +486,27 @@ public class SolrAuditDestination extends AuditDestination {
 		return sslContext;
 	}
 
+	private  UpdateResponse addDocsToSolr(final SolrClient solrClient, final Collection<SolrInputDocument> docs) throws Exception {
+		final UpdateResponse ret;
+
+		try {
+			final PrivilegedExceptionAction<UpdateResponse> action = () -> solrClient.add(docs);
+
+			if (kerberosUser != null) {
+				// execute the privileged action as the given keytab user
+				final KerberosAction kerberosAction = new KerberosAction<>(kerberosUser, action, LOG);
+
+				ret = (UpdateResponse) kerberosAction.execute();
+			} else {
+				ret = action.run();
+			}
+		} catch (Exception e) {
+			throw e;
+		}
+
+		return ret;
+	}
+
 	private InputStream getFileInputStream(String fileName) throws IOException {
 		InputStream in = null;
 		if (StringUtils.isNotEmpty(fileName)) {
diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
new file mode 100644
index 0000000..fd1c96e
--- /dev/null
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.audit.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public abstract class AbstractKerberosUser implements KerberosUser {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractKerberosUser.class);
+
+    static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+
+    /**
+     * Percentage of the ticket window to use before we renew the TGT.
+     */
+    static final float TICKET_RENEW_WINDOW = 0.80f;
+
+    protected final AtomicBoolean loggedIn = new AtomicBoolean(false);
+
+    protected Subject subject;
+    protected LoginContext loginContext;
+
+    public AbstractKerberosUser() {
+    }
+
+    /**
+     * Performs a login using the specified principal and keytab.
+     *
+     * @throws LoginException if the login fails
+     */
+    @Override
+    public synchronized void login() throws LoginException {
+        if (isLoggedIn()) {
+            return;
+        }
+
+        try {
+            // If it's the first time ever calling login then we need to initialize a new context
+            if (loginContext == null) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Initializing new login context...");
+                }
+                if (this.subject == null) {
+                    // only create a new subject if a current one does not exist
+                    // other classes may be referencing an existing subject and replacing it may break functionality of those other classes after relogin
+                    this.subject = new Subject();
+                }
+                this.loginContext = createLoginContext(subject);
+            }
+
+            loginContext.login();
+            loggedIn.set(true);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Successful login for {}", new Object[]{getPrincipal()});
+            }
+        } catch (LoginException le) {
+            LoginException loginException = new LoginException("Unable to login with " + getPrincipal() + " due to: " + le.getMessage());
+            loginException.setStackTrace(le.getStackTrace());
+            throw loginException;
+        }
+    }
+
+    protected abstract LoginContext createLoginContext(final Subject subject) throws LoginException;
+
+    /**
+     * Performs a logout of the current user.
+     *
+     * @throws LoginException if the logout fails
+     */
+    @Override
+    public synchronized void logout() throws LoginException {
+        if (!isLoggedIn()) {
+            return;
+        }
+
+        try {
+            loginContext.logout();
+            loggedIn.set(false);
+            LOG.debug("Successful logout for {}", new Object[]{getPrincipal()});
+
+            loginContext = null;
+        } catch (LoginException e) {
+            throw new LoginException("Logout failed due to: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Executes the PrivilegedAction as this user.
+     *
+     * @param action the action to execute
+     * @param <T> the type of result
+     * @return the result of the action
+     * @throws IllegalStateException if this method is called while not logged in
+     */
+    @Override
+    public <T> T doAs(final PrivilegedAction<T> action) throws IllegalStateException {
+        if (!isLoggedIn()) {
+            throw new IllegalStateException("Must login before executing actions");
+        }
+
+        return Subject.doAs(subject, action);
+    }
+
+    /**
+     * Executes the PrivilegedAction as this user.
+     *
+     * @param action the action to execute
+     * @param <T> the type of result
+     * @return the result of the action
+     * @throws IllegalStateException if this method is called while not logged in
+     * @throws PrivilegedActionException if an exception is thrown from the action
+     */
+    @Override
+    public <T> T doAs(final PrivilegedExceptionAction<T> action)
+            throws IllegalStateException, PrivilegedActionException {
+        if (!isLoggedIn()) {
+            throw new IllegalStateException("Must login before executing actions");
+        }
+
+        return Subject.doAs(subject, action);
+    }
+
+    /**
+     * Re-login a user from keytab if TGT is expired or is close to expiry.
+     *
+     * @throws LoginException if an error happens performing the re-login
+     */
+    @Override
+    public synchronized boolean checkTGTAndRelogin() throws LoginException {
+        final KerberosTicket tgt = getTGT();
+        if (tgt == null) {
+            LOG.debug("TGT was not found");
+        }
+
+        if (tgt != null && System.currentTimeMillis() < getRefreshTime(tgt)) {
+            LOG.debug("TGT was found, but has not reached expiration window");
+            return false;
+        }
+
+        LOG.debug("Performing relogin for {}", new Object[]{getPrincipal()});
+        logout();
+        login();
+        return true;
+    }
+
+    /**
+     * Get the Kerberos TGT.
+     *
+     * @return the user's TGT or null if none was found
+     */
+    private synchronized KerberosTicket getTGT() {
+        final Set<KerberosTicket> tickets = subject.getPrivateCredentials(KerberosTicket.class);
+
+        for (KerberosTicket ticket : tickets) {
+            if (isTGSPrincipal(ticket.getServer())) {
+                return ticket;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * TGS must have the server principal of the form "krbtgt/FOO@FOO".
+     *
+     * @param principal the principal to check
+     * @return true if the principal is the TGS, false otherwise
+     */
+    private boolean isTGSPrincipal(final KerberosPrincipal principal) {
+        if (principal == null) {
+            return false;
+        }
+
+        if (principal.getName().equals("krbtgt/" + principal.getRealm() + "@" + principal.getRealm())) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Found TGT principal: " + principal.getName());
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    private long getRefreshTime(final KerberosTicket tgt) {
+        long start = tgt.getStartTime().getTime();
+        long end = tgt.getEndTime().getTime();
+
+        if (LOG.isTraceEnabled()) {
+            final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+            final String startDate = dateFormat.format(new Date(start));
+            final String endDate = dateFormat.format(new Date(end));
+            LOG.trace("TGT valid starting at: " + startDate);
+            LOG.trace("TGT expires at: " + endDate);
+        }
+
+        return start + (long) ((end - start) * TICKET_RENEW_WINDOW);
+    }
+
+    /**
+     * @return true if this user is currently logged in, false otherwise
+     */
+    @Override
+    public boolean isLoggedIn() {
+        return loggedIn.get();
+    }
+
+    @Override
+    public String toString() {
+        return "KerberosUser{" +
+                "principal='" + getPrincipal() + '\'' +
+                ", loggedIn=" + loggedIn +
+                '}';
+    }
+}
+
diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java
index cc61fb8..2b59ab6 100644
--- a/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java
@@ -19,7 +19,6 @@
 package org.apache.ranger.audit.utils;
 
 import org.apache.commons.collections.MapUtils;
-import org.apache.commons.lang.ArrayUtils;
 import org.apache.hadoop.security.SecurityUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -119,23 +118,26 @@ public final class InMemoryJAASConfiguration extends Configuration {
 
     private static final Logger LOG = LoggerFactory.getLogger(InMemoryJAASConfiguration.class);
 
-    private static final String JAAS_CONFIG_PREFIX_PARAM = "xasecure.audit.jaas.";
-    private static final String JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM = "loginModuleName";
-    private static final String JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM = "loginModuleControlFlag";
-    private static final String JAAS_CONFIG_LOGIN_OPTIONS_PREFIX = "option";
-    private static final String JAAS_PRINCIPAL_PROP = "principal";
+    public static final String JAAS_CONFIG_PREFIX_PARAM                    = "xasecure.audit.jaas.";
+    public static final String JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM         = "loginModuleName";
+    public static final String JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM = "loginModuleControlFlag";
+    public static final String JAAS_CONFIG_LOGIN_OPTIONS_PREFIX            = "option";
+    public static final String JAAS_PRINCIPAL_PROP                         = "principal";
 
-    private Configuration parent = null;
-    private Map<String, List<AppConfigurationEntry>> applicationConfigEntryMap = new HashMap<>();
+    private final Configuration                            parent;
+    private final Map<String, List<AppConfigurationEntry>> applicationConfigEntryMap = new HashMap<>();
 
-    public static void init(String propFile) throws Exception {
+    public static InMemoryJAASConfiguration init(String propFile) throws Exception {
     	LOG.debug("==> InMemoryJAASConfiguration.init( {} ) ", propFile);
 
-        InputStream in = null;
+        InMemoryJAASConfiguration ret = null;
+        InputStream               in  = null;
 
         try {
             Properties properties = new Properties();
+
             in = ClassLoader.getSystemResourceAsStream(propFile);
+
             if (in == null) {
                 if (!propFile.startsWith("/")) {
                     in = ClassLoader.getSystemResourceAsStream("/" + propFile);
@@ -144,8 +146,10 @@ public final class InMemoryJAASConfiguration extends Configuration {
                     in = new FileInputStream(new File(propFile));
                 }
             }
+
             properties.load(in);
-            init(properties);
+
+            ret = init(properties);
         } catch (IOException e) {
             throw new Exception("Failed to load JAAS application properties", e);
         } finally {
@@ -157,89 +161,101 @@ public final class InMemoryJAASConfiguration extends Configuration {
 	            }
 	        }
         }
+
         LOG.debug("<== InMemoryJAASConfiguration.init( {} ) ", propFile);
+
+		return ret;
     }
 
-    public static void init(Properties properties) throws Exception {
+    public static InMemoryJAASConfiguration init(Properties properties) throws Exception {
     	LOG.debug("==> InMemoryJAASConfiguration.init()");
 
+        InMemoryJAASConfiguration ret = null;
+
     	if (properties != null && MapUtils.isNotEmpty(properties)) {
-        	InMemoryJAASConfiguration conf = new InMemoryJAASConfiguration(properties);
-            Configuration.setConfiguration(conf);
+            ret = new InMemoryJAASConfiguration(properties);
         } else {
             throw new Exception("Failed to load JAAS application properties: properties NULL or empty!");
         }
 
         LOG.debug("<== InMemoryJAASConfiguration.init()");
+
+		return ret;
     }
 
     @Override
     public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-        LOG.trace("==> InMemoryJAASConfiguration.getAppConfigurationEntry( {} )", name);
+        LOG.debug("==> InMemoryJAASConfiguration.getAppConfigurationEntry( {} )", name);
 
         AppConfigurationEntry[] ret = null;
-		if (parent != null) {
-        	ret = parent.getAppConfigurationEntry(name);
-		}
-		if (ret == null || ret.length == 0) {
-        	List<AppConfigurationEntry> retList = applicationConfigEntryMap.get(name);
-        	if (retList != null && retList.size() > 0) {
-            	int sz = retList.size();
-            	ret = new AppConfigurationEntry[sz];
-            	ret = retList.toArray(ret);
-			}
+
+        if (parent != null) {
+            ret = parent.getAppConfigurationEntry(name);
+        }
+
+        if (ret == null || ret.length == 0) {
+            List<AppConfigurationEntry> retList = applicationConfigEntryMap.get(name);
+
+            if (retList != null && retList.size() > 0) {
+                ret = retList.toArray(new AppConfigurationEntry[retList.size()]);
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== InMemoryJAASConfiguration.getAppConfigurationEntry( {} ) : {}", name, toString(ret));
         }
-        LOG.trace("<== InMemoryJAASConfiguration.getAppConfigurationEntry( {} ) : {}", name, ArrayUtils.toString(ret));
+
         return ret;
     }
 
     private InMemoryJAASConfiguration(Properties prop) {
         parent = Configuration.getConfiguration();
+
         initialize(prop);
     }
 
     private void initialize(Properties properties) {
     	LOG.debug("==> InMemoryJAASConfiguration.initialize()");
 
-        int prefixLen = JAAS_CONFIG_PREFIX_PARAM.length();
-
+        int                             prefixLen   = JAAS_CONFIG_PREFIX_PARAM.length();
         Map<String, SortedSet<Integer>> jaasClients = new HashMap<>();
+
         for(String key : properties.stringPropertyNames()) {
             if (key.startsWith(JAAS_CONFIG_PREFIX_PARAM)) {
-                String jaasKey  = key.substring(prefixLen);
-                StringTokenizer tokenizer = new StringTokenizer(jaasKey, ".");
-                int tokenCount =tokenizer.countTokens();
+                String          jaasKey    = key.substring(prefixLen);
+                StringTokenizer tokenizer  = new StringTokenizer(jaasKey, ".");
+                int             tokenCount = tokenizer.countTokens();
+
                 if (tokenCount > 0) {
-                    String clientId = tokenizer.nextToken();
+                    String             clientId  = tokenizer.nextToken();
                     SortedSet<Integer> indexList = jaasClients.get(clientId);
+
                     if (indexList == null) {
-                        indexList = new TreeSet<Integer>();
+                        indexList = new TreeSet<>();
+
                         jaasClients.put(clientId, indexList);
                     }
-                    String indexStr = tokenizer.nextToken();
-
-                    int indexId =  isNumeric(indexStr) ? Integer.parseInt(indexStr)  : -1;
 
+                    String  indexStr      = tokenizer.nextToken();
+                    int     indexId       = isNumeric(indexStr) ? Integer.parseInt(indexStr)  : -1;
                     Integer clientIdIndex = Integer.valueOf(indexId);
 
                     if (!indexList.contains(clientIdIndex)) {
                         indexList.add(clientIdIndex);
                     }
-
                 }
             }
         }
-        for(String jaasClient : jaasClients.keySet()) {
 
+        for(String jaasClient : jaasClients.keySet()) {
             for(Integer index :  jaasClients.get(jaasClient)) {
-
                 String keyPrefix = JAAS_CONFIG_PREFIX_PARAM + jaasClient + ".";
 
                 if (index > -1) {
                     keyPrefix = keyPrefix  + String.valueOf(index) + ".";
                 }
 
-                String keyParam = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM;
+                String keyParam        = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM;
                 String loginModuleName = properties.getProperty(keyParam);
 
                 if (loginModuleName == null) {
@@ -252,11 +268,14 @@ public final class InMemoryJAASConfiguration extends Configuration {
                 }
 
                 keyParam = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM;
-                String  controlFlag =    properties.getProperty(keyParam);
+
+                String controlFlag = properties.getProperty(keyParam);
 
                 AppConfigurationEntry.LoginModuleControlFlag loginControlFlag = null;
+
                 if (controlFlag != null) {
                     controlFlag = controlFlag.trim().toLowerCase();
+
                     if (controlFlag.equals("optional")) {
                         loginControlFlag = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
                     } else if (controlFlag.equals("requisite")) {
@@ -278,14 +297,15 @@ public final class InMemoryJAASConfiguration extends Configuration {
                     loginControlFlag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
                 }
 
+                Map<String, String> options         = new HashMap<>();
+                String              optionPrefix    = keyPrefix + JAAS_CONFIG_LOGIN_OPTIONS_PREFIX + ".";
+                int                 optionPrefixLen = optionPrefix.length();
 
-                Map<String, String> options = new HashMap<>();
-                String optionPrefix =  keyPrefix + JAAS_CONFIG_LOGIN_OPTIONS_PREFIX + ".";
-                int optionPrefixLen = optionPrefix.length();
                 for(String key : properties.stringPropertyNames()) {
                     if (key.startsWith(optionPrefix)) {
                         String optionKey = key.substring(optionPrefixLen);
                         String optionVal = properties.getProperty(key);
+
                         if (optionVal != null) {
                             optionVal = optionVal.trim();
 
@@ -298,6 +318,7 @@ public final class InMemoryJAASConfiguration extends Configuration {
                                         + optionVal + "]");
                             }
                         }
+
                         options.put(optionKey, optionVal);
                     }
                 }
@@ -306,30 +327,53 @@ public final class InMemoryJAASConfiguration extends Configuration {
 
                 if (LOG.isDebugEnabled()) {
                     StringBuilder sb = new StringBuilder();
+
                     sb.append("Adding client: [").append(jaasClient).append("{").append(index).append("}]\n");
                     sb.append("\tloginModule: [").append(loginModuleName).append("]\n");
                     sb.append("\tcontrolFlag: [").append(loginControlFlag).append("]\n");
+
                     for (String key : options.keySet()) {
                         String val = options.get(key);
+
                         sb.append("\tOptions:  [").append(key).append("] => [").append(val).append("]\n");
                     }
+
                     LOG.debug(sb.toString());
                 }
 
                 List<AppConfigurationEntry> retList =  applicationConfigEntryMap.get(jaasClient);
+
                 if (retList == null) {
-                    retList = new ArrayList<AppConfigurationEntry>();
+                    retList = new ArrayList<>();
+
                     applicationConfigEntryMap.put(jaasClient, retList);
                 }
-                retList.add(entry);
-
 
+                retList.add(entry);
             }
         }
+
         LOG.debug("<== InMemoryJAASConfiguration.initialize()");
     }
 
     private static boolean isNumeric(String str) {
         return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
     }
+
+    private String toString(AppConfigurationEntry[] entries) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append('[');
+        if (entries != null) {
+            for (AppConfigurationEntry entry : entries) {
+                sb.append("{ loginModuleName=").append(entry.getLoginModuleName())
+                        .append(", controlFlag=").append(entry.getControlFlag())
+                        .append(", options=").append(entry.getOptions())
+                        .append("}");
+            }
+        }
+        sb.append(']');
+
+        return sb.toString();
+    }
 }
diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
new file mode 100644
index 0000000..18861b1
--- /dev/null
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.audit.utils;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.commons.logging.Log;
+
+import javax.security.auth.login.LoginException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Helper class for processors to perform an action as a KerberosUser.
+ */
+public class KerberosAction<T> {
+
+    private final KerberosUser kerberosUser;
+    private final PrivilegedExceptionAction<T> action;
+    private final Log logger;
+
+    public KerberosAction(final KerberosUser kerberosUser,
+                          final PrivilegedExceptionAction<T> action,
+                          final Log logger) {
+        this.kerberosUser = kerberosUser;
+        this.action = action;
+        this.logger = logger;
+        Validate.notNull(this.kerberosUser);
+        Validate.notNull(this.action);
+        Validate.notNull(this.logger);
+    }
+
+    public T execute() throws Exception {
+        T result;
+        // lazily login the first time the processor executes
+        if (!kerberosUser.isLoggedIn()) {
+            try {
+                kerberosUser.login();
+                logger.info("Successful login for " + kerberosUser.getPrincipal());
+            } catch (LoginException e) {
+                throw new Exception("Login failed due to: " + e.getMessage(), e);
+            }
+        }
+
+        // check if we need to re-login, will only happen if re-login window is reached (80% of TGT life)
+        try {
+            kerberosUser.checkTGTAndRelogin();
+        } catch (LoginException e) {
+            throw new Exception("Relogin check failed due to: " + e.getMessage(), e);
+        }
+
+        // attempt to execute the action, if an exception is caught attempt to logout/login and retry
+        try {
+            result = kerberosUser.doAs(action);
+        } catch (SecurityException se) {
+            logger.info("Privileged action failed, attempting relogin and retrying...");
+            logger.debug("", se);
+
+            try {
+                kerberosUser.logout();
+                kerberosUser.login();
+                result = kerberosUser.doAs(action);
+            } catch (Exception e) {
+                throw new Exception("Retrying privileged action failed due to: " + e.getMessage(), e);
+            }
+        } catch (PrivilegedActionException pae) {
+            final Exception cause = pae.getException();
+            throw new Exception("Privileged action failed due to: " + cause.getMessage(), cause);
+        }
+
+        return result;
+    }
+}
diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
new file mode 100644
index 0000000..2667721
--- /dev/null
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.audit.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+/**
+ * Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used.
+ *
+ * */
+public class KerberosJAASConfigUser extends AbstractKerberosUser {
+    private static final Logger LOG = LoggerFactory.getLogger(KerberosJAASConfigUser.class);
+
+    private final String        configName;
+    private final Configuration config;
+
+    public KerberosJAASConfigUser(final String configName, final Configuration config) {
+        this.configName = configName;
+        this.config     = config;
+    }
+
+
+    @Override
+    public String getPrincipal() {
+        String                  ret     = null;
+        AppConfigurationEntry[] entries = config.getAppConfigurationEntry(configName);
+
+        if (entries != null) {
+           for (AppConfigurationEntry entry : entries) {
+               if (entry.getOptions().containsKey(InMemoryJAASConfiguration.JAAS_PRINCIPAL_PROP)) {
+                   ret = (String) entry.getOptions().get(InMemoryJAASConfiguration.JAAS_PRINCIPAL_PROP);
+
+                   break;
+               }
+           }
+        }
+
+        return ret;
+    }
+
+    @Override
+    protected LoginContext createLoginContext(Subject subject) throws LoginException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> KerberosJAASConfigUser.createLoginContext()");
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== KerberosJAASConfigUser.createLoginContext(), Subject: " + subject);
+        }
+
+        return new LoginContext(configName, subject, null, config);
+    }
+}
+
diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java
new file mode 100644
index 0000000..fb6003e
--- /dev/null
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.audit.utils;
+
+import javax.security.auth.login.LoginException;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * A keytab-based user that can login/logout and perform actions as the given user.
+ */
+public interface KerberosUser {
+
+    /**
+     * Performs a login for the given user.
+     *
+     * @throws LoginException if the login fails
+     */
+    void login() throws LoginException;
+
+    /**
+     * Performs a logout for the given user.
+     *
+     * @throws LoginException if the logout fails
+     */
+    void logout() throws LoginException;
+
+    /**
+     * Executes the given action as the given user.
+     *
+     * @param action the action to execute
+     * @param <T> the type of response
+     * @return the result of the action
+     * @throws IllegalStateException if attempting to execute an action before performing a login
+     */
+    <T> T doAs(PrivilegedAction<T> action) throws IllegalStateException;
+
+    /**
+     * Executes the given action as the given user.
+     *
+     * @param action the action to execute
+     * @param <T> the type of response
+     * @return the result of the action
+     * @throws IllegalStateException if attempting to execute an action before performing a login
+     * @throws PrivilegedActionException if the action itself threw an exception
+     */
+    <T> T doAs(PrivilegedExceptionAction<T> action)
+            throws IllegalStateException, PrivilegedActionException;
+
+    /**
+     * Performs a re-login if the TGT is close to expiration.
+     *
+     * @return true if a relogin was performed, false otherwise
+     * @throws LoginException if the relogin fails
+     */
+    boolean checkTGTAndRelogin() throws LoginException;
+
+    /**
+     * @return true if this user is currently logged in, false otherwise
+     */
+    boolean isLoggedIn();
+
+    /**
+     * @return the principal for this user
+     */
+    String getPrincipal();
+
+}
+
diff --git a/security-admin/src/main/java/org/apache/ranger/patch/cliutil/DbToSolrMigrationUtil.java b/security-admin/src/main/java/org/apache/ranger/patch/cliutil/DbToSolrMigrationUtil.java
index c20bcfa..30e0bd6 100644
--- a/security-admin/src/main/java/org/apache/ranger/patch/cliutil/DbToSolrMigrationUtil.java
+++ b/security-admin/src/main/java/org/apache/ranger/patch/cliutil/DbToSolrMigrationUtil.java
@@ -58,6 +58,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 
+import javax.security.auth.login.Configuration;
+
 @Component
 public class DbToSolrMigrationUtil extends BaseLoader {
 	private static final Logger logger = Logger.getLogger(DbToSolrMigrationUtil.class);
@@ -464,10 +466,15 @@ public class DbToSolrMigrationUtil extends BaseLoader {
 				 System.setProperty(PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG, "/dev/null");
 			 }
 			 logger.info("Loading SolrClient JAAS config from Ranger audit config if present...");
-			 InMemoryJAASConfiguration.init(props);
-			} catch (Exception e) {
-				logger.error("ERROR: Unable to load SolrClient JAAS config from ranger admin config file. Audit migration to Secure Solr will fail...",e);
-			}
+
+			 Configuration conf = InMemoryJAASConfiguration.init(props);
+
+			 if (conf != null) {
+				 Configuration.setConfiguration(conf);
+			 }
+		} catch (Exception e) {
+			logger.error("ERROR: Unable to load SolrClient JAAS config from ranger admin config file. Audit migration to Secure Solr will fail...",e);
+		}
 		logger.info("<==createSolrClient.registerSolrClientJAAS()" );
 	}
 }
diff --git a/security-admin/src/main/java/org/apache/ranger/solr/SolrMgr.java b/security-admin/src/main/java/org/apache/ranger/solr/SolrMgr.java
index f4eab64..2031317 100644
--- a/security-admin/src/main/java/org/apache/ranger/solr/SolrMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/solr/SolrMgr.java
@@ -35,6 +35,8 @@ import org.apache.solr.client.solrj.impl.SolrHttpClientBuilder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import javax.security.auth.login.Configuration;
+
 /**
  * This class initializes Solr
  *
@@ -171,10 +173,14 @@ public class SolrMgr {
 				 System.setProperty(PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG, "/dev/null");
 			 }
 			 logger.info("Loading SolrClient JAAS config from Ranger audit config if present...");
-			 InMemoryJAASConfiguration.init(props);
-			} catch (Exception e) {
-				logger.error("ERROR: Unable to load SolrClient JAAS config from ranger admin config file. Audit to Kerberized Solr will fail...", e);
-			}
+			 Configuration conf = InMemoryJAASConfiguration.init(props);
+
+			 if (conf != null) {
+				 Configuration.setConfiguration(conf);
+			 }
+		} catch (Exception e) {
+			logger.error("ERROR: Unable to load SolrClient JAAS config from ranger admin config file. Audit to Kerberized Solr will fail...", e);
+		}
 		logger.info("<==SolrMgr.init()" );
 	}