Istio系列一:Istio的認證授權機制分析
- 2019 年 12 月 11 日
- 筆記
隨著虛擬化技術的不斷發展,以容器為核心的微服務概念被越來越多人認可,Istio因其輕量級、服務網格管理理念、兼容各大容器編排平台等優勢在近兩年脫穎而出,許多公司以Istio的架構設計理念作為模板來管理自己的微服務系統,但在其勢頭髮展猛勁之時,也有許多專家針對Istio的安全機制提出了疑問,比如在Istio管理下,服務間的通訊數據是否會泄露及被第三方劫持的風險;服務的訪問控制是否做到相對安全;Istio如何做安全數據的管理等等,這些都是Istio目前面臨的安全問題,而我們只有深入分析其機制才能明白Istio是如何做安全的。
本文為Istio系列的首篇,後續還有三篇分別對Istio組件Envoy、Pilot、Mixer的原理解讀,本篇作為開胃菜,首先介紹Istio背景及主要架構,再從身份認證和授權鑒權兩方面對Istio的認證授權機制加以剖析,最後通過實驗分析具體講述Istio如何做訪問控制,文章閱讀時間大致15分鐘,希望能給各位讀者帶來收穫。
Istio概述
Istio是由Google、IBM、Lyft聯合開發的開源項目,它是一款微服務管理框架,也被稱為第二代微服務Service Mesh代表。Istio的架構思想類似軟體定義網路(SDN),其主要分為控制平面和數據平面兩個部分,下圖為Istio官方架構圖:

圖1 Istio架構圖
數據平面由一組Sidecar方式部署的智慧代理Envoy(上圖中的Proxy)組成,Envoy是Istio默認的數據平面代理,這些代理用於調節和控制服務及Mixer之間的所有網路通訊。
控制平面按各自功能分為以下四個組件:
- Mixer:負責前置條件檢查(例如ACL檢查、白名單檢查、日誌檢查),遙測報告上報(日誌、監控、配額),配額管理(限流);
- Pilot:負責管理和配置Envoy,將流量路由到各服務中;
- Galley:在Istio 1.1版本被引入,是整個控制平面的配置管理中心,除了提供配置驗證功能以外,還負責配置的管理和分發,使用網路配置協議和其它組件進行配置交互;
- Citadel:負責管理密鑰和證書,用於保證數據平面各服務的通訊安全;
下面本文將著重介紹Istio在安全方面的設計和實現機制。
Citadel認證授權機制
Istio的認證授權機制主要是由Citadel完成,同時需要和其它組件一起配合,參與到其中的組件還有Pilot、Envoy、Mixer,它們四者在整個流程中的作用分別為:
- Citadel:用於負責密鑰和證書的管理,在創建服務時會將密鑰及證書下發至對應的Envoy代理中;
- Pilot: 用於接收用戶定義的安全策略並將其整理下發至服務旁的Envoy代理中;
- Envoy:用於存儲Citadel下發的密鑰和證書,保障服務間的數據傳輸安全;
- Mixer: 負責管理授權完成審計工作。
Istio通過以上四個組件的協同工作可實現服務間的身份認證以及授權鑒權功能。
下面從Istio在Kubernetes中的工作流程來舉例,如下圖所示:

圖2 Istio拓撲圖
具體工作流程可描述如下:
Kubernetes某集群節點新部署了服務A和服務B,此時集群中有兩個Pod被啟動,每個Pod由Envoy代理容器和Service容器構成,在啟動過程中Istio的Citadel組件會將密鑰及證書依次下發至每個Pod中的Envoy代理容器中,以保證後續服務A,B之間的安全通訊。
用戶通過Rules API下發安全策略至Pilot組件,Pilot組件通過Pilot-discovery進程整理安全策略中Kubernetes服務註冊和配置資訊並以Envoy API方式暴露給Envoy。
Pod A, B中的Envoy代理會通過Envoy xDS API方式定時去Pilot拉取安全策略配置資訊,並將資訊保存至Envoy代理容器中。
當服務A訪問服務B時,會調用各自Envoy容器中的證書及密鑰實現服務間的mTLS通訊,同時Envoy容器還會根據用戶下發的安全策略進行更細粒度的訪問控制。
Mixer在整個工作流中核心功能為前置條件檢查和遙測報告上報,在每次請求進出服務A,B時,服務A,B中的Envoy代理會向Mixer發送check請求,檢查是否滿足一些前提條件,比如ACL檢查,白名單檢查,日誌檢查等,如果前置條件檢查通過,處理完後再通過Envoy向Mixer上報日誌,監控等數據,從而完成審計工作。
身份認證分析
Istio官方的身份認證架構如下圖所示:

圖3 Istio的身份認證架構圖
Istio的身份認證流程可以概括為以下步驟:
Administrators制定三個策略,其中一個策略作用域在Foo命名空間,目標服務為Service A,另兩個策略作用域在Bar命名空間,目標服務分別為Service B和All。
Pilot根據Administrators下發的三個策略中的元數據(Pod、Service、Deployment等)調用Kubernetes API Server監視配置存儲,有任何策略變更後,會將新策略轉換為適當配置並將身份驗證以及憑證中的其它聲明(如果適用)輸出至服務對應的Envoy代理。
Envoy代理根據身份驗證資訊以及憑證資訊對服務進行訪問驗證。
以傳輸身份認證舉例,傳輸身份驗證可以理解為服務到服務的身份驗證,Istio提供mTLS(雙向TLS)功能來實現。
在整個身份認證過程中,涉及到兩種證書和私鑰,分別為root-cert.pem、cert-chain.pem、key.pem。其中:
- root-cert.pem:用於證書校驗的根證書,所有Envoy共用同一個root-cert.pem;
- cert-chain.pem:Envoy的證書,由root-cert.pem簽署,會在需要時提供給對端的服務;
- key.pem:Envoy的私鑰,和cert-chain.pem中的證書相匹配;
這三個文件在當服務被創建時,由Citadel組件管理並傳遞至服務對應的Envoy代理中。
以Istio官方的bookinfo demo舉例,我們可以進到productpage服務的Envoy代理容器中查看/etc/certs目錄,如下圖所示:

圖4 Envoy代理容器的證書及密鑰
圖中紅框部分即為證書和私鑰,證書的有效期限默認為三個月,過期後Citadel會對證書密鑰進行更換。
當開啟了mTLS後,服務間的流量為加密流量,並且相互根據證書以及密鑰進行訪問從而保障服務間的通訊安全。值得一提的是,在同一個Pod中服務與Envoy代理之間通訊採用的是localhost,不使用加密流量。
另外,一般會出現這種情況,即沒有注入Envoy代理容器的服務與設置了mTLS的服務之間通訊,通常情況下為了微服務安全性需要為未注入Sidecar容器的服務進行證書配置,或是在訪問時附加上密鑰及證書資訊。
授權鑒權分析
Istio中服務間的授權鑒權主要由Kubernetes的RBAC(基於角色的訪問控制)功能實現。
在微服務架構中,服務間的調用我們可以理解為一個C-S模式。在默認不開啟授權的情況下,服務間的訪問是無限制的。開啟授權模式後,默認情況下必須顯示進行授權才能對服務進行訪問。
這裡會有兩種級別的訪問控制:
命名空間級別
指定命名空間內的所有(或部分)服務可以被另一命名空間的所有(或部分)服務所訪問,需要用戶創建ServiceRole、ServiceRoleBinding策略來實現此過程。
服務級別
指定服務可以被另一個服務訪問,需要用戶創建Service Account、ServiceRole、ServiceRoleBinding策略來實現此過程。
Istio官方授權鑒權的架構圖如下所示:

圖5 Istio授權、鑒權架構圖
根據圖5整個工作流程可描述為:
用戶使用yaml文件指定Istio授權策略。部署後,Istio將策略存至Istio Config Store中;
Pilot通過Kubernetes API Server監控Istio授權策略變更,如果有更改,則獲取最新的授權策略;
Pilot將授權策略下發至服務的Envoy代理;
每個Envoy代理都運行一個授權引擎,在引擎運行時對請求進行授權。
實驗分析
同樣以bookinfo demo舉例,默認情況下,各個服務間通過頁面可以進行訪問,我們通過
kubectl create -f samples/bookinfo/platform/kube/rbac/rbac-config-ON.yaml
可以開啟授權模式,yaml文件如下圖所示:

圖6開啟授權模式yaml文件內容
再次訪問bookinfo頁面時如下圖所示:

圖7 訪問productpage頁面
由圖7可知,Istio授權行為默認為拒絕,所以必須要顯示授權,才能對其服務進行訪問。
實驗前提:
- 部署bookinfo實例時用TLS模式,即istio-demo-auth.yaml。
- 在下面實驗里,我們會在 Service account 的基礎上啟用訪問控制,為了給不同的微服務以不同的訪問授權,需要建立一系列不同的 Service account,用這些帳號來分別運行 Bookinfo 中的微服務。
創建Service Account需執行以下命令:
kubectl create -f bookinfo-add-serviceaccount.yaml
此命令創建了兩個ServiceAccount,如下圖所示:

圖8 查看添加的兩個Service Account
命名空間級的訪問控制
Bookinfo demo中,productpage、reviews、details 以及 ratings 服務被部署在 default 命名空間中,而 istio-ingressgateway等 Istio 組件是部署在 istio-system 命名空間中。我們可以定義一個策略,default 命名空間中所有服務,如果其 app label取值在 productpage、reviews、details 以及 ratings 範圍內,就可以被default命名空間內以及 istio-system 命名空間內的服務進行訪問。
執行以下命令:
istioctl create -f samples/bookinfo/platform/kube/rbac/namespace-policy.yaml
返回結果如下:

圖9 創建基於命名空間的授權策略
我們簡單看下這個策略文件內容:

圖10 基於命名空間的策略內容
如上圖紅框部分所示,這個策略文件定義了兩個對象分別為ServiceRole和ServiceRoleBinding,含義如下所示:
- ServiceRole:創建名為 service-viewer 的 ServiceRole,允許訪問 default 命名空間中所有 app 標籤值在 productpage、reviews、details以及 ratings 範圍之內的服務。
- ServiceRoleBinding:創建 ServiceRoleBinding 對象,用來把 service-viewer 角色指派給所有 istio-system 和 default 命名空間的服務。
執行命令後,再通過網頁訪問bookinfo樣例,發現服務正常了,如下圖所示:

圖11 訪問載入了命名空間的策略頁面
服務級的訪問控制
執行下一策略之前先將之前的策略移除,如圖12所示:

圖12 移除基於命名空間訪問的策略
服務級的訪問控制由於Bookinfo demo的服務調用關係,需要逐步進行訪問控制,首先允許到ProductPage服務的訪問,執行以下命令:
kubectl apply -f productpage-policy.yaml
返回結果如下:

圖13 productpage頁面的訪問策略執行
我們簡單看下productpage-policy.yaml文件的內容:

圖14 productpage頁面的訪問策略內容
如圖14所示,該策略首先創建了一個名為productpage-viewer的ServiceRole,其作用為允許到 productpage 服務的讀取訪問;再創建一個名為bind-productpage-viewer的ServiceRoleBinding,將角色productpage-viewer賦給所有用戶。
再次瀏覽bookinfo demo頁面,如下圖所示:

圖15 訪問載入了productpage策略的頁面
由上圖我們可以看出,訪問productpage服務沒有問題了,但左邊紅框的detail服務和右邊紅框的review服務顯示無法訪問,這是因為我們還沒有給productpage訪問details和reviews的許可權,所以我們再執行下一條命令:
istioctl create -f samples/bookinfo/platform/kube/rbac/details-reviews-policy.yaml
返回結果如下:

圖16 details和reviews的訪問策略執行
我們簡單看下details-reviews-policy.yaml這個文件的內容:

圖17 details和reviews的訪問策略內容
由圖17可看出,該策略首先創建了一個名為details-reviews-viewer的ServiceRole對象,它的作用為允許對 details 和 reviews 服務進行只讀訪問;緊接著又創建了一個名為bind-details-reviews的ServiceRoleBinding對象,用於將details-reviews-viewers對象綁定到cluster.local/ns/default/sa/bookinfo-productpage中去,也就是bookinfo-productpage服務的ServiceAccount,再次訪問頁面,如下圖所示:

圖18 訪問載入details和reviews策略的頁面
由圖18可看出,details和reviews服務可以被訪問了,但ratings服務仍然無法訪問,原因是 reviews 服務無權訪問 ratings 服務,要更正這一問題,就應該給 ratings 服務授權,使其能夠訪問 reviews 服務,所以執行以下命令:
istioctl create -f samples/bookinfo/platform/kube/rbac/ratings-policy.yaml
我們簡單看下文件內容,如下圖所示:

圖19 對ratings服務授權的yaml文件內容
執行文件後再次訪問頁面如下圖所示:

圖20 訪問載入了ratings策略的頁面
可以看到已經可以正常訪問所有的服務。
通過以上兩個實驗結果可以看出,服務之間想要互相訪問首先需要服務賬戶(Service Account),以上兩個實驗的Service Account為productpage和reviews, 具體為何需要這兩個Service Account是因為productpage服務需要允許對reviews和details兩個服務的訪問許可權,reviews服務需要允許對Ratings服務的訪問許可權。這就是實驗開始前添加這兩個ServiceAccount的原因,相信各位讀者看到這裡應該知道Istio是如何做訪問控制了,我們可以看出其核心還是調用了Kubernetes中的RBAC機制。
總結
將單一應用程式拆分為微服務帶來了各種好處,包括更好的靈活性、可伸縮性以及服務復用的能力,但微服務也面臨著許多特殊的安全需求,比如防止中間人攻擊,需要服務間進行流量加密;為了提供靈活的訪問控制,需要mTLS和更細粒度的訪問策略;還有大量的數據需要審計工具來進行審計。
Istio在設計之初就將部分安全機制考慮了進去,Istio官方宣稱在安全上目標要達到默認安全(即應用程式程式碼和基礎結構無需更改)、深度防禦(與現有安全系統集成,提供多層防禦)、零信任網路(在不受信任的網路上構建安全解決方案)。
目前看來Istio在安全方面的實現還是主要依靠Kubernetes一些內置的安全機制。對於微服務的身份認證主要依賴於與雙向TLS及JSON Web Token;授權鑒權策略主要依賴於Kubernetes中的RBAC機制,使用ServiceRole以及ServiceRoleBinding等CRD(CustomResourceDefinition)對象來實現。微服務安全任重而道遠,Istio想實現全面的安全防護還需更多的精力投入。
下一篇將介紹Istio的數據平面組件Envoy,筆者會詳細解釋Envoy在Istio中是如何部署以及如何對入站出站流量進行代理轉發及流量劫持,歡迎各位讀者持續關注。
內容編輯:星雲實驗室 浦明 責任編輯:肖晴