亚马逊AWS官方博客

在多主节点的 Amazon EMR 集群中实现用户身份认证与细粒度访问控制(一)Open LDAP 身份认证与基于 Hive MetaStore 的访问控制

1. 方案介绍

本方案主要介绍 Amazon EMR 多主节点通过集成 Open LDAP + Apache Ranger 实现基于 Hive MetaStore 的用户和用户组级别细粒度的数据访问控制和管理,并实现 Hue、Beeline、Client 三个入口进行访问控制和管理。

Amazon EMR(Amazon Elastic MapReduce)是一款大数据分析服务,可简化在云上运行大数据框架(如 Apache HadoopApache Spark)的过程,用于处理和分析海量数据。使用这些框架和相关的开源项目,可以处理用于以分析为目的的数据和业务工作负载。Amazon EMR 还可以用于处理大量数据并导出/导入至其它 AWS 数据存储和数据库中。Amazon EMR 主要面向于在云上通过弹性扩展能力为用户提供弹性的资源缩放,从而降低使用大数据平台的成本。

Amazon EMR 的安全设计是基于云原生的理念,在满足资源弹性灵活的前提下同时需要对数据访问进行控制管理,满足基于用户和用户组来结合用户的业务需求、组织架构来进行管理控制,对于从基于开源 Hadoop 来构建数据湖体系的用户,希望能在 Amazon EMR 上实现数据访问控制。这时候,我们提供了 Open LDAP + Apache Ranger 的解决方案来实现对 Amazon EMR 的安全访问控制能力,其中对于有可用性要求较高的场景(比如生产环境),强烈建议创建多主节点集群,在 Amazon EMR 多主集群上进行数据访问控制必须使用开源版本的 Apache Ranger Plugin 来部署。

2. 方案架构

在使用 Amazon EMR 时,用户可能会通过多种方式访问,除了使用 Hue 作为数据开发/交互式的入口以外,还会用 Beeline、 Client 的方式或者使用其他开发工具来访问 Amazon EMR,因此在每一种访问方式的入口,都有相应的身份认证和数据访问控制,如下图:

2.1 EMR + Open LDAP + Apache Ranger 的部署

关于 Open LDAP + Apache Ranger 的部署,可以参考 Apache Ranger and AWS EMR Automated Installation and Integration Series (4): OpenLDAP + Open-Source Ranger 一文。按照该文章部署流程执行完成后,我们会获得一个 Amazon EMR 集群,一个部署 Open LDAP 服务和 Apache Ranger 服务的 EC2 实例。并且该 Amazon EMR 集群中的 Hue 已经集成了 Open LDAP,Apache Ranger 中已经安装好 HDFS Plugin 和 Hive Plugin。

由于 Hive Plugin 只能支持控制从 Hive 访问数据的权限。接下来,我们在此环境基础上叠加 Hive MetaStore Plugin,来同时控制 Hive、SparkSQL、Trino 访问数据的权限。并且,通过配置使得 Hive、Trino 支持 Open LDAP 服务,以便支持用户通过 CLI 或者通过外部的客户端方式访问 Amazon EMR 集群通过身份认证的场景。

2.2 用户与用户组

  • Hue 用户同步

用户与用户组使用 Open LDAP 进行管理,所以首先在 Open LDAP 中创建用户/用户组。在 Hue 中添加用户时,这个用户必须是在 Open LDAP 中存在,添加用户时,Hue 会从 Open LDAP 中将用户的其他信息同步至 Hue。

  • Apache Ranger 用户同步

Apache Ranger 通过 sync user从 Open LDAP 自动同步用户至 Apache Ranger(默认每 1 小时同步一次)。

  • 鉴权流程

用户通过 Hue 使用 EMR(Hive/Trino/Spark)引擎查询数据时,会先到 Apache Ranger 中进行鉴权,然后从 EMR 中获取数据。

2.2.1 Open LDAP 创建用户/用户组

首先,我们先在 Open LDAP 中创建用户和用户组:

创建用户:

user=testuser1
GIDNUMBER=10009
OPEN LDAP_ROOT_DN='cn=admin,dc=example,dc=com'
OPEN LDAP_ROOT_PASSWORD='<password>'
cat << EOF | ldapadd -D "$OPENLDAP_ROOT_DN" -w $OPENLDAP_ROOT_PASSWORD
dn: uid=$user,ou=users,dc=example,dc=com
objectClass: posixAccount
objectClass: top
objectClass: inetOrgPerson
uid: $user
displayName: $user
sn: $user
homeDirectory: /home/$user
cn: $user
uidNumber: $((1000+$RANDOM%9000))
userPassword: $(slappasswd -s <your-password>)
gidNumber: $GIDNUMBER
EOF

创建用户组:

用户组类型必须为 Posix,因此,先修改 Apache Ranger 的配置,登录 Apache Ranger 服务器,找到配置文件 ranger-ugsync-site.xml 按如下配置修改。

<property>
    <name>ranger.usersync.group.memberattributename</name>
    <value>memberUid</value>
 </property>
 <property>
    <name>ranger.usersync.group.nameattribute</name>
    <value>cn</value>
 </property>
 <property>
    <name>ranger.usersync.group.objectclass</name>
    <value>posixGroup</value>
 </property>
 <property>
    <name>ranger.usersync.group.searchbase</name>
    <value>ou=groups,dc=example,dc=com</value>
 </property>
 <property>
    <name>ranger.usersync.group.searchenabled</name>
    <value>true</value>
 </property>

使用 posixGroup 创建用户组

GROUP_NAME=group1
GIDNUMBER=10001
OPEN LDAP_ROOT_DN='cn=admin,dc=example,dc=com'
OPEN LDAP_BASE_DN='dc=example,dc=com'
OPEN LDAP_USERS_BASE_DN='ou=users,dc=example,dc=com'
OPEN LDAP_ROOT_PASSWORD='<password>'
cat << EOF | ldapadd -D "$OPENLDAP_ROOT_DN" -w $OPEN LDAP_ROOT_PASSWORD
dn: cn=$GROUP_NAME,ou=groups,$OPENLDAP_BASE_DN
cn: $GROUP_NAME
objectclass: top
objectclass: posixGroup
gidNumber: $GIDNUMBER
EOF

2.2.2 Hue 同步用户/用户组

进入 Hue UI 中,找到用户管理页面。

  1. Add/Sync LDAP user,必须是在 Open LDAP 中已经存在的用户,才能从这里添加。如果 Open LDAP 已经创建好用户之后,可以直接登录,Hue 会自动添加进来。
  2. Sync LDAP users/groups,用于与 Open LDAP 用户进行信息的更新,如果在 Open LDAP 对某一个 Hue 中已经存在的用户做了某些属性的修改,可以通过此按钮将更新的信息同步至 Hue。
  3. Groups,对于已经在 Open LDAP 创建的用户组,在 Hue 的用户管理中,通过 Add/Sync LDAP group 手动添加一次用户组,添加时 Hue 会从 Open LDAP 获取用户组和用户信息。

2.3 身份认证

2.3.1  配置 Hive 支持通过 Open LDAP 用户认证

添加 EMR Hive-site 配置,可在控制台添加以下配置

classification properties value
Hive-site Hive.server2.authentication.ldap.url LDAP 指定Hive身份认证的方式
Hive-site Hive.server2.authentication ldap://<Open LDAP server> Open LDAP服务地址
Hive-site Hive.server2.authentication.ldap.baseDN ou=users,dc=example,dc=com 用户绑定模式
Hive-site Hive.server2.authentication.ldap.userDNPattern uid=%s,ou=users,dc=example,dc=com 用户绑定模式
Hive-site Hive.server2.enable.doAs TRUE 以提交用户的身份去执行语句
Hive-site Hive.security.authorization.manager org.apache.hadoop.Hive.ql.security.authorization.Plugin.sqlstd.SQLStdConfOnlyAuthorizerFactory 指定Hive使用MetaStore端授权的提供程序

下面,我们使用 Hive beeline 验证受 Open LDAP 身份认证的 Hive 查询的权限。

如下图,测试不带密码访问,提示登录失败:

