本文记录YARN、Spark、Hive各服务配置使用kerberos的过程。

我的环境:

  • 三台服务器,分别命名为zelda1、zelda2、zelda3
  • ubuntu 14.04
  • hadoop 2.7.2
  • spark 2.0/1.6.1

YARN认证

目的是将YARN接入到kerberos集群里,使得:

  1. RM和NM之间能互相认证,避免混进去恶意服务;
  2. 其他提交到YARN上的JOB必须能够通过kerberos认证,避免无证商户提交作业到YARN集群上去。

配置:修改yarn-site.xml,指定rm和nm的Principal和keytab,将该文件同步到所有节点。

<property>
     <name>yarn.resourcemanager.principal</name>
     <value>rm/_HOST@ZELDA.COM</value>
</property>

<property>
     <name>yarn.resourcemanager.keytab</name>
     <value>/etc/security/rm.service.keytab</value>
</property>

<property>
     <name>yarn.nodemanager.principal</name>
     <value>nm/_HOST@ZELDA.COM</value>
</property>

<property>
     <name>yarn.nodemanager.keytab</name>
     <value>/etc/security/nm.service.keytab</value>
</property>

Hortonworks还会要求配置container-excutor为LinuxContainer,可以有更好的物理隔离(底层为lxc)。但由于它需要启动container-executor,而这个要求配置文件container-executor.cfg必须为root权限,并且其上层所有目录权限都是root,所以只能把配置文件放到/etc/下,但是!container-executor代码里写死了配置文件的路径为../etc/,所以只能重新编译一个container-excutor。

所以我就先没配,挖个坑。

更详细的资料

spark作业认证

目的是spark-submit提交作业的时候,能够接入到kerberos中,从而向YARN提交作业、访问HDFS等等。

针对spark-submit的任务,有两种办法通过kerberos认证:

  • 先kinit -k -t /etc/security/xx.keytab user/host@REALM.COM,然后spark-submit提交即可
  • 作为参数提供给spark-submit:--keytab /etc/security/ieevee.zelda1.keytab --principal ieevee/zelda1@ZELDA.COM ,注意紧跟着命令,不要放到最后(会被当做spark JOB的参数)

总之还是以keytab的方式来的,只是从当前Principal缓存里读取,还是自己从keytab里读取。

1、新增一个Principal:

addprinc -randkey ieevee/zelda1@ZELDA.COM
xst -k ieevee.spark.keytab ieevee/zelda1@ZELDA.COM

2、将生成的ieevee.spark.keytab文件拷贝到/etc/security/下

3、kinit后submit作业

kinit -kt /etc/security/ieevee.spark.keytab  ieevee/zelda1
klist #检查Principal缓存
./bin/spark-submit --master yarn --class org.apache.spark.examples.SparkLR --name SparkLR lib/spark-examples-1.6.1-hadoop2.6.0.jar

或者跳过kinit直接指定keytab路径:

 ./bin/spark-submit --keytab /etc/security/ieevee.zelda1.keytab --principal ieevee/zelda1@ZELDA.COM --master yarn --class org.apache.spark.examples.SparkLR --name SparkLR lib/spark-examples-1.6.1-hadoop2.6.0.jar

spark sql的thriftserver是作为一个spark作业,通过spark-submit提交给yarn的,启动之前需要设置kinit或者指定keytab由spark-submit自己loginfromkeytab。

spark-submit还可以指定–proxy-user参数,可以模拟其他用户来提交job。

hive认证

hive支持三种:Kerberos、LDAP、CUSTOM(自定义插件)。如果使用 Kerberos 身份验证,Thrift 客户端和 HiveServer2 以及 HiveServer2 和安全 HDFS 之间都支持身份验证。如果使用 LDAP 身份验证,仅在 Thrift 客户端和 HiveServer2 之间支持身份验证(CUSTOM类似)。

下面采用kerberos认证的配置。

  <property>
    <name>hive.server2.enable.doAs</name>
    <value>false</value>
  </property>

<property>
  <name>hive.server2.authentication</name>
  <value>KERBEROS</value>
</property>
<property>
  <name>hive.server2.authentication.kerberos.principal</name>
  <value>ieevee/_HOST@ZELDA.COM</value>
</property>
<property>
  <name>hive.server2.authentication.kerberos.keytab</name>
  <value>/etc/security/ieevee.zelda1.keytab</value>
</property>

注意需要先kinit保证已经有Principal缓存。Kerberos客户端支持两种,一是使用Principal+Password,二是使用Principal+keytab,前者适合交互式应用,例如hadoop fs -ls这种,后者适合服务,例如yarn的rm、nm等。两种初始化方法的命令如下:

$ kinit zlatan/zelda1@ZELDA.COM
Password for zlatan/zelda1@ZELDA.COM:
--
$ kinit -k -t /../xx.keytab {username}/{instance}@{REALM}.COM

kinit后,启动hiveserver2,使用beeline登录:

beeline> !connect jdbc:hive2://zelda1:10000/default;principal=ieevee/zelda1@ZELDA.COM;
Connecting to jdbc:hive2://zelda1:10000/default;principal=ieevee/zelda1@ZELDA.COM;
Enter username for jdbc:hive2://zelda1:10000/default;principal=ieevee/zelda1@ZELDA.COM;: 
Enter password for jdbc:hive2://zelda1:10000/default;principal=ieevee/zelda1@ZELDA.COM;: 
Connected to: Apache Hive (version 2.0.0)
Driver: Hive JDBC (version 2.0.0)
16/06/13 10:46:50 [main]: WARN jdbc.HiveConnection: Request to set autoCommit to false; Hive does not support autoCommit=false.
Transaction isolation: TRANSACTION_REPEATABLE_READ

注意这里还是会提示输入用户名、密码,是个bug,别理它,直接回车就好了,我们用的其实是klist的Principal缓存来表示我是谁。 beeline会将Principal传给hive2 JDBC,由JDBC Driver去做Kerberos认证,这个过程略微复杂,等我弄懂了再写篇文章。

Spark SQL Thriftserver认证

目的是让不同的用户,使用不同的身份来登录beeline。

1、使用管理员账户kinit到Kerberos

我们用ieevee/zelda1@ZELDA.COM。thriftserver实际是个spark Job,通过spark-submit提交到YARN上去,需要这个账户用来访问YARN和HDFS;如果使用一些普通账户,由于HDFS权限不足,可能启动不了,因为需要往HDFS写一些东西。

2、配置spark配置文件里的hive-site.xml

thriftserver在YARN上跑起来了以后,也需要接入到Kerberos,hive-site里指定的就是这个Principal,这里我也是用的Principal。

配置跟前面hive的一样,注意doAs务必是true,否则又会包shim包找不到的错误;又由于doAs是true,所以下面需要安全伪装(我猜的)。

  <property>
    <name>hive.server2.enable.doAs</name>
    <value>true</value>
  </property>

<property>
  <name>hive.server2.authentication</name>
  <value>KERBEROS</value>
</property>
<property>
  <name>hive.server2.authentication.kerberos.principal</name>
  <value>ieevee/_HOST@ZELDA.COM</value>
</property>
<property>
  <name>hive.server2.authentication.kerberos.keytab</name>
  <value>/etc/security/ieevee.zelda1.keytab</value>
</property>

3、配置hadoop安全伪装

跟hive不一样,spark sql thriftserver需要使用安全伪装

修改hadoop的core-site.xml,配合doAs,这样ieevee/zelda1@ZELDA.COM可以模拟任何机器上的任何人,例如zlatan/zelda1@ZELDA.COM登录到beeline上以后,thriftserver会使用zlatan的权限去访问HDFS,而不是启动thriftserver的这个Principal(即ieevee/zelda@ZELDA.COM)。

<property>
  <name>hadoop.proxyuser.ieevee.hosts</name>
  <value>*</value>
</property>
<property>
  <name>hadoop.proxyuser.ieevee.groups</name>
  <value>*</value>
</property>

4、启动spark sql thriftserver

./start-thriftserver.sh --master yarn  --driver-java-options '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=13838'  --executor-memory 6g  --driver-memory 6g --conf spark.yarn.executor.memoryOverhead=4096 --driver-class-path /home/ieevee/spark/spark-1.6.1-bin-hadoop2.6/lib/mysql-connector-java-5.1.35-bin.jar

我在里面指定了remote debug,可以用IDEA远程连接到机器上调试。

5、登录跟hive一样,需要先kinit一个用户(密码or keytab),然后JDBC connect上去。

beeline > !connect jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;
Connecting to jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;
Enter username for jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;: 
Enter password for jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;: 
16/06/13 14:45:30 INFO Utils: Supplied authorities: zelda1:20000
16/06/13 14:45:30 INFO Utils: Resolved authority: zelda1:20000
16/06/13 14:45:30 INFO HiveConnection: Will try to open client transport with JDBC Uri: jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;
Connected to: Spark SQL (version 1.6.1)
Driver: Spark Project Core (version 1.6.1)
Transaction isolation: TRANSACTION_REPEATABLE_READ
6: jdbc:hive2://zelda1:20000/default> show databases;

