关于HDFS-KMS集群化部署教程,你以前看的都错了!

Posted 京东技术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于HDFS-KMS集群化部署教程,你以前看的都错了!相关的知识,希望对你有一定的参考价值。

来这里找志同道合的小伙伴!


作 者 简 介

吕信,京东商城基础架构部资深架构师,拥有多年数据产品研发及架构经验。在京东及国内主导过多种数据产品的开发及社区建设,积极活跃于数据产品领域,对数据库及大数据领域各个产品具有丰富经验,目前在京东商城主导弹性数据库研发及推广使用。


>>>>

写在前面


本来要进行HDFS集群的KMS部署的,其实这是很成熟的技术,在网上找了很长,竟然没有靠谱的教程,不是错误的,就是单机自己玩模式的,不知从什么时候起,有个先驱写了一篇错误的教程,然后这些抄袭者们就开始各种复制粘贴,也不去验证对错,着实让我悲愤。


一气之下,决定自己参照官方文档进行部署,然后将部署的过程和结果写下来,来祭奠那些抄袭者们。


本文章仅代表作者自己的实际部署及操作结果,有任何疏漏,望指正。


>>>>

KMS说明


Hadoop KMS是一个基于 Hadoop的加密管理服务端。Client是一个KeyProvider的实现,使用KMS HTTP REST API与KMS交互。


KMS和它的客户端内置安全验证并且它们支持HTTP SPNEGO Kerberos 身份验证和HTTPS安全转换。


由于本次只是为了说明如何部署KMS,所以本文档就采用simple的身份认证模式,没有采用Kerberos。需要采用Kerberos进行身份认证的同仁可以参考官方文档进行Kerberos认证模式部署。


KMS实际上是一个Java Web应用程序,运行在与Hadoop发行版绑定在一起的预先配置好的Tomcat服务器上。


注意:Kms是一个密钥管理服务器,它并不负责加密文件。


通过KMS可以实现用户无感知的HDFS端到端的透明加密。配置完kms后,用户往hdfs上存储数据的时候,无需用户做任何程序代码的更改(通过调用KeyProvider API ,在数据存入到HDFS上面的时候进行数据加密,解密的过程一样)。数据加密和解密由客户端自动完成。


>>>>

环境说明


软件名称

软件版本

Hadoop

Hadoop  2.6.1

JDK

1.8.0_92

操作系统

CentOS  release 6.5 (Final)

Hadoop超级用户

hadp


>>>>

服务器列表


HostName

角色

说明

BJ-PRESTO-TEST-100080.lvxin.com

192.168.100.80

NameNode,kms


BJ-PRESTO-TEST-100081.lvxin.com

192.168.100.81

NameNode


BJ-PRESTO-TEST-100082.lvxin.com

192.168.100.82

DataNode


BJ-PRESTO-TEST-100083.lvxin.com

192.168.100.83

DataNode


BJ-PRESTO-TEST-100084.lvxin.com

192.168.100.84

DataNode


BJ-PRESTO-TEST-100085.lvxin.com

192.168.100.85

DataNode


BJ-PRESTO-TEST-100086.lvxin.com

192.168.100.86

DataNode


BJ-PRESTO-TEST-100087.lvxin.com

192.168.100.87

DataNode


BJ-PRESTO-TEST-100088.lvxin.com

192.168.100.88

DataNode


BJ-PRESTO-TEST-100089.lvxin.com

192.168.100.89

DataNode


BJ-PRESTO-TEST-100090.lvxin.com

192.168.100.90

DataNode


BJ-PRESTO-TEST-100091.lvxin.com

192.168.100.91

DataNode


BJ-PRESTO-TEST-100092.lvxin.com

192.168.100.92

DataNode


BJ-PRESTO-TEST-100093.lvxin.com

192.168.100.93

HDFS Client



>>>>

配置说明


完成KMS的配置共涉及到5个文件的修改,分别是:core-site.xml、hdfs-site.xml、kms-site.xml、kms-env.sh、kms-acls.xml。下面对每个文件的修改内容进行依次说明:

>>>>

core-site.xml


在所有的NameNode和DataNode上修改该配置文件,在该配置文件上增加如下配置内容:

<property>

<name>hadoop.security.key.provider.path</name>

<value>kms://http@BJ-PRESTO-TEST-100080.lvxin.com:16000/kms</value>

</property>

>>>>

hdfs-site.xml


在所有的NameNode和DataNode上修改该配置文件,在该配置文件上增加如下配置内容:

<property>

<name>dfs.encryption.key.provider.uri  </name>