使用账号密码登录成功

2.3.2  配置 Spark Thrift Server 支持 Open LDAP 身份认证

启动带有 Open LDAP 认证的 Spark Thrift Server 服务

sudo /usr/lib/spark/sbin/start-thriftserver.sh  --master yarn \
    --Hiveconf  Hive.MetaStore.uris="thrift://<master-server>:9083" \  --Hiveconf   Hive.server2.authentication=LDAP \
    --Hiveconf  Hive.server2.authentication.ldap.url  "ldap://<ldap-server>"  \
    --Hiveconf  Hive.server2.authentication.ldap.baseDN  "ou=users,dc=example,dc=com" 

服务启动后,测试不带密码登录

beeline -n example-user-1 -u jdbc:Hive2://localhost:10001

如上图,登录被拒绝,再验证带账号密码,登录访问:

beeline -n example-user-1 -u jdbc:Hive2://localhost:10001 -p <password>

登录成功。

2.3.3 配置 Trino 支持 Open LDAP 的身份认证

Trino 集成 Open LDAP 服务需要通过签名认证,因此我们可以选择 Master 节点,通过以下脚本生成自签名文件

mkdir -p /mnt/demoCA && cd /mnt/demoCA

#获取ip地址
#commonName和IP必须和主机的hostname与IP相同。
local_ipv4=$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) 
local_hostname=$(hostname)
local_hostname_long=$(hostname -f)

cat << EOF > openssl.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[req_distinguished_name]
countryName = <Country Name>
countryName_default = US
stateOrProvinceName = <State or Province Name>
stateOrProvinceName_default = California
localityName = <Locality Name>
localityName_default = San Francisco
organizationName = <Organization Name(eg. company)>
organizationName_default = My Company
organizationalUnitName = <Oraganizational Unit Name(eg. section)>
organizationalUnitName_default  = IT Department
commonName = ${local_hostname}
commonName_max = 64
[v3_req]
basicConstraints = CA:TRUE
subjectAltName = @alt_names
[alt_names]
IP.1 = ${local_ipv4}
DNS.1 = ${local_hostname}
DNS.2 = ${local_hostname_long}
EOF

#创建 CA 目录结构
mkdir -p /mnt/demoCA/{private,newcerts} touch /mnt/demoCA/index.txt
echo 01 > /mnt/demoCA/serial
# 生成 CA 的 RSA 密钥对
PASSWORD="1234"

openssl genrsa -des3 -out /mnt/demoCA/private/cakey.pem -passout pass:"$PASSWORD" 2048
# 自签发 CA 证书
PASSWORD="1234"
local_hostname=$(hostname)
SUBJECT="/C=US/ST=California/L=San Francisco/O=My Company/OU=IT Department/CN=${local_hostname}"
openssl req -new -x509 -days 365 -key /mnt/demoCA/private/cakey.pem -passin pass:"$PASSWORD" -out /mnt/demoCA/cacert.pem -extensions v3_req -config /mnt/demoCA/openssl.cnf -subj "$SUBJECT"
# 查看证书内容
openssl x509 -in /mnt/demoCA/cacert.pem -noout -text # 设置输入密码(cakey.pem的密码)
IN_PASSWORD="1234"
# 设置输出密码(生成的P12文件的密码)
OUT_PASSWORD="1234"
# 生成PKCS12文件
openssl pkcs12 -inkey /mnt/demoCA/private/cakey.pem -in /mnt/demoCA/cacert.pem -export -out /mnt/demoCA/certificate.p12 -passin pass:"$IN_PASSWORD" -passout pass:"$OUT_PASSWORD"
# 默认密码通常为"changeit"
KEYSTORE_PASSWORD="changeit"
ALIAS="myTrinoserver2"
CERT_FILE="/mnt/demoCA/cacert.pem" KEYSTORE_PATH="/usr/lib/jvm/java-1.8.0/jre/lib/security/cacerts"
# 导入证书到密钥库
sudo keytool -import -alias "$ALIAS" -file "$CERT_FILE" -keystore "$KEYSTORE_PATH" -storepass "$KEYSTORE_PASSWORD" -noprompt

