在hdfs+mapred+impala+kerberos运行正常后,开始测试hive+kerberos。hive0.11之后开始支持kerberos的验证,hive主要有两种访问方式,一种方法是shell运行,即直接运行hive命令,另一种方法时启动hiveserver,然后通过jdbc或odbc进行验证,其中第二种方法可以通过hive0.11的beeline进行测试。
在使用hive shell运行时,主要遇到下面几个问题:
1.权限验证问题。
在hive操作hdfs数据的时候,权限是分为两层的,一个是hive层面的权限控制,主要是控制表的权限,一个是hdfs文件的权限控制,控制用户的读写操作。
表的相关权限主要是写在hive的元数据库中,主要是TBL_PRIVS表(控制表的权限)和DB_PRIVS 表(控制库的权限)。
在开启kerberos之后,传入hive的用户名是principal的全名,比如hdfs/gxxxx@KERBEROS_HADOOP,而hive权限表中的PRINCIPAL_NAME 字段是hdfs,会导致没有表的权限,虽然可以通过update表来更改相应的PRINCIPAL_NAME 的值,但是实际使用中缺乏可运维性。在hdfs层面,有一个user mapping的功能(由hadoop.security.auth_to_local参数控制),默认会把principal的全名进行map,转换为unix的用户名形式。

2.hook的问题。
由于hive有grant的bug,因此线上使用了hook来做用户验证,即执行grant语法是会判断当前用户是否是hdfs用户,如果不是则报错,主要的代码如下(老毕提供):
import java.util.HashSet;
import org.apache.hadoop.hive.ql.parse.ASTNode;
import org.apache.hadoop.hive.ql.parse.AbstractSemanticAnalyzerHook;
import org.apache.hadoop.hive.ql.parse.HiveParser;
import org.apache.hadoop.hive.ql.parse.HiveSemanticAnalyzerHookContext;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class AuthHook extends AbstractSemanticAnalyzerHook {
     static final private Log LOG = LogFactory.getLog(AuthHook.class.getName());
     private static HashSet adminlist = new HashSet() {
          {add("hdfs");}
     };
     private static String adminstr = "hdfs";
     @Override
     public ASTNode preAnalyze(HiveSemanticAnalyzerHookContext context,
               ASTNode ast) throws SemanticException {
          switch (ast.getToken().getType()) {
          case HiveParser.TOK_CREATEDATABASE:
          case HiveParser.TOK_DROPDATABASE:
          case HiveParser.TOK_CREATEROLE:
          case HiveParser.TOK_DROPROLE:
          case HiveParser.TOK_GRANT:
          case HiveParser.TOK_REVOKE:
          case HiveParser.TOK_GRANT_ROLE:
          case HiveParser.TOK_REVOKE_ROLE:
               String userName = null;
               if (SessionState.get() != null && SessionState.get().getAuthenticator() != null) {
                    userName = SessionState.get().getAuthenticator().getUserName();
               }
               LOG.debug("authhook username = "+userName);
               if (!adminlist.contains(userName.toLowerCase())) {
                    throw new SemanticException("User:"+userName
                              + " isn't ADMIN, please ask for " + adminstr + ".");
               }
               break;
          default:
               LOG.debug("AST type = "+ast.getToken().getType());
               break;
          }
          return ast;
     }
}

其中获取当前传入的用户名是通过 SessionState.get().getAuthenticator().getUserName();
集成了kerberos之后,在实际使用中会报
FAILED: SemanticException User:hdfs/xxxxx@KERBEROS_HADOOP isn't ADMIN, please ask for hdfs.的错误。

这两个问题的解决,借鉴了点评的patch:
https://github.com/dianping/hive/commit/aefaafd708138048fb52d4a857a1bdcca34d2bb1
即把ql/src/java/org/apache/hadoop/hive/ql/security/HadoopDefaultAuthenticator.java
中的this.userName = ugi.getUserName();改为
this.userName = ShimLoader.getHadoopShims().getShortUserName(ugi);