<value>kms://http@BJ-PRESTO-TEST-177080.jd.com:16000/kms</value>

</property>


注意】上面两个配置文件修改完毕后,需要重启HDFS服务,后面的修改仅重启kms服务即可。

>>>>

kms-site.xml


由于Kms服务只需要在NameNode:BJ-PRESTO-TEST-100080.lvxin.com上启动,因此仅需要修改BJ-PRESTO-TEST-100080.lvxin.com服务器上的该配置文件,其他服务器上的该配置文件不需要修改。在该配置文件上增加如下配置内容:

<!-- KMS Backend KeyProvider -->

<property>

<name>hadoop.kms.key.provider.uri</name>

<value>jceks://file@/${user.home}/kms.keystore</value>

</property>

<property>

<name>hadoop.security.keystore.java-keystore-provider.password-file</name>

<value>kms.keystore.password</value>                    

 <!--秘钥密码存储文件,该文件需要手动创建,并且放在kms的tomcat下classes文件夹下-->

</property>

<!—本文关注与kms部署这里选择simple就可以了-->

 <property>

     <name>hadoop.kms.authentication.type</name>

     <value>simple</value>

   </property>

</configuration>


使用keytool生成的秘钥密码是123456  将密码直接写入到kms.keystore.password文件:

echo   123456  > ${HADOOP_HOME}/share/hadoop/kms/tomcat/webapps/kms/WEB-INF/classes/kms.keystore.password


>>>>

kms-env.sh


由于Kms服务仅需要在NameNode:BJ-PRESTO-TEST-100080.lvxin.com上启动,因此仅需要修改BJ-PRESTO-TEST-100080.lvxin.com服务器上的该配置文件,其他服务器上的该配置文件不需要修改。在该配置文件上增加如下配置内容:

export KMS_HOME=${HADOOP_HOME}

export KMS_LOG=${KMS_HOME}/logs/kms

export KMS_HTTP_PORT=16000

export KMS_ADMIN_PORT=16001


】这里可以通过kms-env.sh这个脚本来设置。也可以直接设置在~/.bashrc中。

>>>>

kms-acls.xml


由于Kms服务仅需要在NameNode:BJ-PRESTO-TEST-100080.lvxin.com上启动,因此仅需要修改BJ-PRESTO-TEST-100080.lvxin.com服务器上的该配置文件:kms-acls.xml,其他服务器上的该配置文件不需要修改。该文件可能会随时改变,主要用来配置key与用户的对应关系。


根据测试需要,我们添加两个用户:user_a和user_b,新增user_a_key对应user_a,user_b_key对应user_b,因此需要在配置文件:$HADOOP_CONF_DIR/kms-acls.xml中添加如下配置项

<property>

      <name>key.acl.user_a_key.DECRYPT_EEK</name>

           <value>user_a</value>

 </property>

 <property>

       <name>key.acl.user_b_key.DECRYPT_EEK</name>

          <value>user_b</value>

 </property>


>>>>

创建密钥


只需要在NameNode:BJ-PRESTO-TEST-100080.lvxin.com 上创建密钥,创建密钥步骤如下:


Step1:创建user_a_key

[hadp@BJ-PRESTO-TEST-100080 hadoop]$  keytool -genkey  -alias 'user_a_key'

输入密钥库口令:

##这里输入的口令是:123456

您的名字与姓氏是什么?

   [Unknown]:  lvxin

您的组织单位名称是什么?

   [Unknown]:  jd

您的组织名称是什么?

   [Unknown]:  jd

您所在的城市或区域名称是什么?

   [Unknown]:  bj

您所在的省/市/自治区名称是什么?

   [Unknown]:  bj

该单位的双字母国家/地区代码是什么?

   [Unknown]:  cn

CN=lvxin, OU=jd, O=jd, L=bj, ST=bj, C=cn是否正确?

  [否]:  是

 

输入 <user_a_key> 的密钥口令

      (如果和密钥库口令相同, 按回车):

#这里输入的口令与第一次输入的口令一样,都是:123456

再次输入新口令:

[hadp@BJ-PRESTO-TEST-100080 hadoop]$


Step2:以相同方式创建user_b_key

[hadp@BJ-PRESTO-TEST-100080 hadoop]$  keytool -genkey  -alias 'user_b_key'

输入密钥库口令:

您的名字与姓氏是什么?

   [Unknown]:  lvxin

您的组织单位名称是什么?

   [Unknown]:  jd

您的组织名称是什么?

   [Unknown]:  jd

