一、背景介绍
-
越来越多的用户关注安全问题,都在寻找一种有效的,方便的加密方式。hadoop提供了几种不同形式的加密,最底层的加密,加密所有节点数据,有效地保护了数据,但是却缺乏更细粒度的加密;
-
kms 透明加密可以做到更细粒度的加密;
加密可以在不同的层级进行,包括软件/软件堆栈,选择不同的加密层级各有优缺点- 应用程序级加密。这是最安全、最灵活的方法。应用程序最终控制是什么加密,可以准确地反映用户的需求。然而,编写应用程序这样做是很难的。这也不是一个选择为客户现有的应用程序不支持加密。
- 数据库级加密。类似的应用程序级加密的属性。大多数数据库厂商提供某种形式的加密。然而,可能有性能问题。一个例子是,索引不能加密。
- 文件系统级进行加密。该选项提供了高性能、应用程序透明,通常容易部署。但是,它无法模型应用程序级别的一些政策。例如,多租户应用程序基于最终用户可能想要加密。一个数据库可能需要不同的加密设置每一列存储在单个文件中。
- 磁盘级别加密。容易部署和高性能,但也很不灵活。
hdfs处于数据库加密层和文件系统加密层之间。能有效地防止对文件系统的攻击,因为他的存储都是加密的。对于每个用户可以有不同的加密区
-
体系结构
- Kms 有一个加密区 这个特殊的区域 对于能够进行操作他的用户 提供有写入和读取的权限。当加密区被创建的时候,每个加密区有唯一的秘钥, data encryption key (DEK), 秘钥dek不由hdfs直接控制,但是hdfs控制 encrypted data encryption key (EDEK) ,被加密的秘钥;由客户端解密 EDEK;
kms server kms 服务器
kms client kms 客户端
kms zone kms 加密区
- Kms 有一个加密区 这个特殊的区域 对于能够进行操作他的用户 提供有写入和读取的权限。当加密区被创建的时候,每个加密区有唯一的秘钥, data encryption key (DEK), 秘钥dek不由hdfs直接控制,但是hdfs控制 encrypted data encryption key (EDEK) ,被加密的秘钥;由客户端解密 EDEK;
-
简单使用场景:
-
前提场景:
-
人物:管理员s、用户a、用户b、外部无权人c
- 管理员在hdfs上创建了一个目录A,让a来存取文件,而b无权查看(通过hdfs权限控制,和Linux上的文件权限控制一样)
- 管理员在hdfs上创建了一个目录B,让b来存取文件,而A无权查看
-
没有透明加密的时候:
正常情况下,通过hdfs客户端,a只能查看A里的文件;b只能查看B的文件。
但a(或者c)如果通过某种方式,进入hdfs的数据块存储区域,直接把B文件的数据块取出来,那么B文件就泄密了 -
有透明加密的时候:
管理员用密钥1把A目录设为“加密区”,用密钥2把B目录也设为“加密区”
当a获取了B文件的数据块,由于他他没有A文件的使用权限,kms不会返回给他密钥2。
当c获取了B文件的数据块,由于他也没有A文件的使用权,kms也不会返回给他密钥2。
此时就保证了文件B的安全。
注:加密的是数据块,就是存在磁盘上的东西,在hdfs上直接看是自动解密了,加密的要直接取数据块看。
二、环境准备:
hadoo-2.10.1集群:节点:node1 node2 node3
三、开始配置
1. KMS server 配置
配置KMS:由于kms是跟hadoop结合在一起的,故可以直接到hadoop安装目录下的etc/hadoop去配置kms的配置文件,kms-site.xml, kms-env.sh,kms-acls.xml
kms-site.xml
用默认配置即可,但是要知道每一项的含义,后面方便去做对应的事情.
hadoop.kms.key.provider.uri
可以直接理解成我们生成 key 后存储到的文件位置。后面我们会根据 java 秘钥生成器 keytool -genkey 去生成一个 key.hadoop.security.keystore.java-keystore-provider.password-file
上面那个文件生成的时候,需要指定一个密码,而这个密码,需要 kms 从一个文件中读取。那么这个文件的路径就是这个属性的 value.- KMS Cache
hadoop.kms.cache.enable
hadoop.kms.cache.timeout.ms
hadoop.kms.current.key.cache.timeout.ms
- 配置缓冲区,这样的话,当获取 getCurrentKey() and getKeyVersion() andgetMetadata() 的时候 不用每次去访问 keystore 只用从缓存中读取就可以。当然,秘钥更新或者被删除的时候,会自动更新。
<property>
<name>hadoop.kms.key.provider.uri</name>
<value>jceks://file@/${user.home}/kms.keystore</value>
<description>
URI of the backing KeyProvider for the KMS.
</description>
</property>
<property>
<name>hadoop.security.keystore.java-keystore-provider.password-file</name>
<value>kms.keystore.password</value>
<description>
If using the JavaKeyStoreProvider, the file name for the keystore password.
</description>
</property>
<!-- KMS Cache -->
<property>
<name>hadoop.kms.cache.enable</name>
<value>true</value>
<description>
Whether the KMS will act as a cache for the backing KeyProvider.
When the cache is enabled, operations like getKeyVersion, getMetadata,
and getCurrentKey will sometimes return cached data without consulting
the backing KeyProvider. Cached values are flushed when keys are deleted
or modified.
</description>
</property>
<property>
<name>hadoop.kms.cache.timeout.ms</name>
<value>600000</value>
<description>
Expiry time for the KMS key version and key metadata cache, in
milliseconds. This affects getKeyVersion and getMetadata.
</description>
</property>
<property>
<name>hadoop.kms.current.key.cache.timeout.ms</name>
<value>30000</value>
<description>
Expiry time for the KMS current key cache, in milliseconds. This
affects getCurrentKey operations.
</description>
</property>
2. 使用 keytool 生成秘钥
(py38_1.12) lzq@node1 ~ keytool -genkey -alias 'key1';
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
[Unknown]: zhiqiang li
您的组织单位名称是什么?
[Unknown]: xxx
您的组织名称是什么?
[Unknown]: xxx
您所在的城市或区域名称是什么?
[Unknown]: hangzhou
您所在的省/市/自治区名称是什么?
[Unknown]: zhejiang
该单位的双字母国家/地区代码是什么?
[Unknown]: china
CN=zhiqiang li, OU=xxx, O=xxx, L=hangzhou, ST=zhejiang, C=china是否正确?
[否]: yes
您的名字与姓氏是什么?
[zhiqiang li]: zhiqiang li
您的组织单位名称是什么?
[xxx]: xxx
您的组织名称是什么?
[xxx]: xxx
您所在的城市或区域名称是什么?
[hangzhou]: hangzhou
您所在的省/市/自治区名称是什么?
[zhejiang]: zhejiang
该单位的双字母国家/地区代码是什么?
[china]: china
CN=zhiqiang li, OU=xxx, O=xxx, L=hangzhou, ST=zhejiang, C=china是否正确?
[否]: 是
输入 <key1> 的密钥口令
(如果和密钥库口令相同, 按回车):
再次输入新口令:
Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore /home/lzq/.keystore -destkeystore /home/lzq/.keystore -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
密码默认位置是在你的家目录下,默认名为.keystore,为隐藏文件,建议使用默认的方式创建密钥.
-
注:有两个密码需要设定,这里我都设置为了123456,首先输入第一个密码,其他的随意写,直到出现no的时候写yes,然后输入第二个密码。
-
注:密码默认位置是在你的家目录下,默认名为.keystore,为隐藏文件,建议使用默认的方式创建密钥。
-
注:其他方式创建密钥:(指定kms密钥文件名和位置)
keytool-genkey -alias ‘kmskey’ -keystore /kms.jks -dname “CN=localhost,OU=localhost, O=localhost, L=SH, ST=SH, C=CN” -keypass 123456 -storepass123456 -validity 180 -
注: 生成了keystore后,这个keystore就可以作为kms的密钥存储数据库了,由于这个keystore有访问密码(比如上面所设置的storepass :123456),这个密码是要告诉kms的,否则kms访问不了这个keystore,kms通过读取一个文本文件来获取密码。所以我们需要创建一个文本文件,在里面写上keystore访问密码。
上面
输入密钥库口令:
再次输入新口令:
这两个输入的密码 是 123456 。 放入到一个文件里面 kms.keystore.password
然后把文件放到 kms 服务的 classes 目录下面
这个文件名和上面 kms-site.xml 里面的 hadoop.security.keystore.java-keystore-provider.password-file
的 value 相对应.
3. 配置 kms-env.sh
# $HADOOP_HOME 需要提前搭建 hadoop 集群的时候就配置好
export KMS_HOME=$HADOOP_HOME
export KMS_LOG=${KMS_HOME}/logs/kms
export KMS_HTTP_PORT=16000
export KMS_ADMIN_PORT=16001
4. 启动 KMS 服务
kms.sh start
# 如果没配置环境变量,需要去 Hadoop home 里面 sbin 下面的脚本去启动。
此时通过jps能看到Bootstrap进程,表示成功启动。
可以在配置的目录里面查看到日志
5. 配置客户端的 core-site.xml hdfs-site.xml
core-site.xml
node1 ip 是 192.168.178.101, 也可以配置成 kms://http@node116000/kms
<!--KMS 配置-->
<property>
<name>hadoop.security.key.provider.path</name>
<value>kms://http@192.168.178.101:16000/kms</value>
</property>
hdfs-site.xml
<!--KMS 配置-->
<property>
<name>dfs.encryption.key.provider.uri</name>
<value>kms://http@192.168.178.101:16000/kms</value>
</property>
6. 重新启动namenode与datanode
7. 验证一下 KMS 服务
-
创建之前查看一下
hadoop key create key1
-
创建之后查看一下
hadoop key list
-
创建一个加密分区
hdfs dfs -mkdir /crypto hdfs crypto -createZone -keyName key1 -path /crypto
如果报错 RemoteException: Can’t create an encryption zone for /crypto since no key provider is available.
那么是因为可能你没配置对下面的属性,它在 core-site.xml
<property>
<name>hadoop.security.key.provider.path</name>
<value>kms://http@node1:16000/kms</value>
</property>
然后重启一下 HDFS 就可以了。
我是因为我命名配置了这个属性,但是重启还是不行。 后来源码 debug 到报错的地方,突然又好了。 后面也不再出问题了。
很奇怪。就是一直报错 since no key provider is available
源码在下面
上面异常 110 行的异常就是命令行的报错。
provider 的创建就是这个代码
private static String keyProviderUriKeyName =
CommonConfigurationKeysPublic.HADOOP_SECURITY_KEY_PROVIDER_PATH;
public static final String HADOOP_SECURITY_KEY_PROVIDER_PATH =
"hadoop.security.key.provider.path";
上面的 keyProviderUriKeyName 其实就是我们配置的 hadoop.security.key.provider.path 属性。
四、验证
一个文件上传到 HDFS 上两个不同文件夹,一个加密 /crypto, 一个不假面 /no_crypto 。
通过命令找到 datanode 上面的文件块,
hdfs fsck /crypto/crypto.txt -files -blocks -locations
hdfs fsck /no_crypto/crypto.txt -files -blocks -locations
我们可以看到, 加密区域的文件无法查看,不加密区域的文件直接用 linux cat 命令就可以看到内容。
直接通过命令查看文件内容
hdfs dfs -ls /.reserved/raw
hdfs dfs -cat /.reserved/raw/crypto/crypto.txt
hdfs dfs -cat /.reserved/raw/no_crypto/crypto.txt