drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sudhe...@apache.org
Subject [20/29] drill git commit: DRILL-4280: CORE (service login)
Date Sat, 25 Feb 2017 07:18:13 GMT
DRILL-4280: CORE (service login)

+ Support Drillbit login to KDC using Hadoop's
  UserGroupInformation library

+ Set hostname in BootstrapContext
+ Use process user's short name in ImpersonationUtil
+ Add KerberosUtil class


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/d732aad2
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/d732aad2
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/d732aad2

Branch: refs/heads/master
Commit: d732aad2dc59d8db12f6dc64b3a4e60b470d00f6
Parents: 0501a67
Author: Sudheesh Katkam <sudheesh@apache.org>
Authored: Wed Jan 25 18:51:32 2017 -0800
Committer: Sudheesh Katkam <sudheesh@apache.org>
Committed: Fri Feb 24 19:01:42 2017 -0800

----------------------------------------------------------------------
 .../org/apache/drill/common/KerberosUtil.java   | 93 +++++++++++++++++++
 .../drill/exec/server/BootStrapContext.java     | 98 +++++++++++++++++++-
 .../org/apache/drill/exec/server/Drillbit.java  |  5 +-
 .../drill/exec/service/ServiceEngine.java       | 54 ++++-------
 .../drill/exec/util/ImpersonationUtil.java      |  4 +-
 5 files changed, 210 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/d732aad2/common/src/main/java/org/apache/drill/common/KerberosUtil.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/drill/common/KerberosUtil.java b/common/src/main/java/org/apache/drill/common/KerberosUtil.java
new file mode 100644
index 0000000..6b8301c
--- /dev/null
+++ b/common/src/main/java/org/apache/drill/common/KerberosUtil.java
@@ -0,0 +1,93 @@
+/*
+ * 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.drill.common;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+public final class KerberosUtil {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(KerberosUtil.class);
+
+  // Per this link http://docs.oracle.com/javase/jndi/tutorial/ldap/security/gssapi.html
+  // "... GSS-API SASL mechanism was retrofitted to mean only Kerberos v5 ..."
+  public static final String KERBEROS_SASL_NAME = "GSSAPI";
+
+  public static final String KERBEROS_SIMPLE_NAME = "KERBEROS";
+
+  public static final String HOSTNAME_PATTERN = "_HOST";
+
+  /**
+   * Returns principal of format primary/instance@REALM.
+   *
+   * @param primary non-null primary component
+   * @param instance non-null instance component
+   * @param realm non-null realm component
+   * @return principal of format primary/instance@REALM
+   */
+  public static String getPrincipalFromParts(final String primary, final String instance,
final String realm) {
+    return checkNotNull(primary) + "/" +
+        checkNotNull(instance) + "@" +
+        checkNotNull(realm);
+  }
+
+  /**
+   * Expects principal of the format primary/instance@REALM.
+   *
+   * @param principal principal
+   * @return components
+   */
+  public static String[] splitPrincipalIntoParts(final String principal) {
+    final String[] components = principal.split("[/@]");
+    checkState(components.length == 3);
+    checkNotNull(components[0]);
+    checkNotNull(components[1]);
+    checkNotNull(components[2]);
+    return components;
+  }
+
+  public static String canonicalizeInstanceName(String instanceName, final String canonicalName)
{
+    if (instanceName == null || HOSTNAME_PATTERN.equalsIgnoreCase(instanceName)) {
+      instanceName = canonicalName;
+    }
+
+    final String lowercaseName = instanceName.toLowerCase();
+    if (!instanceName.equals(lowercaseName)) {
+      logger.warn("Converting service name ({}) to lowercase, see HADOOP-7988.", instanceName);
+    }
+    return lowercaseName;
+  }
+
+  public static String getDefaultRealm() throws ClassNotFoundException, NoSuchMethodException,
+      IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+    final Class<?> classRef = System.getProperty("java.vendor").contains("IBM") ?
+        Class.forName("com.ibm.security.krb5.internal.Config") :
+        Class.forName("sun.security.krb5.Config");
+
+    final Method getInstanceMethod = classRef.getMethod("getInstance", new Class[0]);
+    final Object kerbConf = getInstanceMethod.invoke(classRef, new Object[0]);
+    final Method getDefaultRealmMethod = classRef.getDeclaredMethod("getDefaultRealm", new
Class[0]);
+    return (String) getDefaultRealmMethod.invoke(kerbConf, new Object[0]);
+  }
+
+  // prevent instantiation
+  private KerberosUtil() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/d732aad2/exec/java-exec/src/main/java/org/apache/drill/exec/server/BootStrapContext.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/BootStrapContext.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/BootStrapContext.java
index c498185..90ab018 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/BootStrapContext.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/BootStrapContext.java
@@ -20,26 +20,46 @@ package org.apache.drill.exec.server;
 import com.codahale.metrics.MetricRegistry;
 import io.netty.channel.EventLoopGroup;
 
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.SynchronousQueue;
-import org.apache.drill.common.DrillAutoCloseables;
+import org.apache.drill.common.AutoCloseables;
+import org.apache.drill.common.KerberosUtil;
 import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.common.scanner.persistence.ScanResult;
 import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.exception.DrillbitStartupException;
 import org.apache.drill.exec.memory.BufferAllocator;
 import org.apache.drill.exec.memory.RootAllocatorFactory;
 import org.apache.drill.exec.metrics.DrillMetrics;
 import org.apache.drill.exec.rpc.NamedThreadFactory;
 import org.apache.drill.exec.rpc.TransportCheck;
