Эх сурвалжийг харах

HUE-219. Add authorization to thrift plugins

Todd Lipcon 15 жил өмнө
parent
commit
b6381e48bb

+ 5 - 1
desktop/libs/hadoop/java/src/java/org/apache/hadoop/mapred/ThriftJobTrackerPlugin.java

@@ -1239,7 +1239,11 @@ public class ThriftJobTrackerPlugin extends JobTrackerPlugin implements Configur
         @Override
         @Override
         public TProcessor getProcessor(TTransport t) {
         public TProcessor getProcessor(TTransport t) {
           ThriftServerContext context = new ThriftServerContext(t);
           ThriftServerContext context = new ThriftServerContext(t);
-          ThriftHandler impl = new ThriftHandler(context);
+          Jobtracker.Iface impl =
+            ThriftUtils.SecurityCheckingProxy.create(
+              conf,
+              new ThriftHandler(context),
+              Jobtracker.Iface.class);
           return new Jobtracker.Processor(impl);
           return new Jobtracker.Processor(impl);
         }
         }
     }
     }

+ 6 - 1
desktop/libs/hadoop/java/src/java/org/apache/hadoop/thriftfs/DatanodePlugin.java

@@ -245,7 +245,12 @@ public class DatanodePlugin
     @Override
     @Override
     public TProcessor getProcessor(TTransport t) {
     public TProcessor getProcessor(TTransport t) {
       ThriftServerContext context = new ThriftServerContext(t);
       ThriftServerContext context = new ThriftServerContext(t);
-      ThriftHandler impl = new ThriftHandler(context);
+
+      Datanode.Iface impl =
+        ThriftUtils.SecurityCheckingProxy.create(
+          conf,
+          new ThriftHandler(context),
+          Datanode.Iface.class);
       return new Datanode.Processor(impl);
       return new Datanode.Processor(impl);
     }
     }
   }
   }

+ 6 - 42
desktop/libs/hadoop/java/src/java/org/apache/hadoop/thriftfs/HadoopThriftAuthBridge.java

