Kerberos从入门到放弃(二):YARN、Spark、Hive使用kerberos
by 伊布
本文记录YARN、Spark、Hive各服务配置使用kerberos的过程。
我的环境:
- 三台服务器,分别命名为zelda1、zelda2、zelda3
- ubuntu 14.04
- hadoop 2.7.2
- spark 2.0/1.6.1
YARN认证
目的是将YARN接入到kerberos集群里,使得:
- RM和NM之间能互相认证,避免混进去恶意服务;
- 其他提交到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功能,
- Hadoop level权限的用户(如本例的ieevee),在当前会话过程中模拟proxy user(如普通用户zlatan)来执行,权限检查时都是针对proxy user,而不是特权用户
- 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,以达到认证+数据隔离的目的。
仔细阅读:
传送门:
Kerberos从入门到放弃(一):HDFS使用kerberos
Subscribe via RSS