beeline登录时遇到的几个问题:

1、Unsupported mechanism

beeline> !connect jdbc:hive2://zelda1:20000/default
16/06/12 14:24:32 INFO HiveConnection: Transport Used for JDBC connection: null
Error: Could not open client transport with JDBC Uri: jdbc:hive2://zelda1:20000/default: Peer indicated failure: Unsupported mechanism type PLAIN (state=08S01,code=0)

需要指定principal。不指定的话显然就是PLAIN方式啊笨蛋。

2、不准伪装!

beeline> !connect jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM
Connecting to jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM
Enter username for jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM: 
Enter password for jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM: 
16/06/12 14:40:42 INFO Utils: Supplied authorities: zelda1:20000
16/06/12 14:40:42 INFO Utils: Resolved authority: zelda1:20000
16/06/12 14:40:43 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
16/06/12 14:40:43 INFO HiveConnection: Will try to open client transport with JDBC Uri: jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM
Error: Failed to open new session: java.lang.RuntimeException: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.authorize.AuthorizationException): User: ieevee/zelda1@ZELDA.COM is not allowed to impersonate ieevee (state=,code=0)

需要配置安全伪装。

多租户和编程场景

前面的方案要求先kinit,每个client(linux用户级别)当前只能有一个用户,其他用户再kinit会顶替掉当前的Principal,不适合多租户和编程场景:

In the current approach of using Kerberos you need to have a valid Kerberos ticket in the ticket cache before connecting. This entails a static login (using kinit, key tab or ticketcache) and the restriction of one Kerberos user per client. These restrictions limit the usage in middleware systems and other multi-user scenarios, and in scenarios where the client wants to login programmatically to Kerberos KDC.

不过hive提供了secure proxy users功能,

  1. Hadoop level权限的用户(如本例的ieevee),在当前会话过程中模拟proxy user(如普通用户zlatan)来执行,权限检查时都是针对proxy user,而不是特权用户
  2. Delegation token based connection for Oozie,即使用一个已经授权的机器的token,去访问另一个未经授权的机器。还没用过Oozie。

我们这里先关注JDBC,它的方法比较简单,增加hive.server2.proxy.user=bob参数即可。

beeline> !connect jdbc:hive2://zelda1:20000/default;principal=ieevee/zelda1@ZELDA.COM;hive.server2.proxy.user=bob
0: jdbc:hive2://zelda1:20000/default> insert into x1 select t.* from (select 105, 37) t;
Error: java.lang.RuntimeException: Cannot create staging directory 'hdfs://zelda1/user/hive/warehouse/ieevee.db/x1/.hive-staging_hive_2016-06-14_11-15-29_063_112329594714965380-1': Permission denied: user=bob, access=WRITE, inode="/user/hive/warehouse/ieevee.db/x1/.hive-staging_hive_2016-06-14_11-15-29_063_112329594714965380-1":ieevee:supergroup:drwxrwxr-x

基于存储的认证、授权模型基本是可用的了,若辅助以Ranger这样的权限控制,会比较灵活、统一。不过这里还有个问题,Kerberos的用户怎么跟Ranger打通(啊!)。目前Ranger只支持跟UNIX和LDAP/AD用户同步。

总结

使用Kerberos,的确可以解决服务互相认证、用户认证的功能。

场景1:用户提交spark App。需要该用户有kerberos的权限,以及对应的yarn、hdfs的读写权限等。

场景2:jdbc登录。以beeline为例,不同的用户通过kinit使用自己的Principal+密码通过Kerberos的AS认证拿到TGT,就可以登录到spark sql thriftserver上去查看库、表;不过由于sts还不支持sqlbased authorization,所以还只能做到底层hdfs的权限隔离,比较可惜;相对来说hive的完整度高一些,支持SQLstandard authorization。

对于Zeppeline或者数梦的dataStudio这样的产品来说,web服务可以使用管理员账户启动;普通用户先通过Studio的认证,在JDBC访问Spark thrift server的时候,studio可以使用Principal+proxyUser的方式,以当前会话的用户的身份,来访问最终资源如HDFS,以达到认证+数据隔离的目的。

仔细阅读:

HiveServer2 Clients

通过WEB修改用户的kerberos密码


传送门:

Kerberos从入门到放弃(一):HDFS使用kerberos

Kerberos从入门到放弃(二):YARN、Spark、Hive使用kerberos

Kerberos从入门到放弃(三):kerberos+LDAP各司其职