也說Hadoop敏感信息加密方案的嘗試(下) 原

前面講到了AES對稱加密的兩個不同方案,一是Hive表存儲祕鑰,一是用KMS來存儲祕鑰。在這兩個大的分類下,又各自有兩種不同的方案,每種方案的嘗試都是因爲踩到了坑,所以纔不得不換一種姿勢。

前文博客見 也說Hadoop敏感信息加密方式的嘗試(上)

#KMS祕鑰存儲方案 關於KMS的講解和配置,可見博文 KMS祕鑰管理服務(Hadoop)。這裏只講其在加密方案中的應用。

##方案一:直接從KMS獲取祕鑰 這裏利用KMS存儲字段加密祕鑰,在每個Hive或Spark任務提交到集羣的時候,集羣都會進行提交者的Kerberos身份認證,我們就在Hive/Spark UDF裏利用當前Kerberos身份來獲取祕鑰。

看起來好像很順理成章,事實證明 測試環境 下,在Spark UDF裏確實可以順利利用提交賬戶獲取到該賬戶所能獲取到的祕鑰,進行字段加解密。但是我們發現在Hive裏,這種方案行不通,觀察KMS的審計日誌發現,所有在beeswaxbeeline裏使用UDF提交的查詢,向KMS請求祕鑰的用戶身份都是hive。也就是說Hive裏的代理用戶身份設置並沒有生效,用戶向Hive提交的任務,Hive最後還是以hive的身份鑑權和執行!

這裏的情況讓我有點驚愕,因爲我們提交的所有hive任務,其實都是經過了鑑權的,不可能是以hive身份來鑑權。所以意識到是我們在哪弄錯了,因爲集羣上了Sentry,它提供了不止基於UNIX文件系統的鑑權,所以集羣上的HiveServer2是與Sentry結合的。所有經過HiveServer2的任務都要先經過Sentry的鑑權,鑑權通過Hive纔會往下執行。

而在安裝Sentry的時候,是明確要求Hive關閉impersonation的,也即hive.server2.enable.doAs = false,這個功能就是Hive的代理用戶功能,關掉它之後,所有提交給Hive的任務都將以hive的身份執行!所以成也Sentry敗也Sentry,我們在Hive UDF裏拿到的當前認證用戶就是hive,拿不到實際提交用戶的Kerberos認證!

由於在Hive UDF裏拿不到提交用戶的Kerberos認證,所以這一方案就被KO了

##方案二:通過MapredContext獲取提交用戶名 這是對上一個方案的延伸,由於在Hive UDF裏拿不到當前提交用戶的Kerberos認證,所以只能繞一下。在GenericUDF裏有個函數 public void configure(MapredContext context) ,通過傳入的context,我們可以用context.getConf()來拿到Map函數的Configuration,然後通過hive.sentry.subject.name配置,拿到提交的真正賬戶名。

在GenericUDF裏,如果configure函數執行的話,它在initialize函數之前執行。通過從configure函數裏拿到真正提交賬戶,我們在initialize函數裏向KMS取祕鑰就可以利用真正提交賬戶構造Kerberos認證。

但是configure函數的執行是有條件的,只有Hive的底層引擎是MapReduce時,這個函數纔有可能執行,否則它不會執行。所以底層引擎不是它的時候,這種方式是行不通的。

這種方案裏我們將授權人員的keytab文件進行AES加密,放入jar包裏,將keytab祕鑰存於KMS,在Hive UDF裏先以hive身份請求keytab文件祕鑰,然後解密當前提交賬戶的keytab。之後利用從configure函數裏拿到的提交用戶的身份去訪問KMS,獲取該用戶權限範圍內的字段祕鑰。

方案在這裏看起來似乎也是很OK的,在測試環境裏,很OK的通過了。然後到線上後,發現出了BUG!無論是Spark還是Hive,都只有部分 能進行加解密成功,從日誌上看只加解密了一部分數據就開始報錯,報Kerberos認證失敗

失敗了,那就開始查日誌吧,從日誌上發現,所有Spark認證失敗的都是位於其他節點機上的,而位於提交任務的節點機上的executor則沒有失敗。在Hive上也一樣,位於當前提交任務的節點機上的task是成功的,其他節點機上的是失敗的。爲什麼其他節點機上的任務就會認證失敗呢?