@@ -42,8 +42,6 @@ import org.apache.thrift.transport.TTransportFactory;
 /**
 /**
  * Functions that bridge Thrift's SASL transports to Hadoop's
  * Functions that bridge Thrift's SASL transports to Hadoop's
  * SASL callback handlers and authentication classes.
  * SASL callback handlers and authentication classes.
- *
- * This is borrowed from Hive's metastore.
  */
  */
 class HadoopThriftAuthBridge {
 class HadoopThriftAuthBridge {
   static final Log LOG = LogFactory.getLog(HadoopThriftAuthBridge.class);
   static final Log LOG = LogFactory.getLog(HadoopThriftAuthBridge.class);
@@ -93,38 +91,8 @@ class HadoopThriftAuthBridge {
   public static class Server {
   public static class Server {
     private final UserGroupInformation realUgi;
     private final UserGroupInformation realUgi;
 
 
-    /**
-     * TODO: javadoc
-     */
-    public Server(String keytabFile, String principalConf)
-      throws TTransportException {
-      if (keytabFile == null || keytabFile.isEmpty()) {
-        throw new TTransportException("No keytab specified");
-      }
-      if (principalConf == null || principalConf.isEmpty()) {
-        throw new TTransportException("No principal specified");
-      }
-
-      // Login from the keytab
-      UserGroupInformation ugi;
-      String kerberosName;
-      try {
-        kerberosName = SecurityUtil.getServerPrincipal(
-          principalConf, null);
-        realUgi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-          kerberosName, keytabFile);
-        assert realUgi.isFromKeytab();
-      } catch (IOException ioe) {
-        throw new TTransportException(ioe);
-      }
-    }
-
-    public Server() throws TTransportException {
-      try {
-        realUgi = UserGroupInformation.getLoginUser();
-      } catch (IOException ioe) {
-        throw new TTransportException(ioe);
-      }
+    public Server(UserGroupInformation serverUgi) throws TTransportException {
+      this.realUgi = serverUgi;
       if (realUgi == null || !realUgi.hasKerberosCredentials()) {
       if (realUgi == null || !realUgi.hasKerberosCredentials()) {
         throw new TTransportException("UGI " + realUgi + " has no kerberos credentials");
         throw new TTransportException("UGI " + realUgi + " has no kerberos credentials");
       }
       }
@@ -161,19 +129,15 @@ class HadoopThriftAuthBridge {
     }
     }
 
 
     /**
     /**
-     * Wrap a TProcessor in such a way that, before processing any RPC, it
-     * assumes the UserGroupInformation of the user authenticated by
-     * the SASL transport.
+     * Wraps a processor factory in a new processor factory that interposes
+     * the TUGIAssumingProcessor wrapper. This is required in order to assume
+     * the remote user UGI for each call.
      */
      */
-    public TProcessor wrapProcessor(TProcessor processor) {
-      return new TUGIAssumingProcessor(processor);
-    }
-
     public TProcessorFactory wrapProcessorFactory(final TProcessorFactory factory) {
     public TProcessorFactory wrapProcessorFactory(final TProcessorFactory factory) {
       return new TProcessorFactory(null) {
       return new TProcessorFactory(null) {
         @Override
         @Override
         public TProcessor getProcessor(TTransport trans) {
         public TProcessor getProcessor(TTransport trans) {
-          return wrapProcessor(factory.getProcessor(trans));
+          return new TUGIAssumingProcessor(factory.getProcessor(trans));
         }
         }
       };
       };
     }
     }

+ 5 - 1
desktop/libs/hadoop/java/src/java/org/apache/hadoop/thriftfs/NamenodePlugin.java

@@ -454,7 +454,11 @@ public class NamenodePlugin extends org.apache.hadoop.hdfs.server.namenode.Namen
     @Override
     @Override
     public TProcessor getProcessor(TTransport t) {
     public TProcessor getProcessor(TTransport t) {
       ThriftServerContext context = new ThriftServerContext(t);
       ThriftServerContext context = new ThriftServerContext(t);
-      ThriftHandler impl = new ThriftHandler(context);
+      Namenode.Iface impl =
+        ThriftUtils.SecurityCheckingProxy.create(
+          conf,
+          new ThriftHandler(context),
+          Namenode.Iface.class);
       return new Namenode.Processor(impl);
       return new Namenode.Processor(impl);
     }
     }
   }
   }

+ 6 - 1
desktop/libs/hadoop/java/src/java/org/apache/hadoop/thriftfs/ThriftPluginServer.java

@@ -67,7 +67,12 @@ public class ThriftPluginServer implements Configurable, Runnable {
     this.address = address;
     this.address = address;
 
 
     if (UserGroupInformation.isSecurityEnabled()) {
     if (UserGroupInformation.isSecurityEnabled()) {
-      authBridge = new HadoopThriftAuthBridge.Server();
+      try {
+        authBridge = new HadoopThriftAuthBridge.Server(
+          UserGroupInformation.getLoginUser());
+      } catch (IOException ioe) {
+        throw new TTransportException(ioe);
+      }
 
 
       this.processorFactory = authBridge.wrapProcessorFactory(
       this.processorFactory = authBridge.wrapProcessorFactory(
         processorFactory);
         processorFactory);

+ 65 - 0
desktop/libs/hadoop/java/src/java/org/apache/hadoop/thriftfs/ThriftUtils.java

@@ -18,9 +18,12 @@
 package org.apache.hadoop.thriftfs;
 package org.apache.hadoop.thriftfs;
 
 
 import java.net.InetSocketAddress;
 import java.net.InetSocketAddress;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.logging.LogFactory;
@@ -38,6 +41,8 @@ import org.apache.hadoop.thriftfs.api.DatanodeInfo;
 import org.apache.hadoop.thriftfs.api.DatanodeState;
 import org.apache.hadoop.thriftfs.api.DatanodeState;
 import org.apache.hadoop.thriftfs.api.IOException;
 import org.apache.hadoop.thriftfs.api.IOException;
 import org.apache.hadoop.thriftfs.api.Namenode;
 import org.apache.hadoop.thriftfs.api.Namenode;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.thrift.TException;
 import org.apache.thrift.protocol.TBinaryProtocol;
 import org.apache.thrift.protocol.TBinaryProtocol;
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.transport.TSocket;
 import org.apache.thrift.transport.TSocket;
@@ -46,6 +51,9 @@ public class ThriftUtils {
   
   
   static final Log LOG = LogFactory.getLog(ThriftUtils.class);
   static final Log LOG = LogFactory.getLog(ThriftUtils.class);
 
 
+  static final String HUE_USER_NAME_KEY = "hue.kerberos.principal.shortname";
+  static final String HUE_USER_NAME_DEFAULT = "hue";
+
   public static LocatedBlock fromThrift(Block block) {
   public static LocatedBlock fromThrift(Block block) {
     if (block == null) {
     if (block == null) {
       return null;
       return null;
@@ -165,6 +173,63 @@ public class ThriftUtils {
     return ret;
     return ret;
   }
   }
 
 
+  public static class SecurityCheckingProxy<T> implements java.lang.reflect.InvocationHandler {
+    private final T wrapped;
+    private final Configuration conf;
+
+    public static <T> T create(Configuration conf, T wrapped, Class<T> iface) {
+      return (T)java.lang.reflect.Proxy.newProxyInstance(
+        iface.getClassLoader(),
+        new Class[] { iface },
+        new SecurityCheckingProxy<T>(wrapped, conf));
+    }
+
+    private SecurityCheckingProxy(T wrapped, Configuration conf) {
+      this.wrapped = wrapped;
+      this.conf = conf;
+    }
+
+    public Object invoke(Object proxy, Method m, Object[] args)
+      throws Throwable
+    {
+      Object result;
+      try {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Call " + wrapped.getClass() + "." + m.getName() +
+                    StringUtils.joinObjects(", ", Arrays.asList(args)));
+        }
+        authorizeCall(m);
+
+	    return m.invoke(wrapped, args);
+      } catch (InvocationTargetException e) {
+	    throw e.getTargetException();
+      }
+    }
+
+    private void authorizeCall(Method m) throws IOException, TException {
+      // TODO: this should use the AccessControlList functionality,
+      // ideally.
+      try {
+        UserGroupInformation caller = UserGroupInformation.getCurrentUser();
+
+        if (!conf.get(HUE_USER_NAME_KEY, HUE_USER_NAME_DEFAULT).equals(
+              caller.getShortUserName()) &&
+            !UserGroupInformation.getLoginUser().getShortUserName().equals(
+              caller.getShortUserName())) {
+
+          String errMsg = "Unauthorized access for user " + caller.getUserName();
+          if (Arrays.asList(m.getExceptionTypes()).contains(IOException.class)) {
+            throw ThriftUtils.toThrift(new Exception(errMsg));
+          } else {
+            throw new TException(errMsg);
+          }
+        }
+      } catch (java.io.IOException ioe) {
+        throw new TException(ioe);
+      }
+    }
+  }
+
   /**
   /**
    * Creates a Thrift name node client.
    * Creates a Thrift name node client.
    * 
    *