天天看點

論微服務安全:保護微服務的兩大方案

每個人都在讨論微服務,每個人也都希望能夠實作微服務架構,而微服務安全也日漸成為大家關注的重要問題。今天與大家分享的文章,就從應用層面深入探讨了應對微服務安全挑戰的方案,為微服務安全提供了新的思路。

面向服務架構(簡稱 soa)引入了一類設計規範,其核心思路在于采用高度解耦式服務部署,其中各項服務可通過一套标準資訊格式經由網絡實作彼此通信。這套方案與具體技術無關,即不考慮各項服務具體是如何實作的。每項服務都擁有一個明确定義,用于釋出服務描述或者服務接口。在實踐當中,這類資訊格式通過 soap 實作标準化——即由 w3c 于 2000 年初推出的一項标準——同時亦基于 xml——其中服務描述由 wsdl(另一項 w3c 标準)進行标準化,而服務發現标準由 uddi(同樣為 w3c 标準)實作。這一切正是基于 soap 的 web 服務的實作基礎,甚至使得 web 服務在一定程度上成了 soa 的代名詞。不過這種實作方式在架構模式層面也有着自己的缺陷。soa 的基本原則正被時代所逐漸淘汰,如今由 oasis 提供的 ws-* 堆棧(包括 ws-security, ws-policy, ws-security policy,ws-trust, ws-federation, ws-secure conversation, ws-reliable messaging, ws-atomic transactions, ws-bpel 等等)令 soa 的複雜性不斷提高,這也直接導緻很多普通開發者發現自己很難對其加以駕馭。

多年之後,如今我們得以再次開啟這段通往 soa 基本原則的旅途——但這一次它有了新的名号,即微服務microservice。微服務能夠為應用程式設計提供一種更具針對性、範圍性與子產品性的實作方案。

微服務可謂當下一大熱門詞彙之一,與之并駕齊驅的則包括物聯網、容器化與區塊鍊。“微服務”一詞最初于 2011 年 5 月亮相于威尼斯軟體架構師研讨會。這個詞彙用于解釋一類常見的架構類型。

大家已經意識到微服務并不僅僅是做對了的 soa,它也不隻是一種架構模式——而是一種圍繞架構模式展開的全新文化。其由主要目标作為驅動力,旨在實作快速部署與快速生産。

在保護微服務安全時,需要從以下幾個角度入手:

保護開發生命周期與測試自動化機制:微服務背後的核心驅動力在于提升投付生産的速度。我們需要向服務當中引入變更,加以測試而後立即将成果部署至生産環境。為了確定在代碼層面中不存在安全漏洞,我們需要制定規劃以進行靜态代碼分析與動态測試——更重要的是,這些測試應當成為持續傳遞流程的組成部分。任何安全漏洞都需要在早期開發周期内被發現,另外回報周期也必須盡可能得到縮短。

devops 安全:微服務部署模式可謂多種多樣——但其中使用最為廣泛的當數每主機服務模式。其中的主機指定的并不一定是實體裝置——也很可能屬于容器(docker)。我們需要對容器層面的安全進行關注。我們該如何確定各容器之間得到有效隔離,又該在容器與主機作業系統之間采取怎樣的隔離水準?

應用級别安全:我們該如何驗證使用者身份并對其微服務通路操作進行控制,又要怎樣保障不同微服務之間的通信安全?

在今天的文章中,我們将提供一整套安全模式,旨在解決應用層級所面臨的各類微服務安全保護挑戰。

<a target="_blank"></a>

在整體型應用程式中,所有服務都被部署在同一應用伺服器當中,而該應用伺服器本身則提供會話管理功能。其中不同服務間的接口為本地調用,且全部服務皆可共享使用者的邏輯狀态。每項服務(或者元件)不需要對使用者進行驗證。驗證工作集中由攔截器處理,其攔截所有服務調用并審查其是否可以放行。驗證完成之後,其會在不同平台上的不同服務(或者元件)間發送使用者登入憑證。以下示意圖解釋了整體應用程式中各不同元件間的互動方式。

