فهرست منبع

HUE-712: Support refreshing kerberos ticket for beeswax runing in standalone mode with authentication enabled

Prasad Mujumdar 13 سال پیش
والد
کامیت
999e05c
1فایلهای تغییر یافته به همراه93 افزوده شده و 27 حذف شده
  1. 93 27
      apps/beeswax/java/src/main/java/com/cloudera/beeswax/Server.java

+ 93 - 27
apps/beeswax/java/src/main/java/com/cloudera/beeswax/Server.java

@@ -17,6 +17,7 @@ package com.cloudera.beeswax;
 
 import java.io.IOException;
 import java.net.Socket;
+import java.security.PrivilegedAction;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.MissingOptionException;
@@ -46,6 +47,7 @@ import org.apache.thrift.server.TThreadPoolServer;
 import org.apache.thrift.transport.TSaslServerTransport;
 import org.apache.thrift.transport.TServerSocket;
 import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
 import org.apache.thrift.transport.TTransportFactory;
 
@@ -57,6 +59,8 @@ public class Server {
 
   private static final Logger LOG = Logger.getLogger(Server.class.getName());
   private static final int USHRT_MAX = 65535;
+  // Refresh ticket every 8 hours
+  private static final int DEFAULT_KRB_REFRESH_INTERVAL = (8 * 60 * 60 * 1000);
 
   private static int mport = -1;
   private static int bport = -1;
@@ -67,9 +71,31 @@ public class Server {
   private static int dtPort = -1;
   private static boolean dtHttps = false;
   private static long qlifetime = BeeswaxServiceImpl.RUNNING_QUERY_LIFETIME;
-  private static boolean useSasl = false;
+  private static boolean useKerberos = false;
   private static String principalConf;
   private static String keytabFile;
+  private static Integer refreshInterval = DEFAULT_KRB_REFRESH_INTERVAL;
+  private static String kerberosName;
+  private static UserGroupInformation bwUgi;
+
+  public static class KbrSaslTransportFactory extends TTransportFactory {
+    UserGroupInformation ugi;
+    TSaslServerTransport.Factory saslFactory;
+
+    public KbrSaslTransportFactory(TSaslServerTransport.Factory saslFactory, UserGroupInformation ugi) {
+      this.saslFactory = saslFactory;
+      this.ugi = ugi;
+    }
+
+    @Override
+    public TTransport getTransport(final TTransport base) {
+      return ugi.doAs(new PrivilegedAction<TTransport>() {
+        public TTransport run() {
+          return saslFactory.getTransport(base);
+        }
+      });
+    }
+  }
 
   /**
    * Parse command line options.
@@ -120,6 +146,9 @@ public class Server {
     Option kTabOpt = new Option("k", "keytab", true, "keytab file");
     options.addOption(kTabOpt);
 
+    Option kRefreshOpt = new Option("r", "refresh", true, "kerberos ticket refresh interval in minutes");
+    options.addOption(kRefreshOpt);
+
     options.addOptionGroup(dtOptions); // make "n" and "p" mutually exclusive
     PosixParser parser = new PosixParser();
     CommandLine cmd = parser.parse(options, args);
@@ -145,10 +174,13 @@ public class Server {
         qlifetime = Long.valueOf(opt.getValue());
       } else if (opt.getOpt() == "k") {
         keytabFile = opt.getValue();
-        useSasl = true;
+        useKerberos = true;
       } else if (opt.getOpt() == "c") {
         principalConf = opt.getValue();
+      } else if (opt.getOpt() == "r") {
+        refreshInterval = Integer.valueOf(opt.getValue()) * 1000 * 60; // minutes
       }
+
     }
   }
 
@@ -162,6 +194,9 @@ public class Server {
   public static void main(String[] args)
         throws TTransportException, MetaException, ParseException {
     parseArgs(args);
+    if (useKerberos) {
+      doKerberosAuth();
+    }
     createDirectoriesAsNecessary();
 
     // Start metastore if specified
@@ -227,33 +262,19 @@ public class Server {
     Processor processor = new BeeswaxService.Processor(impl);
     TTransportFactory transFactory;
 
-    if (useSasl) {
-      if (keytabFile == null || keytabFile.isEmpty()) {
-        throw new IllegalArgumentException("No keytab specified");
-      }
-      if (principalConf == null || principalConf.isEmpty()) {
-        throw new IllegalArgumentException("No principal specified");
-      }
-
-      // Login from the keytab
-      String kerberosName;
-      try {
-        kerberosName =
-          SecurityUtil.getServerPrincipal(principalConf, "0.0.0.0");
-        UserGroupInformation.loginUserFromKeytab(
-            kerberosName, keytabFile);
-        kerberosName = UserGroupInformation.getCurrentUser().getUserName();
-      } catch (IOException e) {
-        throw new IllegalArgumentException("couldn't setup authentication subsystem", e);
-      }
+    if (useKerberos) {
       final String names[] = SaslRpcServer.splitKerberosName(kerberosName);
-      if (names.length != 3) {
-        throw new IllegalArgumentException("Kerberos principal should have 3 parts: " + kerberosName);
+      if (names.length < 2) {
+        throw new IllegalArgumentException(
+            "Kerberos principal should have at least 2 parts: " + kerberosName);
       }
-      transFactory = new TSaslServerTransport.Factory(AuthMethod.KERBEROS.getMechanismName(),
-          names[0], names[1],  // two parts of kerberos principal
-          SaslRpcServer.SASL_PROPS,
-          new SaslRpcServer.SaslGssCallbackHandler());
+
+      TSaslServerTransport.Factory saslFactory =
+          new TSaslServerTransport.Factory(AuthMethod.KERBEROS.getMechanismName(),
+              names[0], names[1] , // two parts of kerberos principal
+              SaslRpcServer.SASL_PROPS,
+              new SaslRpcServer.SaslGssCallbackHandler());
+      transFactory = new KbrSaslTransportFactory(saslFactory, bwUgi);
     } else {
       transFactory = new TTransportFactory();
     }
@@ -273,6 +294,51 @@ public class Server {
     server.serve();
   }
 
+  /**
+   * Authenticate using kerberos if configured
+   */
+  private static void doKerberosAuth() throws IllegalArgumentException {
+    if (keytabFile == null || keytabFile.isEmpty()) {
+      throw new IllegalArgumentException("No keytab specified");
+    }
+    if (principalConf == null || principalConf.isEmpty()) {
+      throw new IllegalArgumentException("No principal specified");
+    }
+
+    // Login from the keytab
+    try {
+      kerberosName = SecurityUtil.getServerPrincipal(principalConf, "0.0.0.0");
+      UserGroupInformation.loginUserFromKeytab(kerberosName, keytabFile);
+      LOG.info("Logged in using Kerberos ticket for '" + kerberosName +
+               "' from " + keytabFile);
+      bwUgi = UserGroupInformation.getCurrentUser();
+      // Start a thread to periodically refresh kerberos ticket
+      Thread t = new Thread(new Runnable() {
+        @Override
+        public void run() {
+          while (true) {
+            try {
+              Thread.sleep(refreshInterval);
+            } catch (InterruptedException e) {
+              return;
+            }
+            try {
+              LOG.info("Refreshed Kerberos ticket for '" + kerberosName +
+                       "' from " + keytabFile);
+              UserGroupInformation.getLoginUser().reloginFromKeytab();
+            } catch (IOException eIO) {
+              LOG.error("Error refreshing Kerberos ticket", eIO);
+            }
+          }
+        }
+      }, "KerberosRefresher");
+      t.start();
+    } catch (IOException e) {
+      throw new IllegalArgumentException(
+          "Couldn't setup Kerberos authentication", e);
+    }
+  }
+
   /**
    * Start the thrift metastore server.
    *