其實是因爲其他節點機上並沒有提交用戶的tgt緩存(kinit得到的),無論Spark還是Hive,在啓動任務的時候,都會拿到HDFSDelegationToken, YarnDelegationToken等經過Kerberos認證後的token,所有的task上都會帶上這些token,而不會帶上提交節點機上的Kerberos UGI信息。task之後通過token來做權限驗證,在token失效時間之內,用token來做驗證就不需要再請求KDC來做認證,直接由token就可以認證當前用戶是否有權限執行相關操作!而在用戶提交的節點機上,cache裏有當前用戶的tgt,在上面跑的task通過UGI認證時,可以直接拿到tgt來認證,自然可以認證通過。而其他節點機上並沒有提交用戶的tgt,認證通過不了,任務自然失敗。

上面的問題之所以在測試環境未曾出現,是因爲測試環境只有唯一一臺節點機上沒有測試賬戶的tgt,而測試的時候,所有的任務剛好都沒有分到那臺機器上,或者分配到上面沒執行成功,任務重試時分配在其他機器上成功了,從而沒被我注意到。

方案走到這一步,又走到絕路了。除非在Hive和Spark提交任務時,像HDFSDelegationToken一樣,先獲取到當前用戶或hive用戶KMS認證後的DelegationToken,然後以插件的形式註冊到任務提交代碼裏,從而分發到執行節點task上去,由執行節點通過token來做KMS認證。但是這一步不說能不能做成,就單做起來就很麻煩,還可能需要改源碼,需要花太大精力,不然官方早就有了完善的加密方案!

或者很猥瑣的在集羣各節點機上佈置需要加解密服務的用戶的tgt,讓節點上的任務在執行時可以拿到認證信息。但是這樣做不是正路子也不方便。

所以方案到這就被掐死了,包括凡是想利用Kerberos認證的方案,到這都走不通!

#Hive表祕鑰存儲方案 Hive表存儲祕鑰理解起來就比較簡單,但是實踐起來也可以分兩種方案。一種對外透明,一種需要用戶傳key,相對猥瑣一點。但是實踐證明,猥瑣的反而好使!

##方案一:在Hive UDF裏獲取Hive祕鑰表的加密key 這種方案屬於對外透明型的,用戶只需傳入加密字段類型和加密字段,就可以完成數據加密,不需要去管祕鑰的存儲。

但在測試環境驗證從Hive UDF取Hive表數據的時候,發現JDBC取Hive表數據在拿到Connection,開始執行SQL語句的時候卡住了,多次嘗試一直卡在那,而後臺HiveServer2的日誌也在get Session後不動。這個問題查了好幾次,沒查到到底爲什麼,猜測是在Hive UDF裏實際是以hive用戶去取Hive表的數據,可能會出現問題,但是並沒有查到到底爲什麼!考慮到在線上環境裏,問題還是回到了Kerberos認證的問題,其他節點機上拿不到認證信息,連接HS2都會連不上!所以之後就沒有再探索這個問題。

這一方案跟Kerberos認證相關,而且由於Hive UDF裏取Hive表數據也存在問題,所以也被KO了

##方案二:UDF提供傳入Key的參數,UDF只做加密,由用戶傳入key 這種方案是當時AES加密裏最不看好的一種方案,因爲由用戶傳入key有很大安全隱患,而且API顯得特別矬。

但是由於Key存於HIve表,我們可以控制權限。在UDF裏我們只做加密,而不做其他額外的操作,UDF本身簡單了很多,不用考慮權限認證的問題。所以這是種最簡單的方式,經驗證也是最行之有效的方式!

就這樣,林林總總選擇又放棄了前面的五種方案(其實嘗試的時候還不止,有些是細節上的變動,這裏就沒有記錄),最後選了一種當初覺得最矬的方案~~ 真是不到最後不甘心啊!

#問題記錄

  • 在Hive的Imperonation關閉之後,怎麼才能在Hive UDF裏拿到真實提交用戶的Kerberos認證? 不只是獲取提交用戶的用戶名,而希望拿到UGI。
  • 如何才能在MR的task裏分發KMS認證後的DelegationToken?以怎樣的形式註冊到Hive和Spark的啓動程序裏,像HDFS和Yarn一樣?
  • 在集羣上了Sentry的環境下,Hive UDF裏獲取Hive表無法拿到數據的原因?語句不執行的原因?

如果前兩個問題能有效的解決掉,我想一套正式不猥瑣的加密方案就可以面世了。加油!加油!

小主們轉載請註明出處:https://my.oschina.net/u/2539801/blog/808061

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章