論微服務安全:保護微服務的兩大方案

在 java ee 環境下,攔截器可以由 servlet 過濾器充當。該 servlet 過濾器會攔截全部來自其已注冊上下文的請求,并強制進行驗證。該服務調用要麼攜帶有效的憑證,要麼擁有能夠映射至某個使用者的會話令牌。一旦 servlet 過濾器找到該使用者,則會建立登入上下文,并将其傳遞給下遊元件。每個下遊元件都能夠從該登入上下文内識别出使用者以完成授權。

在微服務環境下,安全性往往成為最大的挑戰。在微服務架構當中,各服務分布及部署在分布式設定當中的多套容器之内。各服務接口不再存在于本地,而是通過 http 進行遠端接入。以下示意圖顯示了不同微服務之間的互動方式。

論微服務安全:保護微服務的兩大方案

這裡的挑戰在于,我們要如何驗證使用者并在不同微服務之間以對稱方式完成登入上下文傳遞,随後還要想辦法讓微服務完成對使用者的授權。

在今天的文章中,我們将探讨兩套方案,旨在保護服務到服務通信。其一基于 jwt,其二則基于 tls 互相驗證。

論微服務安全:保護微服務的兩大方案

jwt(即 json web 令牌)負責定義一套容器,旨在完成各方之間的資料傳輸。其可用于:

在各方之間傳播其中一方的身份。

在各方之間傳播使用者權利。

通過非安全通道在各方之間安全實作資料傳輸。

根據jwt受信名額判斷使用者身份。

已簽名 jwt 被稱為 jws(即 json web 簽名),而加密 jwt 則被稱為 jwe(即 json web 加密)。事實上,jwt 并不會以自身原始方式存在——其要麼作為 jws,要麼作為 jwe,它像是一種抽象類——jws 與 jwe 為其具體實作方式。

來自某一微服務并将被傳遞至另一微服務的使用者上下文可伴随 jws 一同傳遞。由于 jws 由上遊微服務的某一已知密鑰進行簽名,是以 jws 會同時包含有最終使用者身份(在 jwt 中聲明)以及上遊微服務身份(通過簽名實作)。為了接收 jws,下遊微服務首先需要根據 jws 本身中的嵌入公鑰對 jws 的簽名進行驗證。這還不夠,我們還需要檢查該密鑰是否受信。不同微服務之間可通過多種方式建立受信關系。其一為由服務為各服務配置受信證書。很明顯,這種方式在規模化微服務部署環境中并不可行。是以我建議大家建立一套專有證書中心(簡稱 ca),同時可以為不同微服務組設定中介證書中心。現在,相較于互相信任及各自配置設定不同的證書,下遊微服務将隻需要信任根證書授權或者中介機制即可。這能夠顯著降低證書配置所帶來的管理負擔。

每項微服務都需要承擔 jwt 驗證成本,其中還包含用于驗證令牌簽名的加密操作。微服務層級中的 jwt 會進行緩存,而非每次進行資料提取,這就降低了重複令牌驗證造成的性能影響。緩存過期時間必須與 jwt 的到期時間相比對。正是由于利用這種機制,是以如果 jwt 的過期時間設定得太短,則會給緩存性能造成嚴重影響。

jwt 在其聲明集中包含一項參數,名為 sub,其代表擁有該 jwt 的主體或者使用者。jwt 本身也可以包含各類使用者屬性,例如first_name、last_name、email 等等。如果任何微服務需要在其操作過程中識别此使用者,則需要檢視對應的屬性。sub 屬性的值對于給定發行者而言是惟一的。如果大家擁有一項微服務,其能夠從多個發行者處接收令牌,那麼該使用者的惟一性應被認定為該發行者與 sub 屬性的結合體。

