KMS是hadoop自2.6.0版本开始自带的一个密钥管理web服务,提供了一系列API来创建,获取和维护密钥。kms与hadoop结合,可以实现hdfs客户端透明的数据加密传输以及细粒度的权限控制。
本文使用Hadoop 2.6.0-cdh5.13.3为例进行kms服务配置启动及hdfs文件加密传输示例。
配置kms
密钥仓库的文件位置和操作密码
1. kms-site.xml (一般默认的就可以了不修改也行)
<property>
<name>hadoop.kms.key.provider.uri</name>
<value>jceks://file@/home/hadoop/kms.jks</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 -->
<value>kms.keystore.password</value>
<description>
If using the JavaKeyStoreProvider, the file name for the keystore password.
</description>
</property>
<property>
<name>dfs.encryption.key.provider.uri</name>
<value>kms://http@app128130:16000/kms</value>
</property>
hadoop.kms.key.provider.uri
KMS管理的密钥以文件形式存在哪个keystore密钥库文件
配置格式
jceks://file@/home/kms/kms.keystore
这个文件会在KMS服务启动后自动生成到file@后的文件位置上
2.core-site.xml
<property>
<name>hadoop.security.key.provider.path</name>
<value>kms://http@app128130:16000/kms</value>
</property>
<property>
<name>dfs.encryption.key.provider.uri</name>
<value>kms://http@app128130:16000/kms</value>
</property>
<property>
<name>hadoop.security.crypto.jceks.key.serialfilter</name>
<value>java.lang.Enum;java.security.KeyRep;java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;com.sun.crypto.provider.SealedObjectForKeyProtector;org.apache.hadoop.crypto.key.**;!*</value>
</property>
3.密码(访问kms密钥库hadoop.kms.key.provider.uri指定的keystore的密码)设置 kms-env.sh和kms.keystore.password
两种方式
密码文件方式
KMS是通过ClassLoader.getResource来加载该文件,所以该配置必须配在KMS Web服务启动对应的conf目录下
echo 123456 > ${HADOOP_HOME}/share/hadoop/kms/tomcat/conf/kms.keystore.password
环境变量方式(优先级高)
kms-env.sh下增加
export HADOOP_KEYSTORE_PASSWORD=123456
4. 重启hdfs和kms (${HADOOP_HOME}/sbin/kms.sh start)
kms默认使用16000端口,要能访问该端口才能使用kms,可以使用hadoop key list -metadata来测试kms服务是否正常。
如果不能获取到结果,首先就要确认kms的服务正常启动了,查看端口是否通,curl或者直接浏览器访问。可以查看kms的api使用http请求测试。
curl http://192.168.128.130:16000/kms/v1/keys/names?user.name=hdfs
5.key的权限控制,kms-acls.xml(无需修改)这个文件默认配置是所有人都可以访问的,建议先不要修改这个文件,等加密分区搞好了,其他验证也都ok了,最最后再来折腾这个kms-acls.xml的权限控制。可以细化配置kms每个key的访问和管理权限允许哪些用户访问,<name>填写的是key和权限,<value>填写是允许的用户名,多个用逗号分隔,填*则代表所有人都有权限,并且支持黑名单,在name中的key后加blacklist则代表是黑名单。因为加密分区必须要有对应的key才能解密,从而间接控制了key对应的加密分区的访问权限。并且这个kms-acls.xml是热加载的。如下图修改控制名称为keydemo的密钥的MANAGEMENT管理权限只赋值给root用户, 当使用hadoop用户删除key时失败,切换root用户后可以删除。同样可以控制密钥的获取权限,从而控制密钥对应的加密分区的操作权限。
权限有这么几类:
MANAGEMENT - createKey, deleteKey, rolloverNewVersion
GENERATE_EEK - generateEncryptedKey, reencryptEncryptedKey, reencryptEncryptedKeys, warmUpEncryptedKeys
DECRYPT_EEK - decryptEncryptedKey(解密EDK)
READ - getKeyVersion, getKeyVersions, getMetadata, getKeysMetadata, getCurrentKey
ALL - all of the above
二、生成根证书并配置到kms-env.sh
tomcat根证书
- keytool -genkey -alias tomcat -keyalg RSA
- 过程中问到"What is your first and last name?"时,必须填写运行KMS Service那台机器的hostname
- keystore的密码,这个密码假定为123456,需要记住,后面配置时需要用到它
- 执行完后,会在用户的home目录下生成.keystore文件
- 显示当前keystore里可用的证书
- keytool -list -v -keystore .keystore -storepass 123456
- 显示当前keystore里可用的证书
在kms-env.sh增加根证书和密码:
export KMS_SSL_KEYSTORE_FILE=/home/hadoop/.keystore
export KMS_SSL_KEYSTORE_PASS=123456
导出crt文件
- 以hadoop用户导出根证书为crt文件
-
keytool -export -alias tomcat -keystore /home/hadoop/.keystore -file /home/hadoop/tomcat.crt -storepass 123456
-
重启kms
三、操作生成key,创建加密分区,验证加密传输
key:
- 生成key(会被保存到kms配置的keystore文件中)
- hadoop key create testkey
- 查看key
- hadoop key list -metadata
- 删除key
- hadoop key delete testkey
加解密
- 创建sz加密区 (key会被kms加密后保存到分区的metadata中,加密分区和非加密分区之间不可以mv,但是可以相互cp。)
- hdfs crypto -createZone -keyName testkey -path /sz
- 查看加密区
- hdfs crypto -listZones
#上传下载文件测试
hdfs dfs -copyFromLocal /本地文件 /sz
hdfs dfs -copyToLocal /sz
通过在hdfs路径前加/.reserved/raw来直接查看加密分区文件发现是乱码,的确是加密了的.
也可以找到对应的block直接查看
四、总结
kms是一个独立的web服务,提供生成密钥,和对密钥的加解密的restAPI。
hadoop客户端处理加密分区文件时,向kms请求获取加密分区的密钥后,由hadoop客户端对文件进行加解密,加解密都在客户端。
hadoop hdfs本身不做加解密,只在创建加密分区时在namenode中加密分区的metadata中记录对应的keyName和加密后的密钥EDEK(kms可以解密EDEK为EDK,EDK可以解密加密分区的文件)。
五、可能遇到的问题:
1.kms启动失败报错找不到密码文件, 密码文件要在tomcat的classpath下,也有说放到conf下,或者直接写入kms-env.sh中优先级更高。
echo 123456 > ${HADOOP_HOME}/share/hadoop/kms/tomcat/conf/kms.keystore.password
echo 123456 > ${HADOOP_HOME}/share/hadoop/kms/tomcat/webapps/kms/WEB-INF/classes/kms.keystore.password
2.Can't cast key for kmskey in keystore file:/home/hadoop/kms.jks to a KeyMetadata. Key may have been added using keytool or some other non-Hadoop method.
这个配置在 hadoop.kms.key.provider.uri的文件不需要自己生成,kms启动时会自动生成,删掉kms.jks和.kms.jks.cc重启kms即可。
3.重启kms服务后,无法读取kms.jks中的密钥,报错Can't recover key for testkey from keystore file
Caused by: java.security.UnrecoverableKeyException: Rejected by the jceks.key.serialFilter or jdk.serialFilter property
at com.sun.crypto.provider.KeyProtector.unseal(KeyProtector.java:399)
at com.sun.crypto.provider.JceKeyStore.engineGetKey(JceKeyStore.java:144)
at java.security.KeyStore.getKey(KeyStore.java:1023)
at org.apache.hadoop.crypto.key.JavaKeyStoreProvider.getMetadata(JavaKeyStoreProvider.java:408)
这个是因为jdk8u171之后增加了jceks.key.serialFilter来限制证书的序列化,提高安全性。只要把hadoop相关的类加入允许的配置里就可以了。修改两个地方jdk的jre/lib/security/java.security文件修改jceks.key.serialFilter=java.lang.Enum;java.security.KeyRep;java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;com.sun.crypto.provider.SealedObjectForKeyProtector;org.apache.hadoop.crypto.key.**;!*
再修改hadoop的core-site.xml增加
<property>
<name>hadoop.security.crypto.jceks.key.serialfilter</name>
<value>java.lang.Enum;java.security.KeyRep;java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;com.sun.crypto.provider.SealedObjectForKeyProtector;org.apache.hadoop.crypto.key.**;!*</value>
</property>
这样增加了com.sun.crypto.provider.SealedObjectForKeyProtector;org.apache.hadoop.crypto.key.**; 之后就不会再被Rejected了,就可以在kms重启之后从keystore文件里读取密钥条目了。java.security的配置会覆盖core-site.xml配置。
4.创建加密分区报错 no key provider is available
自己对照源码找原因Java KeyProviderCryptoExtension类代码示例 - 纯净天空
应该是配置缺少或者是没有给kms的tomcat生成根证书
hadoop key list -metadata -provider kms://http@localhost:16000/kms
我是直接按这个操作解决的KMS密钥管理服务(Hadoop)_weixin_34377065的博客-CSDN博客
附录:
keytool
- 生成证书
-
keytool -genkey -alias kmskey -keysize 2048 -keyalg RSA -validity 3650 -keystore kms.jks -storetype JKS -dname "CN=localhost, OU=localhost, O=localhost, L=FG, ST=AH, C=CN" -keypass 123456 -storepass 123456
keytool -genkey -alias kmskey
-genkey参数
- - genkeypair:生成公私钥对条目,私钥不可见,公钥会以证书格式保存在keystore中。
- - alias: 指定别名,区分不同条目,默认mykey
- - keysize: 密钥长度
- - keyalg: 公私钥算法
- - validity: 证书过期时间
- - keystore: 指定存储密钥库,若不存在会创建,若指定则在当前文件夹下生成。默认密钥库为用户目录下.keystore文件
- - storetype: 密钥库类型 JKS PKCS等
-
- 查看keystore中证书条目列表
- keytool -list -v -keystore kms.jks -storepass 123456
- 删除密钥库中的条目
- keytool -delete -alias kmsjks -keystore kms.jks -storepass 123456
restApi直接查询key的信息
参考:
Hadoopkms加密 - IT晓白 - 博客园
官网Hadoop KMS – Hadoop Key Management Server (KMS) - Documentation Sets
HDFS数据加密空间--Encryption zone_走在前往架构师的路上-CSDN博客
HDFS加密存储(HDP、Ranger、Ranger KMS实现)_暗焰之珩的博客-CSDN博客_hdfs kms
快速搭建hadoop KMS开发集成环境 - 2012 - 博客园 (kms的restApi)
Java KeyProviderCryptoExtension类代码示例 - 纯净天空
Java™ SE Development Kit 8, Update 171 Release Notes
What is the cause of "java.security.UnrecoverableKeyException: Rejected by the jceks.key.serialFilter or jdk.serialFilter property"? - Stack Overflow
“java.security.UnrecoverableKeyException:由jceks.key.serialFilter或jdk.serialFilter属性拒绝”的原因是什么? - 堆栈内存溢出