RANGER-552 Ranger KMS not able to audit to kerberos HDFS
Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/d283d6cc Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/d283d6cc Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/d283d6cc Branch: refs/heads/tag-policy Commit: d283d6cca8917bc7f7ed6b1729a6d8fe531601c3 Parents: d85beb4 Author: Don Bosco Durai <[email protected]> Authored: Fri Jun 12 16:40:45 2015 -0700 Committer: Don Bosco Durai <[email protected]> Committed: Sat Jun 13 13:00:01 2015 -0700 ---------------------------------------------------------------------- .../audit/destination/HDFSAuditDestination.java | 2 +- .../apache/ranger/audit/provider/MiscUtil.java | 183 +++++++++++++++++-- .../kafka/authorizer/RangerKafkaAuthorizer.java | 41 +---- .../kms/authorizer/RangerKmsAuthorizer.java | 44 ++++- 4 files changed, 225 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d283d6cc/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java ---------------------------------------------------------------------- diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java index 4fc3a0b..49e5fbb 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java @@ -216,7 +216,7 @@ public class HDFSAuditDestination extends AuditDestination { FileSystem fileSystem = FileSystem.get(uri, conf); Path hdfPath = new Path(fullPath); - logger.info("Checking whether log file exists. hdfPath=" + fullPath); + logger.info("Checking whether log file exists. hdfPath=" + fullPath + ", UGI=" + MiscUtil.getUGILoginUser()); int i = 0; while (fileSystem.exists(hdfPath)) { i++; http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d283d6cc/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java ---------------------------------------------------------------------- diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java index 6eee55c..bfded93 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.rmi.dgc.VMID; +import java.security.Principal; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; @@ -30,18 +31,27 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.UUID; +import java.util.regex.Pattern; 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; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authentication.util.KerberosName; +import org.apache.hadoop.security.authentication.util.KerberosUtil; import org.apache.log4j.helpers.LogLog; import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import static org.apache.hadoop.util.PlatformName.IBM_JAVA; + public class MiscUtil { private static final Log logger = LogFactory.getLog(MiscUtil.class); @@ -64,7 +74,7 @@ public class MiscUtil { private static UserGroupInformation ugiLoginUser = null; private static Subject subjectLoginUser = null; - private static Map<String, LogHistory> logHistoryList = new Hashtable<String,LogHistory>(); + private static Map<String, LogHistory> logHistoryList = new Hashtable<String, LogHistory>(); private static int logInterval = 30000; // 30 seconds static { @@ -410,19 +420,51 @@ public class MiscUtil { return ret; } + public static UserGroupInformation createUGIFromSubject(Subject subject) + throws IOException { + logger.info("SUBJECT " + (subject == null ? "not found" : "found")); + UserGroupInformation ugi = null; + if (subject != null) { + logger.info("SUBJECT.PRINCIPALS.size()=" + + subject.getPrincipals().size()); + java.util.Set<Principal> principals = subject.getPrincipals(); + for (Principal principal : principals) { + logger.info("SUBJECT.PRINCIPAL.NAME=" + principal.getName()); + } + try { + // Do not remove the below statement. The default + // getLoginUser does some initialization which is needed + // for getUGIFromSubject() to work. + logger.info("Default UGI before using Subject from Kafka:" + + UserGroupInformation.getLoginUser()); + } catch (Throwable t) { + logger.error(t); + } + ugi = UserGroupInformation.getUGIFromSubject(subject); + logger.info("SUBJECT.UGI.NAME=" + ugi.getUserName() + ", ugi=" + + ugi); + } else { + logger.info("Server username is not available"); + } + return ugi; + } + /** * @param ugiLoginUser */ - public static void setUGILoginUser(UserGroupInformation newUGI, Subject newSubject) { + public static void setUGILoginUser(UserGroupInformation newUGI, + Subject newSubject) { if (newUGI != null) { UserGroupInformation.setLoginUser(newUGI); ugiLoginUser = newUGI; - logger.info("Setting UGI=" + newUGI ); + logger.info("Setting UGI=" + newUGI); } else { logger.error("UGI is null. Not setting it."); } - logger.info("Setting SUBJECT"); - subjectLoginUser = newSubject; + if (newSubject != null) { + logger.info("Setting SUBJECT"); + subjectLoginUser = newSubject; + } } public static UserGroupInformation getUGILoginUser() { @@ -436,7 +478,6 @@ public class MiscUtil { return ugiLoginUser; } - public static Subject getSubjectLoginUser() { return subjectLoginUser; } @@ -462,13 +503,14 @@ public class MiscUtil { return groupsSet; } } catch (Throwable e) { - logErrorMessageByInterval( - logger, "Error getting groups for users. userName=" + userName, e); + logErrorMessageByInterval(logger, + "Error getting groups for users. userName=" + userName, e); } return null; } - static public boolean logErrorMessageByInterval(Log useLogger, String message) { + static public boolean logErrorMessageByInterval(Log useLogger, + String message) { return logErrorMessageByInterval(useLogger, message, null); } @@ -476,7 +518,8 @@ public class MiscUtil { * @param string * @param e */ - static public boolean logErrorMessageByInterval(Log useLogger, String message, Throwable e) { + static public boolean logErrorMessageByInterval(Log useLogger, + String message, Throwable e) { LogHistory log = logHistoryList.get(message); if (log == null) { log = new LogHistory(); @@ -494,7 +537,7 @@ public class MiscUtil { } else { useLogger.error(message, e); } - + return true; } else { log.counter++; @@ -503,9 +546,127 @@ public class MiscUtil { } + public static void authWithKerberos(String keytab, String principal, + String nameRules) { + + if (keytab == null) { + return; + } + Subject serverSubject = new Subject(); + int successLoginCount = 0; + String[] spnegoPrincipals = null; + try { + if (principal.equals("*")) { + spnegoPrincipals = KerberosUtil.getPrincipalNames(keytab, + Pattern.compile("HTTP/.*")); + if (spnegoPrincipals.length == 0) { + logger.error("No principals found in keytab=" + keytab); + } + } else { + spnegoPrincipals = new String[] { principal }; + } + + if (nameRules != null) { + KerberosName.setRules(nameRules); + } + + List<LoginContext> loginContexts = new ArrayList<LoginContext>(); + for (String spnegoPrincipal : spnegoPrincipals) { + try { + logger.info("Login using keytab " + keytab + + ", for principal " + spnegoPrincipal); + final KerberosConfiguration kerberosConfiguration = new KerberosConfiguration( + keytab, spnegoPrincipal); + final LoginContext loginContext = new LoginContext("", + serverSubject, null, kerberosConfiguration); + loginContext.login(); + successLoginCount++; + logger.info("Login success keytab " + keytab + + ", for principal " + spnegoPrincipal); + loginContexts.add(loginContext); + } catch (Throwable t) { + logger.error("Login failed keytab " + keytab + + ", for principal " + spnegoPrincipal, t); + } + } + } catch (Throwable t) { + logger.error("Failed to login as [" + spnegoPrincipals + "]", t); + } + + if (successLoginCount > 0) { + logger.info("Total login success count=" + successLoginCount); + try { + UserGroupInformation ugi = createUGIFromSubject(serverSubject); + if (ugi != null) { + setUGILoginUser(ugi, serverSubject); + } + } catch (Throwable e) { + logger.error("Error creating UGI from subject. subject=" + + serverSubject); + } + } else { + logger.error("Total logins were successfull from keytab=" + keytab + + ", principal=" + principal); + } + } + static class LogHistory { long lastLogTime = 0; int counter = 0; } + /** + * Kerberos context configuration for the JDK GSS library. + */ + private static class KerberosConfiguration extends Configuration { + private String keytab; + private String principal; + + public KerberosConfiguration(String keytab, String principal) { + this.keytab = keytab; + this.principal = principal; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map<String, String> options = new HashMap<String, String>(); + if (IBM_JAVA) { + options.put("useKeytab", keytab.startsWith("file://") ? keytab + : "file://" + keytab); + options.put("principal", principal); + options.put("credsType", "acceptor"); + } else { + options.put("keyTab", keytab); + options.put("principal", principal); + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("isInitiator", "false"); + } + options.put("refreshKrb5Config", "true"); + String ticketCache = System.getenv("KRB5CCNAME"); + if (ticketCache != null) { + if (IBM_JAVA) { + options.put("useDefaultCcache", "true"); + // The first value searched when "useDefaultCcache" is used. + System.setProperty("KRB5CCNAME", ticketCache); + options.put("renewTGT", "true"); + options.put("credsType", "both"); + } else { + options.put("ticketCache", ticketCache); + } + } + if (logger.isDebugEnabled()) { + options.put("debug", "true"); + } + + return new AppConfigurationEntry[] { new AppConfigurationEntry( + KerberosUtil.getKrb5LoginModuleName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options), }; + } + } + } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d283d6cc/plugin-kafka/src/main/java/org/apache/ranger/authorization/kafka/authorizer/RangerKafkaAuthorizer.java ---------------------------------------------------------------------- diff --git a/plugin-kafka/src/main/java/org/apache/ranger/authorization/kafka/authorizer/RangerKafkaAuthorizer.java b/plugin-kafka/src/main/java/org/apache/ranger/authorization/kafka/authorizer/RangerKafkaAuthorizer.java index 3341f84..dbb2723 100644 --- a/plugin-kafka/src/main/java/org/apache/ranger/authorization/kafka/authorizer/RangerKafkaAuthorizer.java +++ b/plugin-kafka/src/main/java/org/apache/ranger/authorization/kafka/authorizer/RangerKafkaAuthorizer.java @@ -19,6 +19,7 @@ package org.apache.ranger.authorization.kafka.authorizer; +import java.io.IOException; import java.security.Principal; import java.util.Date; @@ -81,43 +82,19 @@ public class RangerKafkaAuthorizer implements Authorizer { public void initialize(KafkaConfig kafkaConfig) { if (rangerPlugin == null) { - rangerPlugin = new RangerBasePlugin("kafka", "kafka"); - try { Subject subject = LoginManager.subject(); - logger.info("SUBJECT " - + (subject == null ? "not found" : "found")); - if (subject != null) { - logger.info("SUBJECT.PRINCIPALS.size()=" - + subject.getPrincipals().size()); - java.util.Set<Principal> principals = subject - .getPrincipals(); - for (Principal principal : principals) { - logger.info("SUBJECT.PRINCIPAL.NAME=" - + principal.getName()); - } - try { - // Do not remove the below statement. The default - // getLoginUser does some initialization which is needed - // for getUGIFromSubject() to work. - logger.info("Default UGI before using Subject from Kafka:" - + UserGroupInformation.getLoginUser()); - } catch (Throwable t) { - logger.error(t); - } - UserGroupInformation ugi = UserGroupInformation - .getUGIFromSubject(subject); - logger.info("SUBJECT.UGI.NAME=" + ugi.getUserName() - + ", ugi=" + ugi); + UserGroupInformation ugi = MiscUtil + .createUGIFromSubject(subject); + if (ugi != null) { MiscUtil.setUGILoginUser(ugi, subject); - } else { - logger.info("Server username is not available"); } logger.info("LoginUser=" + MiscUtil.getUGILoginUser()); } catch (Throwable t) { logger.error("Error getting principal.", t); } + rangerPlugin = new RangerBasePlugin("kafka", "kafka"); logger.info("Calling plugin.init()"); rangerPlugin.init(); @@ -135,12 +112,12 @@ public class RangerKafkaAuthorizer implements Authorizer { "Authorizer is still not initialized"); return false; } - - //TODO: If resource type if consumer group, then allow it by default - if(resource.resourceType().equals(ResourceType.CONSUMER_GROUP)) { + + // TODO: If resource type if consumer group, then allow it by default + if (resource.resourceType().equals(ResourceType.CONSUMER_GROUP)) { return true; } - + String userName = null; if (session.principal() != null) { userName = session.principal().getName(); http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/d283d6cc/plugin-kms/src/main/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizer.java ---------------------------------------------------------------------- diff --git a/plugin-kms/src/main/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizer.java b/plugin-kms/src/main/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizer.java index 3407a1d..04b8b91 100755 --- a/plugin-kms/src/main/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizer.java +++ b/plugin-kms/src/main/java/org/apache/ranger/authorization/kms/authorizer/RangerKmsAuthorizer.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.key.kms.server.KMSACLsType; @@ -40,6 +41,7 @@ import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.ranger.audit.provider.MiscUtil; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler; import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; @@ -81,15 +83,52 @@ public class RangerKmsAuthorizer implements Runnable, KeyACLs { private static volatile RangerKMSPlugin kmsPlugin = null; + /** + * Constant that identifies the authentication mechanism. + */ + public static final String TYPE = "kerberos"; + + /** + * Constant for the configuration property that indicates the kerberos principal. + */ + public static final String PRINCIPAL = TYPE + ".principal"; + + /** + * Constant for the configuration property that indicates the keytab file path. + */ + public static final String KEYTAB = TYPE + ".keytab"; + + /** + * Constant for the configuration property that indicates the Kerberos name + * rules for the Kerberos principals. + */ + public static final String NAME_RULES = TYPE + ".name.rules"; + RangerKmsAuthorizer(Configuration conf) { + LOG.info("RangerKmsAuthorizer(conf)..."); + authWithKerberos(); if (conf == null) { conf = loadACLs(); } setKMSACLs(conf); init(conf); + } - public RangerKmsAuthorizer() { + /** + * + */ + private void authWithKerberos() { + //Let's if we can create the login user UGI + Configuration kconf = new Configuration(); + kconf.addResource("kms-site.xml"); + String keytab = kconf.get("hadoop.kms.authentication.kerberos.keytab"); + String principal = kconf.get("hadoop.kms.authentication.kerberos.principal"); + String nameRules = kconf.get(NAME_RULES); + MiscUtil.authWithKerberos(keytab, principal, nameRules); + } + + public RangerKmsAuthorizer() { this(null); } @@ -241,6 +280,7 @@ public class RangerKmsAuthorizer implements Runnable, KeyACLs { plugin.init(); kmsPlugin = plugin; + } } } @@ -306,6 +346,8 @@ public class RangerKmsAuthorizer implements Runnable, KeyACLs { } } + + class RangerKMSPlugin extends RangerBasePlugin { public RangerKMSPlugin() { super("kms", "kms");