您所在的城市或区域名称是什么?

   [Unknown]:  bj

您所在的省/市/自治区名称是什么?

   [Unknown]:  bj

该单位的双字母国家/地区代码是什么?

   [Unknown]:  cn

CN=lvxin, OU=jd, O=jd, L=bj, ST=bj, C=cn是否正确?

  [否]:  是

 

输入 <user_b_key> 的密钥口令

      #(如果和密钥库口令相同, 按回车):

[hadp@BJ-PRESTO-TEST-100080 hadoop]$


Step3:查看刚刚创建完成的密钥

[hadp@BJ-PRESTO-TEST-100080 hadoop]$  keytool -list

输入密钥库口令:  #这里输入上面的密码:123456

 

密钥库类型: JKS

密钥库提供方: SUN

 

您的密钥库包含 2 个条目

 

user_a_key, 2018-7-3, PrivateKeyEntry,

证书指纹 (SHA1):  47:5A:5B:09:6F:50:65:8D:9B:5F:5A:E5:48:88:1C:86:63:BB:10:39

user_b_key, 2018-7-3, PrivateKeyEntry,

证书指纹 (SHA1): 71:75:E8:7F:90:57:BE:E1:CD:03:63:D1:7F:28:5D:51:BC:5D:D5:8C

[hadp@BJ-PRESTO-TEST-100080 hadoop]$


>>>>

启动hdfs


[hadp@BJ-PRESTO-TEST-100080  hadoop]$start-dfs.sh


>>>>

启动KMS


在namenode:BJ-PRESTO-TEST-100080.lvxin.com上执行命令:kms.sh start,此时会启动一个进程:Bootstrap,如下所示:

[hadp@BJ-PRESTO-TEST-100080 ~]$ kms.sh  start

   setting KMS_HOME=${HADOOP_HOME}

   setting KMS_LOG=${KMS_HOME}/logs/kms

   setting KMS_HTTP_PORT=16000

   setting KMS_ADMIN_PORT=16001

Using CATALINA_BASE:    /software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat

Using CATALINA_HOME:    /software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat

Using CATALINA_TMPDIR:  /software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/temp

Using JRE_HOME:        /software/servers/jdk1.8.0_92

Using CLASSPATH:       /software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/bin/bootstrap.jar

Using CATALINA_PID:    /tmp/kms.pid

Existing PID file found during start.

Removing/clearing stale PID file.

[hadp@BJ-PRESTO-TEST-100080 ~]$ ps -ef|grep  -i "Bootstrap"

hadp      14414     1 99 16:12 pts/1    00:00:12  /software/servers/jdk1.8.0_92/bin/java  -Djava.util.logging.config.file=/software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/conf/logging.properties  -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager  -Dkms.home.dir=/software/servers/hadoop-2.6.1  -Dkms.config.dir=/software/servers/hadoop-2.6.1/etc/hadoop  -Dkms.log.dir=/software/servers/hadoop-2.6.1/logs/kms  -Dkms.temp.dir=/software/servers/hadoop-2.6.1/temp -Dkms.admin.port=16001  -Dkms.http.port=16000 -Dkms.max.threads=1000  -Dkms.ssl.keystore.file=/home/hadp/.keystore -Dkms.ssl.keystore.pass=password  -Djava.endorsed.dirs=/software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/endorsed  -classpath /software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/bin/bootstrap.jar  -Dcatalina.base=/software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat  -Dcatalina.home=/software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat  -Djava.io.tmpdir=/software/servers/hadoop-2.6.1/share/hadoop/kms/tomcat/temp  org.apache.catalina.startup.Bootstrap start

hadp      14482 14371  0 16:13 pts/1    00:00:00 grep -i Bootstrap


>>>>

创建加密区


创建key


[hadp@BJ-PRESTO-TEST-100080 ~]$ hadoop key  create user_a_key

user_a_key has been successfully created  with options Options{cipher='AES/CTR/NoPadding', bitLength=128,  description='null', attributes=null}.