而 aud 參數同樣存在于 jwt 聲明集内,負責指定令牌的目标閱聽人。其可以是單個接收者或者是一組接收者。在執行任何驗證檢查之前,該令牌接收者都必須首先檢視是否釋出了特定 jwt 供其使用,如果沒有則立即拒絕。令牌發送方需要在發出令牌之前,确定該令牌實際接收者的身份,同時 aud 參數值必須屬于令牌發送方與接收方間預先約定的值。在微服務環境中,我們可以利用正規表達式來驗證令牌閱聽人。舉例來說,令牌中的 aud 值可以為*.facilelogin.com,意味着 facilelogin.com 域名下的每個接收方(例如foo.facilelogin.com、bar.facilelogin.com 等)都能夠擁有自己的 aud 值。

在 tls 互相驗證與 jwt 方法當中,每項微服務都需要擁有自己的證書。這兩種方法的差別在于,jwt 驗證機制中 jws 可同時攜帶最終使用者身份以及上遊服務身份。而 tls 互相驗證則隻在應用層傳輸最終使用者身份。

在以上提到的兩種方案當中,證書吊銷都是項棘手的任務。證書吊銷盡管難以實作,但仍然存在多種選項供我們選擇:

crl (證書吊銷清單 / rfc 2459)

ocsp (線上證書狀态協定 / rfc 2560)

ocsp stapling (rfc 6066)

ocsp stapling required (尚處于草案階段)

crl 的使用頻率并不高。用戶端在發起 tls 握手時,必須從對應的證書頒發中心處擷取一份長長的吊銷證書清單,而後檢查伺服器證書是否被列入該清單。相較于每一次進行清單擷取,用戶端可以在本地對 crl 進行緩存。在此之後,大家還需要考慮如何避免以陳舊資料為基礎做出判斷的問題。當 tls 互相驗證機制被使用時,伺服器也需要針對用戶端進行同樣的證書驗證。最終,人們發現 crl 的實際效果其實并不理想,是以新的解決方案也應運而生——這就是 ocsp。

在 ocsp 當中,一切元素的實際效果都要比 crl 好上那麼一點。tls 用戶端能夠檢查特定證書的狀态,且無需從證書中心處下載下傳完整的吊銷證書清單。換句話來說,當用戶端每次與新的下遊微服務進行通信時,其都必須同對應的 ocsp 響應方溝通以驗證目前伺服器(或者服務)的證書狀态——而伺服器則必須面向用戶端證書執行同樣的操作。如此一來,ocsp 響應方同樣面臨着巨大的流量壓力。基于同樣的考慮,用戶端仍然可以對 ocsp 決策進行緩存,但這無疑繼續帶來同樣的、基于陳舊資料進行決策的可能性。

而 ocsp stapling 的出現令用戶端不再需要每次同下遊微服務進行通信時,都與 ocsp 響應方“打招呼”。該下遊微服務将從對應的 ocsp 響應方處擷取 ocsp 響應,以及 staple,或者将響應附加到證書本身當中。由于 ocsp 響應得到了對應證書中心的簽名,是以該用戶端能夠驗證通過其簽名并接收此響應。這種方法令事情有了轉機,事實上如今是由服務而非用戶端與 ocsp 響應方進行通信。不過在 tls 互相驗證模式下,ocsp stapling 相較于原始 ocsp 無法帶來任何額外優勢。

由于 ocsp 必須配合 stapling,該服務(即下遊服務)需要向用戶端(即上遊服務)提供保證,證明 ocsp 響應被附加到了該服務在 tls 握手時接收到的證書中。如果 ocsp 響應未被附加至該證書中,那麼結果并非出現軟錯誤,而是用戶端必須立即拒絕該連接配接。

從最終使用者的角度來看,臨時證書的效果與目前的正常證書并無差別,隻不過暫時證書的過期時間非常之短。tls 用戶端并不需要針對臨時證書進行 crl 或者 ocsp 驗證,而是堅持設定好的過期時間,并對證書本身進行時間戳加蓋。