+import org.apache.drill.exec.rpc.security.AuthenticatorProvider;
+import org.apache.drill.exec.rpc.security.AuthenticatorProviderImpl;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.security.UserGroupInformation;
 
 public class BootStrapContext implements AutoCloseable {
   private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BootStrapContext.class);
   private static final int MIN_SCAN_THREADPOOL_SIZE = 8; // Magic num
 
+  // DRILL_HOST_NAME sets custom host name. See drill-env.sh for details.
+  private static final String customHostName = System.getenv("DRILL_HOST_NAME");
+  private static final String processUserName = System.getProperty("user.name");
+
+  private static final String SERVICE_LOGIN_PREFIX = "drill.exec.security.auth";
+  public static final String SERVICE_PRINCIPAL = SERVICE_LOGIN_PREFIX + ".principal";
+  public static final String SERVICE_KEYTAB_LOCATION = SERVICE_LOGIN_PREFIX + ".keytab";
+  public static final String KERBEROS_NAME_MAPPING = SERVICE_LOGIN_PREFIX + ".auth_to_local";
+
   private final DrillConfig config;
+  private final AuthenticatorProvider authProvider;
   private final EventLoopGroup loop;
   private final EventLoopGroup loop2;
   private final MetricRegistry metrics;
@@ -48,10 +68,14 @@ public class BootStrapContext implements AutoCloseable {
   private final ExecutorService executor;
   private final ExecutorService scanExecutor;
   private final ExecutorService scanDecodeExecutor;
+  private final String hostName;
 
-  public BootStrapContext(DrillConfig config, ScanResult classpathScan) {
+  public BootStrapContext(DrillConfig config, ScanResult classpathScan) throws DrillbitStartupException
{
     this.config = config;
     this.classpathScan = classpathScan;
+    this.hostName = getCanonicalHostName();
+    login(config);
+    this.authProvider = new AuthenticatorProviderImpl(config, classpathScan);
     this.loop = TransportCheck.createEventLoopGroup(config.getInt(ExecConstants.BIT_SERVER_RPC_THREADS),
"BitServer-");
     this.loop2 = TransportCheck.createEventLoopGroup(config.getInt(ExecConstants.BIT_SERVER_RPC_THREADS),
"BitClient-");
     // Note that metrics are stored in a static instance
@@ -85,6 +109,66 @@ public class BootStrapContext implements AutoCloseable {
         Executors.newFixedThreadPool(scanDecodeThreadPoolSize, new NamedThreadFactory("scan-decode-"));
   }
 
+  private void login(final DrillConfig config) throws DrillbitStartupException {
+    try {
+      if (config.hasPath(SERVICE_PRINCIPAL)) {
+        // providing a service principal => Kerberos mechanism
+        final Configuration loginConf = new Configuration();
+        loginConf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION,
+            UserGroupInformation.AuthenticationMethod.KERBEROS.toString());
+
+        // set optional user name mapping
+        if (config.hasPath(KERBEROS_NAME_MAPPING)) {
+          loginConf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTH_TO_LOCAL,
+              config.getString(KERBEROS_NAME_MAPPING));
+        }
+
+        UserGroupInformation.setConfiguration(loginConf);
+
+        // service principal canonicalization
+        final String principal = config.getString(SERVICE_PRINCIPAL);
+        final String parts[] = KerberosUtil.splitPrincipalIntoParts(principal);
+        if (parts.length != 3) {
+          throw new DrillbitStartupException(
+              String.format("Invalid %s, Drill service principal must be of format: primary/instance@REALM",
+                  SERVICE_PRINCIPAL));
+        }
+        parts[1] = KerberosUtil.canonicalizeInstanceName(parts[1], hostName);
+
+        final String canonicalizedPrincipal = KerberosUtil.getPrincipalFromParts(parts[0],
parts[1], parts[2]);
+        final String keytab = config.getString(SERVICE_KEYTAB_LOCATION);
+
+        // login to KDC (AS)
+        // Note that this call must happen before any call to UserGroupInformation#getLoginUser,
+        // but there is no way to enforce the order (this static init. call and parameters
from
+        // DrillConfig are both required).
+        UserGroupInformation.loginUserFromKeytab(canonicalizedPrincipal, keytab);
+
+        logger.info("Process user name: '{}' and logged in successfully as '{}'", processUserName,
+            canonicalizedPrincipal);
+      } else {
+        UserGroupInformation.getLoginUser(); // init
+      }
+
+      // ugi does not support logout
+    } catch (final IOException e) {
+      throw new DrillbitStartupException("Failed to login.", e);
+    }
+
+  }
+
+  private static String getCanonicalHostName() throws DrillbitStartupException {
+    try {
+      return customHostName != null ? customHostName : InetAddress.getLocalHost().getCanonicalHostName();
+    } catch (final UnknownHostException e) {
+      throw new DrillbitStartupException("Could not get canonical hostname.", e);
+    }
+  }
+
+  public String getHostName() {
+    return hostName;
+  }
+
   public ExecutorService getExecutor() {
     return executor;
   }
@@ -121,6 +205,10 @@ public class BootStrapContext implements AutoCloseable {
     return classpathScan;
   }
 
+  public AuthenticatorProvider getAuthProvider() {
+    return authProvider;
+  }
+
   @Override
   public void close() {
     try {
@@ -150,6 +238,10 @@ public class BootStrapContext implements AutoCloseable {
       }
     }
 
-    DrillAutoCloseables.closeNoChecked(allocator);
+    try {
+      AutoCloseables.close(allocator, authProvider);
+    } catch (final Exception e) {
+      logger.error("Error while closing", e);
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/d732aad2/exec/java-exec/src/main/java/org/apache/drill/exec/server/Drillbit.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/Drillbit.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/Drillbit.java
index b4300e0..f225714 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/Drillbit.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/Drillbit.java
@@ -94,7 +94,7 @@ public class Drillbit implements AutoCloseable {
     context = new BootStrapContext(config, classpathScan);
     manager = new WorkManager(context);
 
-    webServer = new WebServer(config, context.getMetrics(), manager);
+    webServer = new WebServer(context, manager);
     boolean isDistributedMode = false;
     if (serviceSet != null) {
       coord = serviceSet.getCoordinator();
@@ -105,8 +105,7 @@ public class Drillbit implements AutoCloseable {
       isDistributedMode = true;
     }
 
-    engine = new ServiceEngine(manager.getControlMessageHandler(), manager.getUserWorker(),
context,
-        manager.getWorkBus(), manager.getBee(), allowPortHunting, isDistributedMode);
+    engine = new ServiceEngine(manager, context, allowPortHunting, isDistributedMode);
 
     logger.info("Construction completed ({} ms).", w.elapsed(TimeUnit.MILLISECONDS));
   }

http://git-wip-us.apache.org/repos/asf/drill/blob/d732aad2/exec/java-exec/src/main/java/org/apache/drill/exec/service/ServiceEngine.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/service/ServiceEngine.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/service/ServiceEngine.java
index 5cad0d4..aa43ee5 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/service/ServiceEngine.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/service/ServiceEngine.java
@@ -39,13 +39,10 @@ import org.apache.drill.exec.proto.CoordinationProtos.DrillbitEndpoint;
 import org.apache.drill.exec.rpc.TransportCheck;
 import org.apache.drill.exec.rpc.control.Controller;
 import org.apache.drill.exec.rpc.control.ControllerImpl;
-import org.apache.drill.exec.rpc.control.WorkEventBus;
 import org.apache.drill.exec.rpc.data.DataConnectionCreator;
 import org.apache.drill.exec.rpc.user.UserServer;
 import org.apache.drill.exec.server.BootStrapContext;
-import org.apache.drill.exec.work.WorkManager.WorkerBee;
-import org.apache.drill.exec.work.batch.ControlMessageHandler;
-import org.apache.drill.exec.work.user.UserWorker;
+import org.apache.drill.exec.work.WorkManager;
 
 import com.codahale.metrics.Gauge;
 import com.codahale.metrics.MetricRegistry;
@@ -58,16 +55,17 @@ public class ServiceEngine implements AutoCloseable {
   private final Controller controller;
   private final DataConnectionCreator dataPool;
   private final DrillConfig config;
-  boolean useIP = false;
   private final boolean allowPortHunting;
   private final boolean isDistributedMode;
   private final BufferAllocator userAllocator;
   private final BufferAllocator controlAllocator;
   private final BufferAllocator dataAllocator;
+  private final String hostName;
 
 
-  public ServiceEngine(ControlMessageHandler controlMessageHandler, UserWorker userWorker,
BootStrapContext context,
-      WorkEventBus workBus, WorkerBee bee, boolean allowPortHunting, boolean isDistributedMode)
throws DrillbitStartupException {
+  public ServiceEngine(final WorkManager manager, final BootStrapContext context,
+                       final boolean allowPortHunting, final boolean isDistributedMode)
+      throws DrillbitStartupException {
     userAllocator = newAllocator(context, "rpc:user", "drill.exec.rpc.user.server.memory.reservation",
         "drill.exec.rpc.user.server.memory.maximum");
     controlAllocator = newAllocator(context, "rpc:bit-control",
@@ -76,18 +74,15 @@ public class ServiceEngine implements AutoCloseable {
         "drill.exec.rpc.bit.server.memory.data.reservation", "drill.exec.rpc.bit.server.memory.data.maximum");
     final EventLoopGroup eventLoopGroup = TransportCheck.createEventLoopGroup(
         context.getConfig().getInt(ExecConstants.USER_SERVER_RPC_THREADS), "UserServer-");
-    this.userServer = new UserServer(
-        context.getConfig(),
-        context.getClasspathScan(),
-        userAllocator,
-        eventLoopGroup,
-        userWorker,
-        context.getExecutor());
-    this.controller = new ControllerImpl(context, controlMessageHandler, controlAllocator,
allowPortHunting);
-    this.dataPool = new DataConnectionCreator(context, dataAllocator, workBus, bee, allowPortHunting);
+    userServer = new UserServer(context, userAllocator, eventLoopGroup, manager.getUserWorker());
+    controller = new ControllerImpl(context, controlAllocator, manager.getControlMessageHandler(),
+        allowPortHunting);
+    dataPool = new DataConnectionCreator(context, dataAllocator, manager.getWorkBus(), manager.getBee(),
+        allowPortHunting);
     this.config = context.getConfig();
     this.allowPortHunting = allowPortHunting;
     this.isDistributedMode = isDistributedMode;
+    this.hostName = context.getHostName();
     registerMetrics(context.getMetrics());
 
   }
@@ -141,23 +136,16 @@ public class ServiceEngine implements AutoCloseable {
         name, context.getConfig().getLong(initReservation), context.getConfig().getLong(maxAllocation));
   }
 
-  private String getHostName() throws UnknownHostException{
-    // DRILL_HOST_NAME sets custom host name.  See drill-env.sh for details.
-    String customHost = System.getenv("DRILL_HOST_NAME");
-    if (customHost != null) {
-      return customHost;
-    }
-    return useIP ? InetAddress.getLocalHost().getHostAddress() : InetAddress.getLocalHost().getCanonicalHostName();
-  }
+  public DrillbitEndpoint start() throws DrillbitStartupException, UnknownHostException {
+    final int userPort = userServer.bind(config.getInt(ExecConstants.INITIAL_USER_PORT),
allowPortHunting);
 
-  public DrillbitEndpoint start() throws DrillbitStartupException, UnknownHostException{
-    int userPort = userServer.bind(config.getInt(ExecConstants.INITIAL_USER_PORT), allowPortHunting);
-    String address = getHostName();
-    checkLoopbackAddress(address);
+    // loopback address check
+    if (isDistributedMode && InetAddress.getByName(hostName).isLoopbackAddress())
{
+      throw new DrillbitStartupException("Drillbit is disallowed to bind to loopback address
in distributed mode.");
+    }
 
     DrillbitEndpoint partialEndpoint = DrillbitEndpoint.newBuilder()
-        .setAddress(address)
-        //.setAddress("localhost")
+        .setAddress(hostName)
         .setUserPort(userPort)
         .setVersion(DrillVersionInfo.getVersion())
         .build();
@@ -192,12 +180,6 @@ public class ServiceEngine implements AutoCloseable {
     });
   }
 
-  private void checkLoopbackAddress(String address) throws DrillbitStartupException, UnknownHostException
{
-    if (isDistributedMode && InetAddress.getByName(address).isLoopbackAddress())
{
-      throw new DrillbitStartupException("Drillbit is disallowed to bind to loopback address
in distributed mode.");
-    }
-  }
-
   @Override
   public void close() throws Exception {
     // this takes time so close them in parallel

http://git-wip-us.apache.org/repos/asf/drill/blob/d732aad2/exec/java-exec/src/main/java/org/apache/drill/exec/util/ImpersonationUtil.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/util/ImpersonationUtil.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/util/ImpersonationUtil.java
index 93ee7a0..8dab549 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/util/ImpersonationUtil.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/util/ImpersonationUtil.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -170,7 +170,7 @@ public class ImpersonationUtil {
    * @return Drillbit process user.
    */
   public static String getProcessUserName() {
-    return getProcessUserUGI().getUserName();
+    return getProcessUserUGI().getShortUserName();
   }
 
   /**


Mime
View raw message