KMSClientProvider[http://BJ-PRESTO-TEST-100080.lvxin.com:16000/kms/v1/]  has been updated


】user_a_key为上面通过keytool创建的密钥。


查看key详细信息


[hadp@BJ-PRESTO-TEST-100080 ~]$ hadoop  key list

Listing keys for KeyProvider:  KMSClientProvider[http://BJ-PRESTO-TEST-100080.lvxin.com:16000/kms/v1/]

user_a_key

[hadp@BJ-PRESTO-TEST-100080 ~]$


创建文件目录


[hadp@BJ-PRESTO-TEST-100080 ~]$ hadoop fs  -mkdir /user_a


配置user_a目录权限


[hadp@BJ-PRESTO-TEST-100080 ~]$ hadoop fs -chown  user_a:test_group /user_a


设置/user_a为加密区


[hadp@BJ-PRESTO-TEST-100080 ~]$ hdfs crypto  -createZone -keyName user_a_key -path /user_a

Added encryption zone /user_a

[hadp@BJ-PRESTO-TEST-100080 ~]$


】加密区必须是空目录,几级目录都行,但必须为空。


查看已加密区域


[hadp@BJ-PRESTO-TEST-100080 ~]$ hdfs  crypto -listZones

/user_a   user_a_key

 

[hadp@BJ-PRESTO-TEST-100080 ~]$


】以相同方式创建user_b目录并使用user_b_key对目录加密。


>>>>

验证


我们通过添加三个用户user_a、user_b、user_c对加密结果进行验证。

>>>>

添加测试用户


>>>>

NameNode操作


在两个NameNode:BJ-PRESTO-TEST-100080.lvxin.com和BJ-PRESTO-TEST-100081.lvxin.com上的配置文件:hadoop-policy.xml中添加测试用户访问权限。


常用的限制访问hdfs的权限配置,只需要修改security.client.protocol.acl参数即可,此参数用于控制哪些用户可以访问hdfs,配置为“*”时表示任何用户都不受限制,此参数在配置文件:hadoop-policy.xml中。


Step1:修改配置文件


在NameNode:BJ-PRESTO-TEST-100080.lvxin.com的配置文件:$HADOOP_CONF_DIR/Hadoop-policy.xml中添加如下配置项:

<property>

     <name>security.client.protocol.acl</name>

     <value>hadp,user_a,user_b,user_c</value>

</property>


修改完成后,将该配置文件scp到另一个NameNode:BJ-PRESTO-TEST-100081.lvxin.com上:

[hadp@BJ-PRESTO-TEST-100080 hadoop]$ scp  hadoop-policy.xml 192.168.100.81:$HADOOP_CONF_DIR/

hadoop-policy.xml


Step2:刷新NameNode用户访问权限


在任意一个NameNode上执行以下命令刷新用户权限:

[hadp@BJ-PRESTO-TEST-100080 hadoop]$ hdfs  dfsadmin -refreshServiceAcl

Refresh service acl successful for  BJ-PRESTO-TEST-100080.lvxin.com/192.168.100.80:8020

Refresh service acl successful for  BJ-PRESTO-TEST-100081.lvxin.com/192.168.100.81:8020

[hadp@BJ-PRESTO-TEST-100080 hadoop]$


】如果启动了Federation的功能,每组NameNode都需要执行该命令,并且每组的用户权限都是独立的。

>>>>

Client端操作


在HDFS Client端: BJ-PRESTO-TEST-100093.lvxin.com上有三个用户分别是user_a,user_b,user_c,然后在每个新建用户的~/.bashrc中添加如下配置信息:

[user_a@BJ-PRESTO-TEST-100093 ~]$ cat  ~/.bashrc

# .bashrc

# Source global definitions

if [ -f /etc/bashrc ]; then

      .  /etc/bashrc

fi

export JAVA_HOME=/software/servers/jdk1.8.0_92

export PATH=$JAVA_HOME/bin:$PATH

export  HADOOP_HOME=/software/servers/hadoop-2.6.1

export HADOOP_MAPRED_HOME=$HADOOP_HOME

export HADOOP_COMMON_HOME=$HADOOP_HOME

export HADOOP_HDFS_HOME=$HADOOP_HOME

export HADOOP_YARN_HOME=$HADOOP_HOME

export  HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop

export  HDFS_CONF_DIR=$HADOOP_HOME/etc/hadoop

export  YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop

export  PATH=$PATH:$HADOOP_HOME/bin:$JAVA_HOME/bin:$HADOOP_HOME/sbin:


然后执行source:

[user_a@BJ-PRESTO-TEST-100093 ~]$ source  ~/.bashrc


>>>>

加密验证


在Client端:BJ-PRESTO-TEST-100093.lvxin.com,使用user_c用户上传文件test.txt至user_a及user_b文件目录,均报错没有权限,但是可以上传至/user_c目录:

[root@BJ-PRESTO-TEST-100093 ~]# su - user_c

[user_c@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -put test.txt /user_a

put: Permission denied: user=user_c,  access=WRITE, inode="/user_a":user_a:test_group:drwxr-xr-x

[user_c@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -put test.txt /user_b

put: Permission denied: user=user_c,  access=WRITE, inode="/user_b":user_b:test_group:drwxr-xr-x

[user_c@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -put test.txt /user_c

user_c@BJ-PRESTO-TEST-100093 ~]$


切换至user_a用户,将test.txt文件上传至/user_a文件夹,切换至user_b用户,将test.txt文件上传至/user_b文件夹。


使用user_a用户读取/user_a/test.txt文件可正常显示,读取/user_b/test.txt文件提示没有权限:

[root@BJ-PRESTO-TEST-100093 ~]# su - user_a

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -put test.txt /user_a

[user_a@BJ-PRESTO-TEST-100093 ~]$ logout

[root@BJ-PRESTO-TEST-100093 ~]# su - user_b

[user_b@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs -put  test.txt /user_b

[user_b@BJ-PRESTO-TEST-100093 ~]$ logout

[root@BJ-PRESTO-TEST-100093 ~]# su - user_a

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -cat /user_a/test.txt

this is content!!!!

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -cat /user_b/test.txt

cat: User [user_a] is not authorized to  perform [DECRYPT_EEK] on key with ACL name [user_b_key]!!

[user_a@BJ-PRESTO-TEST-100093 ~]$


由于在hdfs中所有的文件都会在目录/.reserved下面存储一份原始文件,因此我们查看加密区下文件/user_a/test.txt在/.reserved下面的原始文件,从而验证该文件有没有被加密:

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -cat /.reserved/raw/user_a/test.txt

W�F(�9����N!�r'�:[user_a@BJ-PRESTO-TEST-100093  ~]$


由于原始文件没有在加密区中,但是文件的内容是经过kms加密后的内容,所以读取原始文件的过程没有kms解密阶段,所以读出来的内容是密文。


然后查看/user_c/test.txt对应的的原始文件:

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -cat /.reserved/raw/user_c/test.txt

this is content!!!!

[user_a@BJ-PRESTO-TEST-100093 ~]$

 


由于/user_c不是加密区,所以文件/user_c/test.txt的内容是没有经过加密的因此该目录下的所有文件都是非加密的,因此读到的原始文件的内容就是明文。


因此,可以证明加密区的文件确实经过了kms的透明加密


>>>>

总结


通过KMS可以实现hdfs文件的透明加密,并且验证通过。


>>>>

问题及解决方案


1、Access denied for user user_a. Superuser privilege is require


在使用user_a查看/.reserved/raw下面的文件的时候,报出错误,说没有权限,现象如下:


[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs -cat /.reserved/raw/user_a/test.txt

cat: Access denied for user user_a. Superuser privilege is required

[user_a@BJ-PRESTO-TEST-100093 ~]$


原因:user_a,user_b,user_c不是超级用户权限。由于hdfs中没有配置超级用户组,因此hdfs的的默认超级用户组就是:supergroup,由于hdfs的用户权限验证过程是:根据客户端的用户名,验证在NameNode上的操作系统中该用户名所属的用户组是否为超级用户:supergroup,若是supergroup,则该用户就是超级用户;否则就不是超级用户。因此需要在两个NameNode上都执行下面的操作可以解决该问题:

useradd -g supergroup user_a

useradd -g supergroup user_b

useradd -g supergroup user_c


2、can't be moved from an encryption zone


当从加密区删除透明加密过的文件时报出此错误,如下:

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -rm /user_a/test.txt

18/07/04 13:04:25 INFO fs.TrashPolicyDefault:  Namenode trash configuration: Deletion interval = 1440 minutes, Emptier  interval = 0 minutes.

rm: Failed to move to trash:  hdfs://ns1/user_a/test.txt: /user_a/test.txt can't be moved from an  encryption zone.


解决:


删除的时候加上-skipTrash。

[user_a@BJ-PRESTO-TEST-100093 ~]$ hdfs dfs  -rm -skipTrash /user_a/test.txt

Deleted /user_a/test.txt

---------------------END---------------------

下面的内容同样精彩

点击图片即可阅读


以上是关于关于HDFS-KMS集群化部署教程,你以前看的都错了!的主要内容,如果未能解决你的问题,请参考以下文章

redis集群搭建

七、rancher搭建Mongodb集群化部署:

六、rancher搭建rabbitmq集群化部署

必须收藏别再乱找TiDB 集群部署教程了,这篇保姆级教程来帮你!!| 博学谷狂野架构师

教程|使用Istio在Kubernetes集群中实现金丝雀部署

redis官方文档学习之——分区