論微服務安全:保護微服務的兩大方案

臨時證書帶來的最大挑戰在于其部署與維護工作。自動則正是解決這些難題的靈丹妙藥。netflix 公司建議使用分層方案以建構臨時證書部署機制。大家可以在 tpm(即受信平台子產品)或者 sgx(軟體保護擴充)當中獲得系統身份或者長期證書,進而顯著提升安全性。在此之後,再使用這些憑證作為臨時證書。最後,在微服務中使用臨時證書——這些證書亦可由其它微服務使用。每項微服務都能夠利用自身長期證書對臨時證書進行定期重新整理。當然,僅僅擁有臨時證書還不夠——托管該服務(或者 tls 終止器)的主機應當支援對伺服器證書的動态更新。目前存在大量能夠運作伺服器證書動态重載的 tls 終止器,但其中大多數可能會導緻短暫的服務停機。

微服務集與外部世界的連通一般經由 api 網關模式實作。利用 api 網關模式,需要進行聲明的微服務能夠在該網關内獲得對應的 api。當然,并不是所有微服務都需要立足于 api 網關實作聲明。

論微服務安全:保護微服務的兩大方案

最終使用者對微服務的通路(通過 api 實作)應當在邊界或者 api 網關處進行驗證。目前最為常見的 api 安全保護模式為 oauth 2.0。

oauth 2.0 是一套作為通路代表的架構。它允許某方對另一方進行某種操作。oauth 2.0 引入了一系列 grant type。其中之一用于解釋協定,用戶端可利用此協定擷取資源擁有方的許可,進而代表擁有方進行資源通路。另外,還有部分 grant type 可解釋用于擷取令牌的協定,且整個操作完全等同于由資源擁有方執行——換言之,該客戶在這種情況下即相當于資源擁有方。以下示意圖解釋了 oauth 2.0 協定的宏觀實作流程。其中描述了 oauth 用戶端、資源擁有方、驗證伺服器以及資源伺服器之間的互動方式。

論微服務安全:保護微服務的兩大方案

要想通過 api 網關通路某項微服務,請求發起方必須首先獲得有效的 oauth 令牌。系統能夠以自身角色通路微服務,也可以作為其他使用者實作通路。對于後一種情況,假設使用者登入至某 web 應用,那麼此後該 web 應用即可以所登入使用者的身份進行微服務通路。

論微服務安全:保護微服務的兩大方案

下面來看端到端通信的具體實作方式,如上圖所顯示:

使用者通過 identity provider 登入至 web 應用/移動應用,而 web 應用/移動應用則通過 openid connect(也可以是 saml 2.0)信任該 provider。

該 web 應用擷取一條 oauth 2.0 access_token 與一條 id_token。其中 id_token 将驗證通路該 web 應用的最終使用者。如果使用 saml 2.0,則該 web 應用需要與其信任的 oauth 驗證伺服器的 token 端點進行通信,同時将 saml 令牌交換為一條 oauth acess_token,随後交換 oauth 2.0 的 saml 2.0 grant type。

該 web 應用會作為最終使用者調用一個 api——并随同 api 請求發送 access_token。

api 網關會攔截來自該 web 應用的請求,提取 access_token,與令牌交換端點(或者 sts)進行通信,并由後者驗證該 access_token,而後向該 api 網關提供 jwt(由其簽名)。此 jwt 還攜帶有使用者上下文。在 sts 對 acess_token 進行驗證時,其還将通過 introspection api 與對應的 oauth 授權伺服器進行通信。

api 網關向下遊微服務将同時送出請求與 jwt。

每項微服務都會驗證其接收到的 jwt,而後作為下遊服務調用,其能夠建立新的自簽名 jwt 并将其與該請求一同發送。在其它方案中,亦會用到嵌套 jwt——即由新的 jwt 攜帶上一 jwt。

