網(wǎng)易基于Apache Ranger 的數(shù)據(jù)安全中心實踐
一、Apache Ranger介紹
1、總體介紹
Apache Ranger是Hadoop生態(tài)中的一個安全解決方案,它通過在各個組件中集成Ranger 的插件,用中心化的 Ranger Admin來控制所有的policy,插件通過同步policy到本地緩存來提供鑒權(quán)的能力。
上方右圖是Ranger 的能力列表??梢钥吹?Ranger 的各種能力,包括管理行級權(quán)限,tag base policy,列級權(quán)限,列級過濾,列級脫敏等等能力。
接下來看一下Apache Ranger的優(yōu)勢在哪里。
安全體系的5A 理論中, Ranger可以解決其中訪問控制、監(jiān)察審計以及鑒權(quán)授權(quán)這三塊,解決起來的核心優(yōu)勢有四點:
第一個優(yōu)勢是它集成了多個大數(shù)據(jù)組件鑒權(quán)插件,比如Hive、Spark、Presto 等等一大批主流引擎的鑒權(quán)的插件。插件對組件做了非常多的適配。如果使用Ranger,可以把所有引擎的鑒權(quán)收攏由安全團(tuán)隊統(tǒng)一負(fù)責(zé)管理。如果不用Ranger,大概率是每個團(tuán)隊獨自管理各個組件鑒權(quán)邏輯,這樣比較分散,也會增加團(tuán)隊之間溝通的成本。
第二個優(yōu)勢是它有較好的鑒權(quán)性能,Ranger針對policy緩存構(gòu)建了大量的索引,可以很好到應(yīng)對 NN 這種實時性要求高的服務(wù)。對于NN來說鑒權(quán)幾毫秒和幾百毫秒會有很大差距。
第三個優(yōu)勢是它有非常靈活的鑒權(quán)能力,除了 RBAC以外,還提供 ABAC 的能力。同時還有 deny、exclude語義,鑒權(quán)非常靈活,支持的權(quán)限模型也比較多。
第四個優(yōu)勢也是最關(guān)鍵的一點,是我們調(diào)研發(fā)現(xiàn),業(yè)內(nèi)的 CDP 7.0 之后,整體的權(quán)限方案都切換為Ranger,包括業(yè)內(nèi)的一些存儲引擎,比如alluxio等等也都在集成Ranger。因此我們有了一個基本的判斷,就是在大數(shù)據(jù)的生態(tài), Ranger已經(jīng)基本成為一個事實上的標(biāo)準(zhǔn)或者是一個業(yè)內(nèi)主流的發(fā)展方向了。為了以后社區(qū)安全的新功能更好的集成,我們需要去緊跟Ranger。
左圖可以看到Ranger對所有 policy 的資源都做了一個壓縮的字典樹的索引,極大地提高了針對policy的查詢效率。在我們內(nèi)部的實踐中,在5~6萬 policy 的情況下,namenode 鑒權(quán)依然可以達(dá)到毫秒級,性能非常高。
另外Ranger 同時支持 RBAC 和ABAC 兩種模型。
這里簡單介紹一下兩種模型:
RBAC 模型是一個最常用的通過角色來管理資源的權(quán)限管理模型,這種模型比較簡單,也比較常用。ABAC模型是用戶資源以及上下文都會帶有屬性,最終的權(quán)限根據(jù)屬性計算得來,這種模式比較靈活。深入觀察可以發(fā)現(xiàn) RBAC 其實是 ABAC的一種特殊情況,因為可以把角色當(dāng)成一種特殊的屬性。ABAC的優(yōu)勢在于可以讓權(quán)限做得更精準(zhǔn),比如可以允許所有網(wǎng)易員工早上9點到10點之間訪問某個資源,或者是一個IP 段在某個時間點可以訪問某個資源。這都是Ranger所支持的一些能力。所以我們可以看到Ranger的能力還是比較強大的。
2、整體評估
前文介紹了Ranger的優(yōu)勢,不過僅用Ranger是無法完全解決企業(yè)中的大數(shù)據(jù)安全的問題。一個好的安全平臺需要做到五點。
- 第一點是安全性,我們需要的是既防君子、也防小人的安全平臺,它不同于一般的業(yè)務(wù)平臺主要考慮易用性,還要考慮被惡意攻擊或者繞開的問題。
- 第二點是精準(zhǔn)性,也就是最小權(quán)限原則,能不能給一個用戶最小權(quán)限,這個權(quán)限既不放大也不縮小。權(quán)限放大指的是,比如本來不需要給用戶某個權(quán)限,但是系統(tǒng)架構(gòu)導(dǎo)致不得不給他額外的授權(quán)才能去訪問權(quán)限。權(quán)限縮小一般都是鑒權(quán)邏輯導(dǎo)致的問題,比如本來只需要 a 資源權(quán)限,但是鑒權(quán)邏輯或者權(quán)限體系有問題,導(dǎo)致還鑒了另外一個資源的權(quán)限,這個時候就得 a 和 b 一起申請才能用。權(quán)限系統(tǒng)設(shè)計越精準(zhǔn),權(quán)限管控越有效。
- 第三點是管理性,是否有利于管理員對整體的企業(yè)內(nèi)部權(quán)限有全方位的了解。比如想知道一個資源有哪些人能訪問,或者是想知道一個用戶能訪問什么資源,或者想知道權(quán)限快到期的資源有哪些等等。能夠支持檢索的維度越多, 管理性越強,管理員的權(quán)限管控體驗越好。
- 第四點是效率,比如管理員授權(quán)操作鏈路是否復(fù)雜,有大量用戶需要授權(quán)時是否高效,是否需要專人管理賦權(quán),普通用戶能否快速獲取到需要的權(quán)限等。效率過低,會大大增加管理員的成本,且普通用戶也會感覺這套體系很難用,從而拖慢開發(fā)的節(jié)奏,最終不得不通過犧牲精準(zhǔn)性來提高效率。
- 第五點是性能,即鑒權(quán)要占用多少資源,要耗費多少時間等等。增加權(quán)限后對于整個系統(tǒng)的性能一定是有所下降的,關(guān)鍵看這種影響的程度。
我們來一一對照,看看 Ranger 在這五點上面的表現(xiàn)如何。
第一點安全性。整體而言Ranger 的安全性是比較好的,因為 Ranger 是一個內(nèi)存鑒權(quán)的機制,同時Ranger 的服務(wù)端集成了SSL認(rèn)證。假設(shè)操作系統(tǒng)、Kerberos認(rèn)證、網(wǎng)絡(luò)都是安全的,那么我們認(rèn)為Ranger是很難被繞過的,即使繞過,成本也是非常高的。當(dāng)然原生Ranger對Spark權(quán)限的管理是比較弱的,后面會詳細(xì)分析。
第二點是精準(zhǔn)性。Ranger 的權(quán)限生命周期落在 policy 上面,每條 policy 可能會包含非常多的對象,不同的對象可能生命周期又是不同的。使用Ranger這套體系時policy 的生命周期只能按照下面所有授權(quán)對象中最大的生命周期來算,否則就會有權(quán)限縮小的問題。這種情況導(dǎo)致精準(zhǔn)性有所欠缺。另外ranger對于HDFS 刪除權(quán)限包含在寫權(quán)限中,沒法只授權(quán)寫而不授權(quán)刪除。而刪除比一般的寫風(fēng)險更大所以刪除權(quán)限應(yīng)該要比寫權(quán)限或者創(chuàng)建權(quán)更高,因此可以認(rèn)為ranger在這點上的精準(zhǔn)性也有缺陷。
第三點是可管理性。這一點上Ranger的表現(xiàn)是比較差的。Ranger 的一個特點是,只能根據(jù)資源去搜policy,在 policy 中可以搜出它的訪問控制列表,但是用其它維度搜索很困難。如果要搜索一個用戶所擁有的權(quán)限,就必須先把相關(guān)所有的 policy 搜出來,再從 policy 中解析出需要的信息。在policy 數(shù)量非常大的時候,這種分頁查詢會受到諸多的限制。再如脫敏的時候,Ranger 的脫敏規(guī)則是,policy根據(jù)優(yōu)先級排列,排在前面的優(yōu)先,排在后面的優(yōu)先級依次降低。我們很難推斷一個用戶對某個資源的脫敏規(guī)則,因為角色非常多,很難去分析脫敏的整體情況。
第四點效率性。Ranger 也是相對較差的,因為Ranger的體系是依賴于管理員來進(jìn)行操作的,普通用戶是沒有任何入口的。Ranger的體系整體上是一個管理員視角的工具,如果用戶需要一個權(quán)限,只能通過管理員登錄Ranger搜索并修改policy。而Ranger 頁面搜索性能很低,搜索結(jié)果還要再編輯,整體授權(quán)鏈路是很長的。如果一天有很多用戶需要申請權(quán)限,會非常低效,尤其是團(tuán)隊擴大的時候,低效更加明顯。
第五點性能。 前面分析過Ranger 的鑒權(quán)性能非常好,但 Ranger policy 對內(nèi)存占用是較大的,因為 Ranger 是全量的緩存policy,內(nèi)存占用大的情況不僅僅是絕對的數(shù)值比較大,由于 Ranger 的policy的生命周期比較短的,10 秒、15 秒到 30 秒會全量更新一次,會形成非常多短生命周期的內(nèi)存。比如1G 的內(nèi)存雖然不算太大,但是1G內(nèi)存每 10 秒鐘都會被全量更新一次,這種情況就會對內(nèi)存的壓力以及 JVM調(diào)優(yōu)產(chǎn)生較大的影響(ranger 2.x之后提供了增量更新,但是還不太成熟容易漏policy)。
從以上分析可以看到 Ranger 的優(yōu)勢以及不足。眾所周知,安全性、精準(zhǔn)性與管理性、效率、性能必然是矛盾的,如果做得很安全,很精準(zhǔn),那么往往會導(dǎo)致管理性、效率和性能的下降。而管理性、效率、性能很優(yōu)秀的時候,又必然對安全性和精準(zhǔn)性有一定的損失。我們要做的是把這五方面統(tǒng)籌起來,尋找到一個平衡點。
二、大數(shù)據(jù)安全中心整體解決方案
1、平臺歷程
前文中介紹了 Ranger 的優(yōu)缺點,接下來將介紹網(wǎng)易內(nèi)部是如何解決這些問題的。首先整體介紹一下網(wǎng)易內(nèi)部大數(shù)據(jù)平臺安全的發(fā)展歷程,它主要經(jīng)歷了三個階段,具體如下圖。
(1)階段一
第一個階段是 2009 年到 2014 年的一個初始階段。在這一階段是采用 Ranger 加 kerberos 加 ldap 去管理權(quán)限。Ranger 直接當(dāng)產(chǎn)品使用,就在Ranger頁面上去管理Hadoop集群的權(quán)限,其整體就是一個Hadoop的權(quán)限工具。
(2)階段二
第二個階段是 2014 年到 2021 年,這個階段的安全管理是數(shù)據(jù)中臺的一個權(quán)限模塊。這個階段數(shù)據(jù)中臺對接了Ranger,進(jìn)行了一些角色用戶到中臺資源的映射,也構(gòu)建了一些資源多租戶隔離的權(quán)限管理方案。這個階段 Ranger 基本上跟數(shù)據(jù)中臺有了一定的聯(lián)動,效率得到了一定的提升,但還是存在一些問題。
(3)階段三
第三個階段是 2021 年到現(xiàn)在,我們構(gòu)建了一個獨立的一站式權(quán)限管理平臺。它不僅僅是權(quán)限管理平臺,更是一個安全管理平臺,擁有權(quán)限脫敏、審計監(jiān)控、數(shù)據(jù)掃描識別等多種功能,包括以后的加解密等等。同時我們還做了一個大動作,就是把內(nèi)部的Ranger從 0. 5 版本升級到 2. 1 版本。
2、整體方案
下圖是整體的產(chǎn)品方案圖。
可以看到我們的產(chǎn)品基本上覆蓋了安全生命周期中的所有流程,還有一些權(quán)限申請、操作審計、行為識別、權(quán)限轉(zhuǎn)移、權(quán)限釋放、數(shù)據(jù)脫敏、數(shù)據(jù)加密、白名單等等功能。最后面我們對數(shù)據(jù)和操作進(jìn)行了分類分級。下圖是一個整體的技術(shù)架構(gòu),這也是本文要重點分享的。
我們的技術(shù)架構(gòu)基本上分為三層,最下面一層為Hadoop集群,采用多Hadoop集群的模式,各個集群之間網(wǎng)絡(luò)都可能是隔離的。中間是服務(wù)層,數(shù)據(jù)平臺就構(gòu)建在這一層,主要包括安全中心、Ranger和一些插件。最上面一層是業(yè)務(wù)層,包括了剛才產(chǎn)品中提到的授權(quán)審批、白名單、凍結(jié)目錄以及一些定時任務(wù)等功能。
這套體系中比較重要的一個點就是對客戶端的鑒權(quán),比如Spark和Hive的鑒權(quán),采用了自研的plugin。plugin把內(nèi)存的鑒權(quán)轉(zhuǎn)成了網(wǎng)絡(luò)請求,這樣就不用再去緩存過多的內(nèi)存。同時增加了hive metastore 插件,去根據(jù)ddl同步進(jìn)行Ranger中的權(quán)限變更。
對于 Hive server, Impala 以及 namenode,我們基本上采用的是原生的方案,因為對于一個服務(wù)來說,內(nèi)存稍微大并不會產(chǎn)生太大影響。但是Ranger的插件也是經(jīng)過一些優(yōu)化的。中間這一層首先對 Ranger admin 進(jìn)行了一個垂直拆分,比如一個Ranger可能管理若干個集群,且有多個Ranger,這種模式安全中心會將集群路由到指定的Ranger,同時安全中心也會緩存一份policy來做一些鑒權(quán)的操作。對于頂層,是一些業(yè)務(wù)邏輯和提供的能力。
這套架構(gòu)有四個核心的思路。
(1)低耦合
首先,對 Ranger 的開發(fā),一定要是低耦合的開發(fā)。因為過度耦合在軟件工程層面會削弱變更的穩(wěn)定性。我們不依賴Ranger的policy格式和弱字段做業(yè)務(wù)邏輯。只能依賴 policy 的語義,只要Policy的語義是一樣的,上層的邏輯就必須是一樣的。比如Ranger的一個 policy 有三個 item另外一個policy有兩個item,但是兩條policy表達(dá)的含義是一樣的,此時上層的邏輯也必須是一樣的。另外policy 中有些弱字段,比如policy描述字段description,這種描述字段不能用它去做強邏輯,只能做展示邏輯。這是我們總結(jié)出來的一個比較重要的經(jīng)驗。
這么做的目的首先是不搞約定式的邏輯能夠讓代碼在多個人之間更好維護(hù)。同時Ranger admin也可以獨立使用,以達(dá)到低耦合。否則如果過度耦合,Ranger admin 就相當(dāng)于變成了一個類似于數(shù)據(jù)庫的存儲服務(wù),也就無法對用戶暴露出來,只能用我們的平臺去管理權(quán)限。在我們的實踐中,尤其是ToB商業(yè)化的過程中,這種情況是不現(xiàn)實的,有些用戶對ranger理解可能比較深入,就不期望這樣。
(2)低侵入
第二個就是低侵入,就是盡量不去修改Ranger 本身的代碼,原因之一是 Ranger 服務(wù)端代碼是比較老舊的, Ranger采用的還是jpa的一些數(shù)據(jù)庫框架,包括Spring版本都是很老的,這些老舊框架對研發(fā)效率的折損是較為嚴(yán)重的,我們現(xiàn)在的微服務(wù)等后端技術(shù)框架日新月異,如果還用這種老的框架,會導(dǎo)致整體的研發(fā)效率大打折扣。其二,如果我們把大量業(yè)務(wù)邏輯注入在Ranger中,會導(dǎo)致Ranger很難升級,尤其是大版本的升級,比如剛才談到的,從0. 5版本升級到2. 1版本,成本將非常高。
(3)流量分?jǐn)?/span>
第三個是一定要做好流量的分?jǐn)?,不能把所有的流量都打?Ranger admin 上面,因為Ranger admin 的接口內(nèi)部有一些緩存更新機制,這些機制都是會加鎖的,在流量增大的時候,鎖沖突就會非常嚴(yán)重,這時Ranger admin的性能降低,會導(dǎo)致上層業(yè)務(wù)超時等問題影響客戶使用,所以我們要遵循一個核心思路,讀的請求盡量不要直接打到Ranger admin上面,盡量做一些額外的緩存策略或者是一些索引策略。Ranger本身其實可查詢的維度也不夠豐富,如果把所有數(shù)據(jù)都放到內(nèi)存里面再去慢慢查,內(nèi)存消耗將過大。
(4)一致性保證
最后一點就是要保證一致性,比如中臺和Ranger之間先調(diào)用了中臺去存儲一個權(quán)限,然后再去將權(quán)限存儲到Ranger的時候失敗了,這樣就可能導(dǎo)致中臺與Ranger的數(shù)據(jù)不一致,體現(xiàn)到用戶視角就是在中臺中看到有權(quán)限, Ranger中卻沒有權(quán)限。因此一致性是一定要考慮到的,不能只考慮中臺操作和ranger同時成功的情況,還要考慮部分成功,部分失敗的情況,這樣才可以保證系統(tǒng)可用性,提高用戶體驗。
三、關(guān)鍵技術(shù)解析
接下來將介紹10個比較有特色的設(shè)計細(xì)節(jié)。
1、統(tǒng)一權(quán)限檢索問題
上文討論的Ranger的資源,其原生的權(quán)限模型有很多優(yōu)勢,但是也存在一些劣勢。比如我想知道一個人具備哪些權(quán)限,一個脫敏算法到底被哪些地方用到了,這些都是沒法搜索的,用戶粒度的權(quán)限生命周期也沒法設(shè)置。針對這個問題,網(wǎng)易內(nèi)部搭建了一層權(quán)限檢索模型,如下圖。
權(quán)限檢索模型是一個純粹只讀的視圖,基于Ranger的 change log進(jìn)行一個變更的同步,比如 30 秒進(jìn)行一個變更的同步,同步過來后,會把policy 解析成我們需要的搜索維度和格式存放到在我們的權(quán)限模型中,所有的查詢接口都通過我們的權(quán)限模型,Ranger admin只做寫的入口。比如業(yè)務(wù)層要授權(quán),要申請審批,之后權(quán)限調(diào)整,修改脫敏規(guī)則等等這些修改的操作都只會操作Ranger admin ,保證是單寫的,不會去先在中臺寫一份,Ranger再寫一份。從而避免上文說的雙寫不一致的問題。通過這種異步的同步機制,雖然查詢可能會有 30 秒的延遲,但是可以保證最終的一致性。
Ranger change log 是2.x版本才推出的一個特性,它記錄了所有的policy增刪改查的事件,這些變更日志存在一個數(shù)據(jù)庫里面,通過一個接口去拉取。通過這套方案既能保證最終一致性,又能在此基礎(chǔ)上極大地提高查詢性能,而靈活的查詢又使得產(chǎn)品體驗提升。各個維度的權(quán)限數(shù)據(jù)用戶都可以去檢索,前面舉例提到的,一個人有哪些權(quán)限、哪些權(quán)限要到期了等等問題,都可以便捷搜索到結(jié)果,管理員對權(quán)限整體上有很全面的了解,體驗非常好。同時權(quán)限生命周期也使用這套權(quán)限模型來實現(xiàn),可以達(dá)到精細(xì)化的設(shè)置權(quán)限生命周期的目的。
2、鑒權(quán)優(yōu)化&多集群
我們在實踐中發(fā)現(xiàn)1萬條 policy 基本上占用內(nèi)存在 100- 200 M之間,如果有幾萬條,甚至上 10 萬條,量就非常大。再加上,Ranger admin緩存了一份全量的policy,導(dǎo)致policy 多的時候GC會有一個很長的停頓時間。另外在 client 模式下,一臺機器可能會起多個client,每一個 client 如果按照Ranger的原生模式,都會緩存一份 全量policy 到內(nèi)存,這種情況下內(nèi)存的損耗在多個client中是疊加的,我們的解決方案是做一個內(nèi)存轉(zhuǎn)網(wǎng)絡(luò)的鑒權(quán)方案,統(tǒng)一把這種 client 模式的鑒權(quán)轉(zhuǎn)成了網(wǎng)絡(luò)的方式,如下圖。
第二個改進(jìn)是,我們對Ranger進(jìn)行了垂直拆分。以前一個Ranger管理了多個集群,Ranger中一個 service 對應(yīng)的就是一個集群,一個service其實就是對應(yīng)一套元數(shù)據(jù),一套元數(shù)據(jù)約等價于一套擁有唯一的Hive metastore的hadoop集群。我們對Ranger進(jìn)行了垂直拆分,比如我們內(nèi)部現(xiàn)在有九個集群,有三個Ranger來支撐,一個Ranger只管三個集群,這樣單個Ranger的 policy 數(shù)量就下降了,GC 時間就得到了控制,Ranger admin 的性能也得到了保障。
這兩個思路其實都不復(fù)雜,但是我們實踐中還是遇到了不少的小問題。比如第一個問題是內(nèi)存轉(zhuǎn)網(wǎng)絡(luò)的時候,如果服務(wù)端不可用,任務(wù)會不會受到影響,這涉及到高可用的問題。第二個問題是請求的性能問題,同機房的調(diào)用在我們的實踐中基本上就是幾毫秒,但是我們在對外商業(yè)化的過程中遇到了一個情況,存在有跨國的機房,甚至跨洲的機房,比如印度機房調(diào)用中國的機房,或者是歐洲的機房調(diào)用中國的機房。這種跨洲調(diào)用過程中,網(wǎng)絡(luò)的延遲就會非常明顯。我們之前是每一列都會去調(diào)用的,現(xiàn)在發(fā)現(xiàn)調(diào)用次數(shù)還是比較影響性能的,多個請求必須要壓縮成一次調(diào)用,需要去優(yōu)化,尤其是對于Spark,由于執(zhí)行計劃優(yōu)化的不同周期中沒有通信所以容易存在重復(fù)鑒權(quán)的情況。
3、鑒權(quán)請求報文體優(yōu)化
我們是把Ranger插件改造成了遠(yuǎn)程鑒權(quán)的模型,大部分的修改是基于以前ranger插件代碼去修改的。改造過程中如果圖省事,可能直接把以前內(nèi)存的一些參數(shù)轉(zhuǎn)成網(wǎng)絡(luò)的參數(shù),但這會帶來很大的性能問題。因為Ranger鑒權(quán)內(nèi)存參數(shù)很大,一個報文體可能就有幾Mb,如果并發(fā)QPS有幾百上千的時候,內(nèi)存消耗非???,實踐中發(fā)現(xiàn)一個大寬表可能對應(yīng)鑒權(quán)報文有4-5Mb。對這種報文體是必須要進(jìn)行優(yōu)化的,如果還是用 Ranger原生的內(nèi)存鑒權(quán)報文就會太大,對性能損耗也會很大。
4、凍結(jié)目錄
2019 年,我們內(nèi)部一些重要庫的 HDFS 路徑被誤刪。雖然誤刪之后,因為HDFS有一些垃圾回收站等機制,最終數(shù)據(jù)還是能恢復(fù)過來,但恢復(fù)的過程非常吃力,另外恢復(fù)過程中基本數(shù)據(jù)是不可用的,對業(yè)務(wù)的可用性影響很大。
除了組織上權(quán)限管理的問題以外,前面也提到過我們認(rèn)為Ranger最大的一個缺陷就是它對目錄的delete、 rename 這種刪除、重命名操作的權(quán)限是含在其寫的權(quán)限中的。這就可能存在一些隱患,因為刪除權(quán)限級別本身應(yīng)該是高于寫的。我們的解決方案比較簡單,就是把 HDFS 中的delete、 rename 這兩個 action 單獨抽離出來進(jìn)行鑒權(quán),不合在寫的權(quán)限里面,最終我們的實現(xiàn)效果也是明顯的。內(nèi)部的核心庫得到保護(hù),路徑不會再被誤刪。
5、動態(tài)脫敏
為什么要增強ranger的動態(tài)脫敏?這里舉了兩個例子。第一個是如果一個數(shù)倉中有 100 列都是電話號碼,開始是把電話號碼全部配成了遮蓋脫敏,突然需要電話號碼把前三位露出來,只對后面的幾位數(shù)字脫敏,這種情況下,原生的脫敏規(guī)則管理員可能就要改 100 次,成本很高。如果下次又要把電話號碼的前四位露出來,又需要改 100 次。第二個是 Ranger 的policy沒法作用在一個范圍上(只能作用在具體的列上),導(dǎo)致無法配置對指定庫不脫敏,如果需要配置管理員角色對一批庫不脫敏,在原生的Ranger,至少 2. 1 版本的Ranger中是沒法做到的。
我們認(rèn)為第一個問題的主要原因是Ranger沒有對規(guī)則的泛化能力必須指定一個具體的UDF函數(shù)。我們對Ranger 插件進(jìn)行了優(yōu)化,它不再依賴于一個具體的 UDF 函數(shù),而是依賴一個泛化脫敏規(guī)則。比如我的一個脫敏規(guī)則就叫「電話號碼脫敏」,它是一個泛化的規(guī)則,具體如何脫敏不確定。對電話號碼的列,就配置為「電話號碼脫敏」,具體對應(yīng)的脫敏的UDF細(xì)節(jié)是可變的。脫敏的時候插件首先通過policy找到對應(yīng)脫敏規(guī)則的ID,然后去服務(wù)端請求把 ID 換成具體的脫敏實例。最終吐給計算引擎的時候,再把它轉(zhuǎn)成一個具體 UDF 的格式,這樣就可以達(dá)到上文描述的效果。
第二個問題,我們認(rèn)為Ranger的脫敏規(guī)則管理是有一定缺陷的。上圖的左邊可以看到Ranger原生的管理是通過優(yōu)先級進(jìn)行管理的,一列可有多個優(yōu)先級不同的脫敏規(guī)則,排在最上面的優(yōu)先級最高,比如角色a用規(guī)則1脫敏優(yōu)先級最高,角色b用規(guī)則2脫敏優(yōu)先級其次,當(dāng)然這樣是很靈活,但是管理成本也是非常高的,尤其是當(dāng)用戶在多個角色中的時候?qū)嶋H走到的是哪條規(guī)則情況就較為復(fù)雜。
我們的方案是把它轉(zhuǎn)成了一種白名單的方式,比如配置a、b、c用戶是白名單,不需要脫敏,對其余所有的用戶都是用規(guī)則1脫敏這張表,這樣我們只用去管理白名單的機制就可以了,用戶也可以去獨立申請白名單。
此時白名單只有兩層優(yōu)先級的概念了。白名單優(yōu)先級是大于脫敏的,只要在白名單中,肯定不脫敏,退出白名單,肯定會脫敏。這樣邏輯就簡單化了,雖然可能略微喪失一定的靈活性例如不同角色有分級的脫敏規(guī)則,但是實踐中發(fā)現(xiàn)該方案才是產(chǎn)品中實際能被用起來的一種方案,為了實現(xiàn)該目地需要修改對應(yīng)的ranger 插件中脫敏policy匹配相關(guān)代碼,加入范圍的脫敏規(guī)則(例如配置在庫.*下的規(guī)則)匹配,以便實現(xiàn)在一個范圍下配置白名單效果。
我們優(yōu)化的最終效果就是脫敏的易用性得到了提升,同時掃描和脫敏是一體化的,從圖右邊可以看到,掃描任務(wù)可以自動地把掃出來的敏感類型推到脫敏里面去。掃描脫敏的一體化能夠使得產(chǎn)品體驗得到很好的提升,這也是單一ranger做不到的。
6、審計和治理
這其實對于權(quán)限管理是一個老生常談的問題了,做過權(quán)限的同學(xué)可能都會遇到一個問題就是用戶突然找過來說:“我之前有權(quán)限,怎么突然就沒有了,你幫我加一下”。這個時候可能研發(fā)只能幫用戶把權(quán)限補上去了,但是也不知道什么原因。第二個問題是有多少權(quán)限是無效權(quán)限?權(quán)限授了,系統(tǒng)里面很多權(quán)限也沒什么問題,但是到底有多少有用,很難判斷。Ranger 也不好解決這兩個問題,因為它沒有一個全資源視角的審計,對 policy 變更的審計,只能是 policy 的視角,在 policy 刪除重建等操作時就審計不到。同時它也沒有一個 item 視角的層級,具體一個人到底是通過角色還是通過個人權(quán)限訪問的資源是無法審計到的。
我們的解決方案就是針對這個問題,首先把權(quán)限模型和用戶角色變更同步一份到數(shù)倉里去分析追蹤。同時我們在Ranger審計插件的審計模塊中做了一些改動,首先把 policy 鑒權(quán)過程中的命中信息發(fā)送到 Kafka 中去進(jìn)行維度的補充,跟中臺的維度進(jìn)行了打通,這樣我們審計可以提供所有的權(quán)限變化追蹤,同時我們還補了一個角色命中的信息,通過什么角色去訪問的信息也很重要。在治理中,通過維度的補充這個過程,我們就可以把它細(xì)化到具體的人或角色了,根據(jù)它給我們的原始數(shù)據(jù)的policy id,我們會把它拆成具體用戶是通過哪一條item,哪一個具體的權(quán)限去訪問的一個表,我們的細(xì)粒度就可以解決這個問題。
方案的最終效果明顯,我們在內(nèi)部定位發(fā)現(xiàn),用戶經(jīng)常刪表重建,重建后的表就沒了訪問權(quán)限,我們就清楚了這個問題的答案。同時我們發(fā)現(xiàn)內(nèi)部有 70% 的權(quán)限是半年都沒有用過的權(quán)限,這個比例是非常大的。但是無效權(quán)限或者冗余權(quán)限能不能直接回收,這是另外一個問題,可能冗余權(quán)限不一定就完全可以直接回收,我們還在實踐過程中。
7、權(quán)限的ownership
Ownership指的是表的負(fù)責(zé)人自動對表有所有權(quán)限,而不需要額外用 policy來授權(quán)。我們經(jīng)常遇到的一個問題是用戶反饋我建的表為什么不能刪除,但Ranger原生的ownership機制并不完備,因為社區(qū)的 Hive 4.0 之后才集成了Ranger原生的 ownership,而Impala 應(yīng)該是在較早的版本就集成了。所以 ownership 是一個比較棘手的問題。
我們的解決方案是基于元數(shù)據(jù)中心的 owner 的補充。我們會從鑒權(quán)的時候,去請求一個元數(shù)據(jù)中心,也就是內(nèi)部的元數(shù)據(jù)系統(tǒng),補充 owner 的信息,然后再走 Ranger原生的ownership鑒權(quán)邏輯。
為什么要走元數(shù)據(jù)中心,不能從底層組件把 owner 帶過來?因為我們遇到過,低版本的Spark尤其是 2.3 版本以下,會對表 owner 的元數(shù)據(jù)進(jìn)行污染。Hive meta store 里面的 owner 字段會被低版本的 Spark 污染。因此從上層來解決能夠提供更好的兼容性。
同時還有一個困難點是 HDFS 的 owner 問題。原生的 HDFS owner權(quán)限其實不包含遞歸屬性,但是表的 owner 對表的路徑應(yīng)該是有遞歸的權(quán)限的,否則表下面的分區(qū)就不一定能訪問,但是原生的 HDFS 的權(quán)限(acl)或者原生Ranger,都沒法支持路徑的遞歸的owner語義。
針對這個問題,我們對NN的鑒權(quán)插件進(jìn)行了一個擴展,可以看到上圖圖左下面,上面 owner 是原生的owner,下面 recursive owner 是我們在Ranger上面新增的owner。recursive owner的含義就是這個路徑的 owner對這個路徑具有遞歸的訪問權(quán)限。
實現(xiàn)的方案略微有點復(fù)雜。鑒權(quán)請求中的路徑首先命中了一條 policy 之后,把所有的「請求路徑」到「policy資源路徑」之間的「祖先」owner 算出來,例如上圖右圖中的user2、user3、user4為「祖先」owner。然后通過「祖先」 owner 去跟當(dāng)前請求的被鑒權(quán)用戶匹配,如果「祖先」 owner包含被鑒權(quán)用戶則命中item中的recursive owner占位符,然后再看recursive owner 的item有什么權(quán)限就返回相應(yīng)的結(jié)果。要對 Ranger 的插件進(jìn)行一些修改,主要是NN的插件進(jìn)行修改。通過這些手段我們就達(dá)成了一套很完備的owner語義。同時我們的表負(fù)責(zé)人對路徑的權(quán)限也得到了增強,不存在表負(fù)責(zé)人對表路徑只有非遞歸的權(quán)限了,目錄下的子目錄的權(quán)限都可以被繼承。
最后一個點是Ranger的 policy 數(shù)量也會下降,因為以前的這種體系沒有 owner 語義。其實我們每張表是有一條 policy 去表征其owner 的權(quán)限。在表的數(shù)量大的時候, policy 數(shù)量也很大。有了 owner 語義后,一條 policy 就可以表示出來所有的 owner 都有對應(yīng)表的權(quán)限。
8、Spark權(quán)限/脫敏
Ranger原生是不支持 Spark 的,我們通過一些介入 Spark 的執(zhí)行計劃的過程來實現(xiàn)權(quán)限脫敏。首先是 Spark 的執(zhí)行計劃有四個階段,在解析這個階段又分為 analyzed執(zhí)行計劃和 optimized 執(zhí)行計劃,我們的動態(tài)脫敏和行級權(quán)限,其原理就是去修改 analyzed 執(zhí)行計劃,在該analyzed階段中添加 filter 和脫敏函數(shù),其實就是添加UDF。
鑒權(quán)是通過 optimized 階段執(zhí)行計劃的一個輸入輸出提取去鑒權(quán)。但是在這種思路下有一些難點需要去處理,比如Spark每一個優(yōu)化的階段是反復(fù)執(zhí)行的,不是一次優(yōu)化就結(jié)束的。所以該思路存在階段通信的問題,比如此階段已經(jīng)執(zhí)行過脫敏了,下一個階段再進(jìn) analyzed 的時候,就不能再脫敏了。權(quán)限其實也是一樣的,只需要鑒權(quán)一次。這種階段之間通信需要一個溝通或者通信的機制。其次就是視圖的處理,Spark視圖權(quán)限整體是比較復(fù)雜的,需要單獨處理。
最后一個要點是這三者之間的優(yōu)先級關(guān)系,行級權(quán)限和脫敏的優(yōu)先級,到底是先脫敏之后再走行級權(quán)限,還是用原文去走行列權(quán)限再脫敏,這些都是實踐中會遇到的一些問題。當(dāng)然,目前問題我們也都已經(jīng)解決了??梢钥磮D右邊的執(zhí)行計劃代碼。其中紅色的部分是一個脫敏的執(zhí)行計劃修改,藍(lán)色的部分是一個行級權(quán)限的執(zhí)行計劃修改,橙色的部分是我們插入用來階段之間溝通的虛擬節(jié)點,只要遇到這個節(jié)點,就認(rèn)為執(zhí)行計劃已經(jīng)被處理過了??梢钥吹轿覀兪前研屑墮?quán)限放在最下面的,所以我們是要先過行級權(quán)限再過脫敏的,這樣也是比較合理的。我們的最終效果是Spark 與 Hive以及Impala 權(quán)限脫敏實現(xiàn)統(tǒng)一管理,不再需要多次授權(quán)。這樣,便利性和安全性都得到了提升。
9、Ranger社區(qū)優(yōu)化
可能做Ranger的同學(xué)會比較感興趣,如果深入去做產(chǎn)品化會發(fā)現(xiàn)Ranger 的小 bug 其實挺多的。這里我分享兩個,也是相對是隱藏得比較深的、解決耗時比較久的兩個bug。
第一個 bug 是Ranger2. 0 以后的版本中 RangerPolicyResponsitory 這個類用了finalize去釋放內(nèi)存。Java 的 finalize 關(guān)鍵字風(fēng)險是很高的,因此用了以后,內(nèi)存的釋放很慢,本質(zhì)上的原因是Responsitory與enricher的生命周期不同,Responsitory短于它所依賴的enricher的生命周期。Ranger開發(fā)者的解決方法是用 finalize 去釋放。
但是這可能不是一個很好的方案,因為我們實踐中發(fā)現(xiàn),我們NN上線之后, fullGC 很頻繁,基本上30-40分鐘一次 fullGC,因為policy是全量內(nèi)存緩存,尤其是Ranger policy 多的時候,內(nèi)存可能有1-2G,釋放太慢,每次又是全量更新,積累下來很快就 fullGC 了。我們知道 NN 是實時性很強的服務(wù),一次 fullGC 對業(yè)務(wù)的影響是非常大的,所以我們線上不得不立即全量回滾,直到后面才解決這個問題。我們解決完之后,現(xiàn)在我們的NN的QPS 基本上4w+,峰值可能5w,基本上鑒權(quán)毫秒級還是非常穩(wěn)定的。
第二個主要是右圖代碼所示的部分。
右圖其實是兩個并發(fā)不安全的問題,第一個問題,原生的Ranger可能對并發(fā)修改稍微有點薄弱,圖中可以看到原生Ranger首先是拿到policy當(dāng)前的一個版本號,在第二步里面,它就去把版本號+1,再去設(shè)置成新的版本號。1和2之間是沒有任何加鎖和互斥的,這肯定是不安全的,兩個請求并發(fā)進(jìn)1和2之間,會導(dǎo)致線程不安全,版本號肯定是會錯位的。
第二個問題,如圖所示,我們可以看到Ranger的緩存更新操作,在更新緩存的時候,首先是設(shè)置到當(dāng)前緩存的版本號,再去設(shè)置緩存的內(nèi)容,如果在步驟1和2之間有讀請求進(jìn)來會比較麻煩,這可能就會發(fā)生一次臟讀,這也是不安全的,因為版本號已經(jīng)上去了,但是policy內(nèi)容還沒更新好,讀出來的就是一個新的版本號,但是 policy 內(nèi)容是舊的,如果后面繼續(xù)增量更新,就會基于一個錯誤視圖去更新,就會永遠(yuǎn)漏掉一些policy。
10、商業(yè)化
最后,分享網(wǎng)易數(shù)帆遇到的一個特殊的場景。
網(wǎng)易數(shù)帆也是一個 ToB 商業(yè)化的平臺,我們不光要解決網(wǎng)易內(nèi)部的安全問題,也致力于解決企業(yè)級服務(wù)的大數(shù)據(jù)安全問題。在大范圍內(nèi)鋪開過程中,我們遇到了兩個問題,第一個是依賴關(guān)系有點復(fù)雜,可以看到圖中橘色的部分是我們安全團(tuán)隊負(fù)責(zé),但是藍(lán)色的部分,這些計算引擎又是其他底層團(tuán)隊負(fù)責(zé)的。我們兩個團(tuán)隊之間的依賴變成了犬牙交錯的狀態(tài)。
這種犬牙交錯的狀態(tài)會帶來一個問題,就是我們對外多環(huán)境部署的時候,配置依賴比較復(fù)雜,同時我們版本升級變更頻繁的時候,變更順序也比較復(fù)雜,容易出現(xiàn)升級出問題或者配置丟失的情況。因此在版本上必須要做好協(xié)同管理。同時我們安全中心也做好了兼容性保證,比如底層引擎沒升級,安全中心即橘色部分會去兼容一些場景。
圖右邊是想說明這樣一個問題,我們這套解決方案由于商業(yè)化場景客戶的多種多樣,我們底層 Hadoop 不一定是我們自己的Hadoop,有可能是用戶自己提供的一套Hadoop,也有可能是CDH、CDP,什么都有可能。在這種情況下,我們的功能也是可以實現(xiàn)的,因為我們的解決方案是一套相對通用的數(shù)據(jù)安全解決方案。當(dāng)然如果在用戶使用自己Hadoop 的情況下,也會損失一部分能力,但是我們安全的核心能力還是可以保證的。
這兩個點其實就要求我們的安全中心要做一個抽象,需要把不變的東西抽出來,從而隨機應(yīng)變,這對設(shè)計會有一定的要求。我們現(xiàn)在在做的30來個環(huán)境整體上部署變更都比較敏捷。其中很大比例也采用了對接模式,即用戶使用自己的Hadoop,我們也都能順利承擔(dān)。
四、成果
最后簡單分享一下我們的成果。
內(nèi)部主要應(yīng)用在網(wǎng)易云音樂、網(wǎng)易嚴(yán)選等,更多的是外部商業(yè)化。我們方案的穩(wěn)定性和產(chǎn)品的易用性都已得到了很好的驗證。