原生 KMS 模型
概覽
適用場景
- 將 DataNode 上的數(shù)據(jù) block 加密存放,這樣即使惡意用戶通過某種方式繞過了權(quán)限控制,或直接訪問了 DataNode,獲取了其它用戶的數(shù)據(jù) block,也看不到這些 block 的真實(shí)內(nèi)容。
- 對 HDFS 的讀寫性能會有一定的降低,但應(yīng)該不會太嚴(yán)重(未測試),HDFS 優(yōu)先使用 native 的 libcrypto.so 完成加解密(默認(rèn)算法 AES-CTR,支持128位 AES 加密),新版本的 libcrypto.so 都支持 AES-NI 指令集,在 CPU 層面有相關(guān)的 AES 指令,為了使用 AES-NI,要求在所有的客戶端上(包括所有的 map/reduce 客戶端),都安裝最新的 libcrypto.so
幾個關(guān)鍵點(diǎn)
- HDFS client 向 DataNode 寫入/讀取數(shù)據(jù)時,數(shù)據(jù)的加密/解密過程都在 client 端的 JVM 中完成,HDFS 集群本身不感知這個過程。
- HDFS client 如果需要開啟加密傳輸,只需要在 core-site.xml 中配置幾個選項(xiàng)即可,不需要修改現(xiàn)有的業(yè)務(wù)代碼,整個加解密功能是作為插件的形式存在的。
- KMS 集群獨(dú)立于 HDFS 集群之外,可以為多個 HDFS 集群所共享。
- KMS 集群作為 HDFS 之外的獨(dú)立系統(tǒng),它的認(rèn)證機(jī)制和 HDFS 是分開的。
幾個關(guān)鍵概念
EZ(Encrpytion Zone)
加密區(qū)域,HDFS 可以將一個現(xiàn)有的空目錄做成 EZ,在該目錄的 xattr 中會保存一些相關(guān)的加密信息(例如該 EZ 使用的 EZ key 名字、使用的加密算法、使用的協(xié)議版本等等),該EZ 下的所有文件在寫入時都將被自動加密,讀取時都將被自動解密,如前所述,加解密過程都在 client 端的 jvm 中進(jìn)行。EZ Key(Encryption Zone key)
每個 Encrytion Zone 都關(guān)聯(lián)著一個 root key,這是在該 EZ 創(chuàng)建時,由 HDFS 管理員指定關(guān)聯(lián)起來的,EZ Key 由 KMS 產(chǎn)生、存儲和管理,保存在 KMS 的后備存儲中。DEK(Data Encrpytion Key)
數(shù)據(jù)加密 key,EZ 中的每個文件都有自己特定的 DEK,client 讀寫該文件時,需要用該文件的 DEK 進(jìn)行加解密操作。-
EDEK(Encrypted Data Encryption Key)
文件的 DEK 過于敏感,不會保存在任何地方,而是用他所從屬的 EZ 的 EZ Key 進(jìn)行加密,生成一個 EDEK,然后將此 EDEK 保存在該文件的擴(kuò)展屬性中,一個 EDEK 的內(nèi)容主要包括該 EDEK 加密時使用的 EZ Key 名字,EZ Key 版本(同一個 EZ key 可以有多個版本)、加密后的密文、加密使用的初始向量等等,后面需要解密 EDEK 時,需要將這些信息發(fā)送給 KMS Server 進(jìn)行解密處理。- client 創(chuàng)建新文件時,NameNode 會調(diào)用 KMS 接口,為該文件產(chǎn)生一個唯一的 EDEK,并將其保存在該文件的擴(kuò)展屬性中。
- client 讀寫文件時,從 NameNode 拿到的都是該文件的 EDEK,client 需要調(diào)用 KMS 接口,將此 EDEK 解密,拿到真正的 DEK,然后使用此 DEK 進(jìn)行加解密操作。
這樣設(shè)計(jì)的目的,是為了保證在 HDFS 中,不保存任何明文密鑰信息,這樣即使惡意用戶攻破了 HDFS,也只能拿到加密后的文件和加密后的 DEK,依然讀不到文件的原始內(nèi)容。
-
幾個 key 的關(guān)系如下
關(guān)鍵操作流程分析
-
一個典型的操作流程如下,其中創(chuàng)建 EZ key、創(chuàng)建 EZ 這兩個操作需要管理員使用 hadoop shell 手動完成:
# 創(chuàng)建 EZ Key
hadoop key create mykey# 創(chuàng)建一個空目錄,把這個空目錄做成 EZ
hdfs dfs -mkdir /zone
hdfs crypto -createZone -keyName mykey -path /zone# 向 EZ 中讀寫文件
hadoop fs -put helloWorld /zone
hadoop fs -cat /zone/helloWorld -
生成 EZ Key
hadoop shell 命令如下:
hadoop key create mykey-
交互流程
- 創(chuàng)建 EZ,使用上一步生成的 EZ Key,將一個 HDFS 中現(xiàn)有的空目錄做成 Encryption Zone
hadoop shell 命令如下:
hdfs crypto -createZone -keyName mykey -path /zone-
交互流程
- 在 EZ 中 創(chuàng)建一個文件
使用正常的 FileSystem API 就可以,和使用普通的 HDFS 沒有區(qū)別。
-
交互流程
- 寫 EZ 中的文件
- 使用正常的 FileSystem API 就可以,和使用普通的 HDFS 沒有區(qū)別。
-
交互流程
- 讀 EZ 中的文件
- 使用正常的 FileSystem API 就可以,和使用普通的 HDFS 沒有區(qū)別。
-
交互流程
原生 KMS存在的幾個問題
EZ key 共享存儲問題
多個 KMS Server 的 backing keystore 并不共享,原生 KMS Server 使用的是 JKS(Java KeyStore) 存儲機(jī)制,EZ Key 都保存在本地文件系統(tǒng)上的 keyStore 文件中,這樣的話,由于 keyStore 文件無法在多個 KMS Server 之間共享,導(dǎo)致掛掉任何一個 Server 后,其它剩余的 KMS Server 都可能讀取不到已有的 EZ Key,導(dǎo)致 EZ key 丟失,進(jìn)而使得加密文件打不開。
原生的這種集群組織方式,更像是一種負(fù)載均衡策略,從這種情況下 KMS 客戶端的名字也能看的出來:LoadBalancingKMSClientProvider。在 Hadoop 的官方文檔中,也強(qiáng)調(diào)了 JKS 這種存儲機(jī)制不能用在生產(chǎn)環(huán)境中。
這個地方需要實(shí)現(xiàn)一個新的分布式的、共享存儲的 backing keyStore 方式,可以使用 zookeeper 或者 ratis 等等。Apache Zookeeper 和 Apache Ratis 的比較:
| Apache zookeeper | Apache ratis | |
|---|---|---|
| 協(xié)議 | ZAB(Zookeeper Atomic Broadcast protocol) | Raft |
| 成熟度 | 項(xiàng)目成熟,使用廣泛 | 項(xiàng)目較新,應(yīng)用不多,目前 OZone 的多副本存儲使用了 Ratis |
| 一致性保證 | 順序一致性,單個 KMS Server 可能讀到較老數(shù)據(jù),但可借助 watch 機(jī)制和 sync 原語實(shí)現(xiàn)強(qiáng)一致性 | 強(qiáng)一致性 |
| 緩存友好度 | 借助 watch 機(jī)制和 sync 原語,可以實(shí)現(xiàn)多個 KMS Server 的緩存一致性 | 沒有主動通知機(jī)制,無法保證多個 KMS Server 的緩存一致 |
目前的話,還是建議:
- 選擇 Zookeeper。
- 使用緩存提高性能,同時借助 Zookeeper 的 watch 機(jī)制和 sync 原語,保證多個 KMS Server 的緩存一致性。
Move 問題
在普通的 HDFS 目錄之間,文件的 move 并不涉及到數(shù)據(jù)的移動,僅僅是元數(shù)據(jù)的修改,速度非???,但在 EZ 和非 EZ 之間、不同 EZ 之間,并不支持 move 操作,只支持 copy 操作,相對較慢,具體如下圖所示:
這其中的原因是,HDFS client 無論 create、append 還是 open 一個文件時,如果發(fā)現(xiàn)這個文件是一個加密文件,那就需要特殊處理,而判斷文件是否加密及隨后正確的加解密處理(寫入需要加密、讀取需要解密),需要來自兩方面的信息:
- 來自該文件本身的 xattr 的信息,即該文件的 EDEK。
- 來自該文件所處的 EZ 根目錄的 xattr 信息,包括加解密算法、協(xié)議版本等等。
既然如此,那么:
- 對整個 EZ(即 EZ 根目錄)做 move 操作是可以的,這可以保證上面所說的兩部分信息都保存下來。
- 將文件從非 EZ move 到 EZ 中,由于該文件本身沒有 EDEK,最終它會被當(dāng)成普通文件讀寫,邏輯上雖然行的通,但這違背了 EZ 本身的語義,因此這種操作不被允許。
- 將文件從 EZ move 到非 EZ 中,由于缺失了 EZ 根目錄的 xattr,也會導(dǎo)致該文件被當(dāng)做普通文件對待,導(dǎo)致無法讀取真實(shí)內(nèi)容,后續(xù)寫入的內(nèi)容也缺失了加密過程使得文件內(nèi)容混亂,因此這種操作也不被允許。
- 在不同 EZ 之間 move 文件也不可以,出于一些安全考慮,HDFS 直接禁止了這種操作。
回收站問題
如前所述,EZ 和 非 EZ 之間的 move 操作是禁止的,換句話說 Trash 功能對 EZ 中的文件是不可用的,而在 TDWHadoop 中,數(shù)據(jù)保護(hù)功能默認(rèn)打開(配置項(xiàng) hadoop.tdw.protect.data.enable 默認(rèn)為 true),客戶端的 delete 操作實(shí)際上最終是一個 move 操作,既然 move 被禁止,那么最終表現(xiàn)為EZ 中的文件無法刪除。
這個問題在 Hadoop 2.8.0 中得到解決,解決思路如下:
- 為每一個 EZ 單獨(dú)配置一個 Trash 目錄,例如,假設(shè) EZ 為 /zone,則為其配置的 Trash 為 /zone/.Trash,用戶 root 對應(yīng)的回收站就是 /zone/.Trash/root/,其它用戶類似。
- 啟用 Trash 功能后,如果整個 EZ 被刪除,那整個 EZ 目錄將被 move 到 /user/$USER/.Trash/ 下,此時和正常目錄的刪除沒有區(qū)別。
- 如果 EZ 中的文件被刪除,則它將被移到 EZ 自己的 Trash 目錄下,例如 root 用戶刪除 /zone/file.txt 將會把 /zone/file.txt move 為 /zone/.Trash/root/Current/zone/file.txt。
- 無論是 EZ 自己的 Trash 目錄,還是 /user/$USER/ 下面的 Trash 目錄,都遵循 HDFS Trash 的 checkpoint 規(guī)則,過期的文件將被自動清理。