Apache Zeppelin 基于 kerberos 多租户集
0 前言
构建大数据平台初期,我们的数据分析师做数据开发需要 ssh 到机器上,面对黑黑的 terminal 进行开发,为了提升开发效率,架构师让我开发一个 hive 可视化查询页面,支持 hive 查询,数据可视化,数据导出等。本着开发前先调研轮子的原则,一番调研到了两个轮子:
-
Hue : cloudera 生态自带组件,功能强大,但是仅限于 cloudera 生态圈
-
Zeppelin :Apache 开源顶级项目,支持 hive spark 相关开发,支持数据导出,数据可视化等。同时还支持一系列组件;
ok,少造轮子,拥抱开源,我就把 Zeppelin 第一版本(0.6.0)搭起来了,当然,途中也遇到不少坑。
在 shiro 中配置了一个 admin 用户,大家都可以登陆,进行 Spark Hive 开发和数据可视化等。(印象中 0.6.0 版本都不支持配置用户?)
这个版本的问题是:所以人都是一个账号登陆,同样的权限执行 hive spark 类任务。用户的权限没有隔离,对于大型企业来说会带来一系列安全隐患,这也是数据平台都要面对问题。这样,多租户应运而生。
1 多租户
每个用户对应一个独立账号 - user,用户归为一个分组 - group,权限通过linux hdfs 组来控制(linux 用户文件系统 group 和 hdfs group)
1.1 IAM
自研统一身份认证服务 IAM (Identity and Access Management ),提供身份认证和权限管理功能,可以管理用户(比如员工、系统或应用程序)账号,对账号的权限资源范围进行控制
IAM 根据用户职责规划用户组,并根据职责为用户组授权,再将用户加入到用户组中,使得用户具有所属用户组中的权限。
当前 IAM 用户的组(项目)来确定,创建用户后,分配对应的组,创建对应的权限。
先创建一个组(group)- 项目 - music
-
元数据存储
-
hdfs 目录创建项目的根目录, /data/music ,目录权限为 755 组为:music
新建一个用户 yao,所属组 (项目) music。
-
存储用户名,密码,分组等元数据
-
将用户,组相关信息 写入 LDAP,供后续 SSSD 使用
1.2 LDAP + Kerberos + SSSD
认证体系一般包含账号,认证,授权,审计这些部分组成。LDAP 用来做账号管理,Kerberos作为认证。授权一般来说是由应用来决定的,通过在 LDAP 数据库中配置一些属性可以让应用程序来进行授权判断。
单纯基于 LDAP 已经能实现集中的帐号和认证管理了,但考虑到 LDAP 里的密码信息是直 接存储在数据库中,在认证时需要将用户名和密码直接发送给 LDAP 服务器,在不安全和可信的环境下这种模式会有安全隐患。
因此使用 Kerberos 来实现用户认证。Kerberos 相关的数据也需要存储在某个数据库中,在这里我们选择使用 LDAP 作为其数据库,目的是为了数据备份的方便(只需要统一备份 LDAP 数据库即可)。
linux 上的认证授权基于 SSSD 来实现,SSSD 可以接入 LDAP 的数据,我们可以通过官方介绍有一个比较清晰的了解
Most system authentication is configured locally, which means that services must check with a local user store to determine users and credentials. What SSSD does is allow a local service to check with a local cache in SSSD, but that cache may be taken from any variety of remote identity providers — an LDAP directory, an Identity Management domain, Active Directory, possibly even a Kerberos realm.
正式因为有了 SSSD, 我们创建租户账号后,可以不用做任何操作,自动 ssh 登陆到机器。
2 Zepplin 多租户支持现状
当前 Zeppelin 官方 feature 上写的是 multi-user,刚开始很容易理解为支持多租户。经过一番研究,代码走读,发现 Zeppelin 支持的确实是多用户,即使支持用户模拟 Interpreter User Impersonation
。但是用户实际执行 hive 或者 spark 操作的时候, Kerberos 认证只能通过 keytab 的方式,且一个 interpreter 只能指定一个 keytab。
我们通过 一段 jdbc interperter 源码来分析
public static AuthenticationMethod getAuthtype(Properties properties) {
AuthenticationMethod authType;
try {
// 前端获取认证类型
authType = AuthenticationMethod.valueOf(properties.getProperty("zeppelin.jdbc.auth.type")
.trim().toUpperCase());
} catch (Exception e) {
LOGGER.error(String.format("Invalid auth.type detected with value %s, defaulting " +
"auth.type to SIMPLE", properties.getProperty("zeppelin.jdbc.auth.type")));
authType = SIMPLE;
}
return authType;
}
如果是 Kerberos 认证类型,通过配置的 keytab 初始话认证信息。这里就可以看出,keytab 的配置是基于 interpreter 配置的,而不是基于用户动态变化的;
而实际我们大数据平台 Kerberos 认证是基于用户级,用户通过执行 kinit 命令 ,输入密码完成认证,Zeppelin 当前不支持独立租户认证执行任务。
public static void createSecureConfiguration(Properties properties) {
AuthenticationMethod authType = getAuthtype(properties);
switch (authType) {
case KERBEROS:
Configuration conf = new org.apache.hadoop.conf.Configuration();
conf.set("hadoop.security.authentication", KERBEROS.toString());
UserGroupInformation.setConfiguration(conf);
try {
UserGroupInformation.loginUserFromKeytab(
properties.getProperty("zeppelin.jdbc.principal"),
// interpreter keytab 固化
properties.getProperty("zeppelin.jdbc.keytab.location")
);
} catch (IOException e) {
LOGGER.error("Failed to get either keytab location or principal name in the " +
"interpreter", e);
}
}
}
为了让 Zeppelin 支持基于租户 kinit 模式的独立认证方式,我们对 Zeppelin 进行了一系列代码,脚本改写,实现多租户。
3 Zeppelin 多租户集成
3.1 基础 Zeppelin 环境搭建
可以参考 Apache 官方文档,不复杂
3.2 交互式分析页面 - 租户登录认证拦截配置
进入 Zeppelin server 配置目录 conf/shiro.ini 配置 LDAP
使得 Zeppelin 登录使用 LDAP 里面的用户进行登录校验,参考的配置如下
ldapRealm = org.apache.zeppelin.realm.LdapGroupRealm
# search base for ldap groups (only relevant for LdapGroupRealm):
ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM
ldapRealm.contextFactory.url = [ldap://ldap.test.com:389](ldap://ldap.test.com:389/)
ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM
ldapRealm.contextFactory.authenticationMechanism = simple
这样我们多租户可以登陆啦,剩下待解决的问题就是,多租户独立认证访问集群服务。
3.3 用户名 / 密码 存储服务
为了实现多租户的独立认证,不同模块的无缝登录认证,避免多次密码输入和流程中断;
笔者改写 Zeppelin Server 端登陆部分的源码,将租户登录后的用户名和密码通过加密存储在本地轻量级数据库,以备后续程序调用。
当然这里存储方案有两种供选择:
-
直接写入 linxu 本地文件:实现起来快,但是有安全隐患
-
写入数据库:加密存储,高可靠
3.4 租户自动化认证模块 – 基于 Expect 的脚本
-
新增
kinit.sh
脚本, 传入租户用户名,密码,完成 kinit 操作 -
修改了
interpreter.sh
新增 expect su 实现租户自动化的免密登录,并调用认证脚本进行 kerberos 认证。 -
租户认证通过后,启动该租户对应的组件解释器,实现组件服务监听,接受客户端请求。
kinit 脚本就比较简单
#!/bin/bash
# kinit with user and password
user=$1
password=$2
echo "${password}" | kinit $user
expect 脚本代码片段,提供两种方式自动登陆,su 和 ssh,由于 ssh 登陆是临时会话, kerberos 认证生成的票据过期时间短,后面改成 su 的方式。
auto_su () {
expect -c "set timeout -1;
spawn su $2 -c \"${@:3}\";
expect {
*assword:* {
send $1\r;
}
eof
}
"
}
auto_smart_ssh () {
expect -c "set timeout -1;
spawn ssh -o StrictHostKeyChecking=no $2 ${@:3};
expect {
*assword:* {send -- $1\r;
expect {
*denied* {exit 2;}
eof
}
}
eof {exit 1;}
"
return $?
}
总体流程如如下图所示:
image.png
最终架构如下图所示:
image.png
4 总结
总结下这种方案的优点:
1. 在已有多租户大数据平台下,基于开源工具快速开发,只需修改少量 Zeppelin 源码,实现多租户交互式查询系统,实现多租户据隔离,保证集群数据安全性。
2. 基于Zeppelin + LDAP + Kerberos + Expect 脚本 + 用户密码存储服务模式, 实现前端查询平台与大数据系统平台,无缝衔接,完成统一用户登录认证,平台服务认证授权。
3. 通过shell expect 脚本结合本地加解密存储服务,实现租户无密码,单点登录,统一认证访问 Hadoop Hive Spark HBase 等大数据组件,实现交互式查询分析服务。