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:13:38 UTC
[ranger] branch master 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 master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new f13d208 RANGER-3134:Remove global JAAS Configuration for Ranger auditing to SOLR
f13d208 is described below
commit f13d2081dddba0eb76b999f84ef1006c3f1ef04b
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()" );
}