确认 EMR 集群已经配置 Open LDAP,然后可以修改 EMR 的配置。主实例和核心实例的值需要一致的 internal-communication.shared-secret。

Amazon EMR Trino 配置参考如下:

主实例

Classfication Propertiy Value Remark
Trino-config http-server.authentication.type PASSWORD 启用密码登录
Trino-config http-server.https.enabled TRUE 开启https
Trino-config http-server.https.port 8446 https端口
Trino-config http-server.https.keystore.path /mnt/demoCA/certificate.p12 p12密钥文件
Trino-config http-server.https.keystore.key 1234 密钥文件密码
Trino-password-authenticator password-authenticator.name Open LDAP 启用Open LDAP
Trino-password-authenticator ldap.url Open LDAP://<openldap-server>:389 Open LDAP服务地址
Trino-password-authenticator ldap.user-bind-pattern uid=${USER},ou=users,dc=example,dc=com 用户绑定模式
Trino-password-authenticator ldap.allow-insecure TRUE 是否允许在与Open LDAP通信时使用不安全的连接
Hue-ini.notebook.interpreters.Trino options {“url”: “jdbc:Trino://<master>:8446/Hive/default?SSL=true&SSLKeyStorePath=/mnt/demoCA/certificate.p12&SSLKeyStorePassword=1234″, “driver”:”io.Trino.jdbc.TrinoDriver”,”has_impersonation”: true} Hue Trino jdbc开启用户认证

核心实例

配置中 internal-communication.shared-secret 配置可以使用命令在任意节点上执行生成:

openssl rand 512 | base64
Classfication Propertiy Value Remark
Trino-config internal-communication.shared-secret <通过 openssl 命令生成的密钥> 启用密码登录

修改 Amazon EMR 配置后,Amazon EMR 会自动重启组实例组与 Core 实例组,重启完成后。使用 trino-cli,登录 Trino UI 以及在 Hue 中使用 Trino,都需要验证用户密码后才可以使用。

2.4 数据访问控制

数据访问控制使用 Apache Ranger,Apache Ranger 是基于访问策略的权限控制模型,通过对库表配置不同的访问策略,再赋权给用户,达到数据隔离的目的。Apache Ranger 提供了基于行列级别的权限控制,粒度更细,同时在数据查询中,可以对行级数据做脱敏操作。

Amazon EMR 原生已经支持了 Apache Ranger,在 Amazon EMR 多主节点的场景,我们要使用开源 Apache Ranger Plugin 的方案来实施。

Apache Ranger 是一个可插拔式的权限控制组件,用户对那些存储系统做权限管理时,只需要配置安装对应的 Plugin 即可。目前 Apache Ranger 原生支持的 Plugin 包括 Hive,Trino(2.4 以上版本),HDFS 等等。

2.4.1 Hive MetaStore Plugin

本方案主要对应在数仓分析的场景,大多数用户会使用 SparkSQL、Hive、Tez 来进行数据仓库场景的分析,但是由于每一个组件需要部署一个单独的 Apache Ranger Plugin 来控制,因此对于运维管理会变的比较复杂,同一张库表的权限,需要在不同组件的 Plugin 中配置,增加了用户权限配置的工作量,并且每次增加或者修改权限,都同时需要在每个 Plugin 中进行配置操作,增加了出错的风险。因此这里我们选择了一个开源但非 Apache Ranger 社区提供的 Hive MetaStore Plugin,它可以通过 Hive MetaStore 将 Trino、spark、Hive、Tez 的权限统一管理起来,这样,用户只需要在以一个 Plugin 中配置库表权限,即可在 Trino、spark、Hive、Tez 中都能应用。

获取 Hive MetaStore Plugin 代码,由于开源提供的 Hive MetaStore Plugin 并未实现用户组权限的管理,我们在开源代码的基础上做了修改,增加了针对 Hive MetaStore 的用户和用户组的权限控制。代码可从以下地址获取。

git clone https://github.com/norrishuang/Ranger-MetaStore-Plugin.git

编译打包

mvn clean compile package install assembly:assembly -Dmaven.test.skip=true

将编译好的文件 Apache Ranger-MetaStore-Plugin-2.1.0-MetaStore-Plugin.tar.gz 上传至 EMR master 每一个主节点中,修改配置文件 install.properties

POLICY_MGR_URL=http://<rangerserver>:6080
REPOSITORY_NAME=MetaStore
COMPONENT_INSTALL_DIR_NAME=/usr/lib/Hive/

启用 Hive MetaStore Plugin,并且重启 Hive-HCatalog-server 和 Hive-server2 服务:

#启用
cd Ranger-MetaStore-Plugin
sudo ./enable-MetaStore-Plugin.sh

#重启服务
sudo systemctl restart Hive-HCatalog-server
sudo systemctl restart Hive-server2

回到 Apache Ranger UI,将 Hive MetaStore 添加到 Apache Ranger 中,Username 填写 hadoop,Password 也填 hadoop

注意,由于启用了 Hive 的 Open LDAP,因此在 Hue 中使用 Hive 时,会出现异常。是因为,Hue 访问 Hive 的元数据的时候也需要认证登录。因此需要在 Open LDAP users 的 OU下添加一个 Hue 的用户,用于 Hue 在 Hive 中的认证,并且需要在 Apache Ranger 中将 Hue 用户添加进去,并且赋予 admin 的权限。

验证 Hive MetaStore Plugin

在 Apache Ranger 中添加一个策略,只给用户 example-user-1 访问 DWD 数据库的权限。

在 Hive beeline 中,通过用户账号密码登录:

beeline -u jdbc:Hive2://localhost:10000/ -n example-user-1 -p <password>

由于没有设置访问的数据库,会默认到 default 数据,因为 example-user-1 这个用户没有 default 数据的权限,登录失败。

使用用户 example-user-1 登录 DWD 数据库

beeline -u jdbc:Hive2://localhost:10000/dwd -n example-user-1 -p <password>

并且查询数据

select count(*) from dwd.dwd_uber_data_hour_1

可以正常查询数据。

3. 总结

目前随着用户越来越重视数据的安全合规,当基于 Amazon EMR 做为最重要的组件之一来构建数据湖时,如何对 Amazon EMR 的数据访问控制是非常重要的。本博客提供的解决方案提供一个全面的 Amazon EMR 数据访问控制和权限管理框架,帮助有数据细粒度权限管控要求的 Amazon EMR 用户,并且会通过多种连接方式(客户端 CLI,Hue,Beeline 等)访问 Amazon EMR 的场景,实现了基于 Hive MetaStore 的多组件(Hive、Trino、Spark、Yarn 队列、HBase)的身份认证,并且分别通过使用 Hive MetaStore Plugin 、Yarn Plugin、Simple 用户等方式实现细粒度的统一管理 Hive、SparkSQL、Trino、Tez 、Yarn 队列、 HBase 访问 Amazon EMR 中数据的权限控制,满足了 Amazon EMR 的安全管理和控制,确保只有经过授权的用户可以访问和操作数据。

关于针对 Yarn 队列,HBase 相关的访问控制,可以参考 《在多主节点的 Amazon EMR 集群中实现用户身份认证与细粒度访问控制(二)Yarn 队列控制与 HBase Simple 认证》

4. 参考资料

https://aws.amazon.com/cn/blogs/china/amazon-emr-authentication-scheme-based-on-openldap-and-kerberos-i-integrating-background-databases/

https://aws.amazon.com/cn/blogs/china/amazon-emr-authentication-scheme-based-on-openldap-and-kerberos-ii-synchronize-ldap-accounts-based-on-sssd/

https://docs.aws.amazon.com/zh_cn/emr/latest/ReleaseGuide/emr-presto-ldap.html

https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-presto-ldap.html

本篇作者

黄霄

亚马逊云科技数据分析解决方案架构师,专注于大数据解决方案架构设计,具有多年大数据领域开发和架构设计经验。

胡正光

AWS 解决方案架构师,15 年 IT 行业经验,目前负责基于 AWS 云计算方案架构的咨询和设计。