在上述流程當中,來自外部用戶端的 api 請求将經由該 api 網關。當某項微服務與其它微服務通信時,其将不再需要經過該網關。另外,從特定微服務的角度來看,無論大家是從外部用戶端還是其它微服務處擷取請求,獲得的都是 jwt——也就是說,這是一種對稱安全模式。

授權屬于一項業務功能。每項微服務可以決定使用何種标準以允許各項通路操作。從簡單的授權角度來講,我們可以檢查特定使用者是否向特定資源執行了特定操作。将操作與資源加以結合,也就構成了權限。授權檢查會評估特定使用者是否具備通路特定資源的最低必要權限集合。該資源能夠定義誰可以進行通路,可在通路中具體執行哪些操作。為特定資源聲明必要權限可通過多種方式實作。

xacml 已經成為細粒度通路控制領域的客觀标準。其引入的方式能夠代表通路某種資源所需要的權限集,且具體方法采用基于 xml 的特定域語言(簡稱 dsl)編寫而成。

論微服務安全:保護微服務的兩大方案

上圖所示為 xacml 元件架構。首先,政策管理者需要通過 pap(即政策管理點)定義 xacml 政策,而這些政策将被儲存在政策存儲内。要檢查特定實體是否擁有通路某種資源的權限,pep(即政策執行點)需要攔截該通路請求、建立一條 xacml 請求并将其發送至 xacml pdp(即政策決策點)。該 xacml 請求能夠攜帶任何有助于在 pdp 上執行決策流程的屬性。舉例來說,其能夠包含拒絕辨別符、資源辨別符以及特定對象将對目标資源執行的操作。需要進行使用者授權的微服務則需要與該 pdp 通信并從 jwt 中提取相關屬性,進而建立 xacml 請求。pip(即政策資訊點)會在 pdp 發現 xacml 請求中不存在政策評估所要求的特定屬性時介入。在此之後,pdp 會與 pip 通信以找到缺失的對應屬性。pip 能夠接入相關資料存儲,找到該屬性而後将其傳回至 pdp。

論微服務安全:保護微服務的兩大方案

遠端 pdp 模式存在幾大弊端,其可能與微服務的基本原則發生沖突:

性能成本:每一次被要求執行通路控制檢查時,對應微服務都需要通過線纜與 pdp 進行通信。當該決策被緩存在用戶端時,此類傳輸成本與政策評估成本将得到有效降低。不過在使用緩存機制時,我們亦有可能根據陳舊資料進行安全決策。

政策資訊點(簡稱 pip)的所有權:每項微服務都應當擁有自己的 pip,其了解要從哪裡引入實作通路控制所必需的資料。在以上方案中,我們建立起的一套“整體式” pdp,其中包含全部 pip——對應全部微服務。

論微服務安全:保護微服務的兩大方案

如上圖所示,嵌入式 pdp 将遵循一類事件模式,其中每項微服務都會訂閱其感興趣的主題以從 pap 處擷取合适的通路控制政策,而後更新其内嵌 pdp。大家可以通過微服務組或者全局多租戶模型擷取 pap。當出現新政策或者政策存在更新時,該 pap 會向對應的主題釋出事件。

這套方案不會違反微服務中的“伺服器不變”原則。“伺服器不變”意味着當大家在持續傳遞流程結尾處,直接利用加載自庫的配置建構伺服器或者容器時,整個建立流程應該能夠基于同樣的配置進行不斷重複。是以,我們不希望任何使用者登入伺服器并對配置做出變更。在内嵌 pdp 模式下,盡管伺服器會加載對應的政策,但其仍同時處于運作當中。這意味着當我們啟動新容器時,其仍然立足于同樣的政策集。

原文釋出時間為:2016-07-13

本文來自雲栖社群合作夥伴“linux中國”

繼續閱讀