1. 程式人生 > >【譯文連載】 理解Istio服務網格(第七章 安全)

【譯文連載】 理解Istio服務網格(第七章 安全)

全書目錄

  • 第一章 概述

  • 第二章 安裝

  • 第三章 流控

  • 第四章 服務彈性

  • 第五章 混沌測試

  • 第六章 可觀測性

 

本文目錄

第7章 安全

7.1 身份認證

    7.1.1 Kubernetes上的Istio的身份認證方案

    7.1.2 mTLS(雙向TLS)

7.2 訪問授權 - 基於角色的訪問控制(RBAC)

7.3 訪問策略 - 通過Mixer Policy進行訪問控制

7.4 結論 

第7章 安全

越來越多的現代雲原生應用開發體系中都會有好幾個獨立的開發團隊,每個團隊採用不同的開發迭代週期,新功能以周或天為單位迭代上線,只對他們自己的任務負責。Istio的使命是從組成整個應用的所有微服務層面,確保一定程度的連續性。Isitio的關鍵能力之一是實施安全約束,同時對每個服務的邏輯程式碼透明。有了Istio代理後,你就可以在服務之間的網路層面施加這些約束。

將單一應用程式分解為微服務可提供各種好處,包括更好的靈活性、可伸縮性以及服務複用的能力。但是,微服務也有特殊的安全需求:

·       為了抵禦中間人攻擊,需要流量加密。

·       為了提供靈活的服務訪問控制,需要雙向TLS和細粒度的訪問策略。

·       要稽核誰在什麼時候做了什麼,需要審計工具。

Istio Security 嘗試提供全面的安全解決方案來解決所有這些問題。Istio 安全功能提供強大的身份、強大的策略、透明的TLS加密以及用於保護您的服務和資料的身份驗證,授權和審計(AAA)工具,如圖7-1所示。Istio 安全的目標是:

·       預設安全: 應用程式程式碼和基礎結構無需更改

·       深度防禦: 與現有安全系統整合,提供多層防禦

·       零信任網路: 在不受信任的網路上構建安全解決方案

 

圖7-1. Istio安全概述

 

Istio 中的安全性涉及多個元件,其總體架構如圖7-2所示:

·       Citadel 用於金鑰和證書管理

·       Envory用於實現客戶端和伺服器之間的安全通訊

·       Pilot 將授權策略和安全命名資訊分發給代理

·       Mixer 管理授權和審計

 

 

圖7-2. Istio安全架構

 

即使本書中用到的非常簡單的示例應用,其中customer微服務呼叫preference微服務,它又呼叫recommendation微服務,還是有好幾個地方需要應用這些安全約束。

本章中,我們會介紹Istio的基於mTLS的身份認證、基於Mixer Policy的策略控制,以及基於RBAC的授權管控。

7.1 身份認證

7.1.1 Kubernetes上的Istio的身份認證方案

身份是任何安全基礎架構的基本概念。在服務間通訊開始時,雙方必須互相交換身份資訊憑證以進行相互的身份認證。

 

在Istio中,客戶端根據安全命名資訊(secure naming information)來檢查伺服器端的身份標識,並檢視它是否是該服務的授權執行者;伺服器端通過授權策略(authentication policy)來確定客戶端可訪問什麼,稽核誰在什麼時候訪問了什麼,並進行計費等。不同平臺上的Istio採用不同的服務標識。在Kubernetes上Istio採用服務賬戶(service account)作為身份標識,它使用X.509證書來攜帶SPEFFE格式的身份,X.509證書中的URI欄位格式為spiffe://<domain>/ns/<namespace>/sa/<serviceaccount>。

 

建立和管理身份標識的基本流程:

  1. Istio Citadel 元件監視Kubernetes api-server,為每個現有和新的服務帳戶建立SPIFFE 證書和金鑰對。Citadel將證書和金鑰對儲存為 Kubernetes secret。

  2. 建立 pod 時,Kubernetes會根據其服務帳戶通過 Kubernetes secret volume 將證書和金鑰對掛載到 pod 上。

  3. Citadel 監視每個證書的生命週期,並通過重寫 Kubernetes secret自動輪換證書。

  4. Pilot 生成安全命名資訊,該資訊定義了哪些服務帳戶可以執行哪些服務,然後將安全命名資訊傳遞給Envoy 代理。

 

Istio 提供兩種型別的身份驗證:

  • 傳輸身份驗證(transport authentication),也稱為服務間身份驗證:驗證建立連線的直接客戶端。 Istio 提供雙向TLS 作為傳輸身份驗證的完整解決方案。 您可以輕鬆啟用此功能,而無需更改服務程式碼。這個解決方案:

  • 為每個服務提供強大的身份,表示其角色,以實現跨群集和雲的互操作性。

  • 保護服務到服務通訊和終端使用者到服務的通訊。

  • 提供金鑰管理系統,以自動執行金鑰和證書生成,分發和輪換。

  • 來源身份認證(origin authentication),也稱為終端使用者身份驗證:驗證作為終端使用者或裝置發出請求的原始客戶端。Istio 通過 JSON Web Token(JWT)驗證和 ORY Hydra、Keycloak、Auth0、Firebase Auth、Google Auth 和自定義身份驗證來簡化開發人員體驗,並且輕鬆實現請求級別的身份驗證。 

在這兩種情況下,Istio都通過自定義Kubernetes API將身份認證策略儲存在Istio配置儲存中。Pilot會在適當的時候為每個代理保持最新狀態以及金鑰。

7.1.2 mTLS(雙向TLS)

傳輸層安全性協議(Transport Layer Security,縮寫TLS)及其前身安全套接層(Secure Sockets Layer,縮寫SSL)是一種安全協議,目的是為網際網路通訊提供安全及資料安全性保障。Netscape公司在1994年推出HTTPS協議,以SSL進行加密,這是SSL的起源。SSL包括1.0,2.0和3.0等三個版本。IETF(Internet Engineering Task Force,網際網路工程任務組)將SSL標準化,並將其稱為TLS。從技術上講,TLS 1.0與SSL 3.0的差異非常微小,目前TLS已發展到了1.3版本。預設地,TLS協議只通過X.509證書向客戶端證明伺服器端的身份,而向伺服器端證明客戶端的身份這事情則留給應用自己去做。Mutual TLS(mutual Transport Layer Security,雙向TLS,簡稱mTLS)則能讓兩端都能證明對端的身份,因此能保障通訊在兩個方向上都是安全和可信的。mTLS是TLS協議的一種補充和增強。

 

在Istio服務網格中,mTLS在有注入邊車Istio代理的兩個微服務之間啟用加密通訊。預設地,示例程式中的三個服務之間的通訊是明文的,只使用了HTTP協議。這意味著,其它能訪問你叢集的團隊,可部署一個自己的服務,然後抓取你應用之間的通訊資料。要做到這一點,只需要開啟兩個終端,一個執行tcpdump,另一個執行curl命令。

 

終端1:

    PREFPOD=$(oc get pod -n tutorial -l app=preference -o \

    'jsonpath={.items[0].metadata.name}')

    oc exec -it $PREFPOD -n tutorial -c istio-proxy /bin/bash

    sudo tcpdump -A -s 0    'tcp port 8080 and (((ip[2:2]-((ip[0]&0xf)<<2))-((tcp[12]&0xf0)>>2))!= 0)'

 

終端2:

    PREFPOD=$(oc get pod -n tutorial -l app=preference -o \

    'jsonpath={.items[0].metadata.name}')

    oc exec -it $PREFPOD -n tutorial -c preference /bin/bash

    curl recommendation:8080

 

然後在終端1中你會看到以下輸出:

    ..:...:.HTTP/1.1 200 OK

    content-length: 47

    x-envoy-upstream-service-time: 0

    date: Mon, 24 Dec 2018 17:16:13 GMT

    server: envoy

    recommendation v1 from '66b7c9779c-75fpl': 345

 

在終端2中你會看到:

    recommendation v1 from '66b7c9779c-75fpl': 345

curl命令正常輸出,同時在tcpdump命令的輸出中能看到明文資訊,如圖7-3所示:

 

圖7-3. 未使用mTLS時的三個終端的輸出

 

要在Istio中啟用mTLS,只需要使用Policy和DestionationRule物件。Policy定義如下:

    apiVersion: "authentication.istio.io/v1alpha1"

    kind: "Policy"

    metadata:

      name: "default"

      namespace: "tutorial"

    spec:

      peers:

      - mtls: {}

 

該策略會被應用到tutorial名稱空間內的所有服務上。你還可以設定spec.peers.mtls.mode欄位值為“PERMISSIVE”(寬容)而不是“STRICT”(嚴格),這會允許服務能同時支援mTLS和非mTLS通訊,從而相容那些還沒有采用Istio邊車代理的服務。

 

我們的三個服務都已經被注入了Istio邊車代理,因此我們可以在Tutorial名稱空間上應用mTLS。

 

在第三個終端中,應用該Policy宣告:

    oc apply -n tutorial -f istiofiles/authentication-enable-tls.yml

 

現在,定義DestionationRule宣告,它在tutorial名稱空間的服務間啟用mTLS:

    apiVersion: "networking.istio.io/v1alpha3"

    kind: "DestinationRule"

    metadata:

      name: "default"

      namespace: "tutorial"

    spec:

      host: "*.tutorial.svc.cluster.local"

      trafficPolicy:

        tls:

          mode: ISTIO_MUTUAL

應用它:

    oc apply -n tutorial -f istiofiles/destination-rule-tls.yml

然後你在終端2中再次執行curl命令,結果如圖7-4所示:

 

圖7-4.啟用了mTLS後的三個終端

 

你看到了tcpdump終端中再也不輸出明文資訊了,同時curl命令還是成功執行了。你還可以使用istioctl命令去驗證這三個服務的mTLS是否啟用成功:

istioctl authn tls-check customer -n tutorial

istioctl authn tls-check preference -n tutorial

istioctl authn tls-check recommendation -n tutorial

 

現在我們從叢集外面進行測試。在終端2中,從preference容器中退出回到宿主機上。使用curl訪問customer服務端點,你會收到“Empty reply from server”返回:

    curl customer-tutorial.$(minishift ip).nip.io

    curl: (52) Empty reply from server

 

上面用到的customer服務URL是由OpenShift Route產生的。在啟用了mTLS後,你需要利用一個閘道器來獲得端到端的加密通訊。Istio有它自己的入口閘道器,名為Istio Gateway,它暴露URL給到網格外面,支援Istio的監控、流控和策略等功能。

 

在Istio中,Gateway實際上是一個執行在網格邊緣的提供L4-L6負載均衡能力的負載均衡器,負責接收進入網格或出網格的HTTP/TCP流量,控制著網格邊緣的服務暴露。閘道器根據流入流出方向分為Ingress Gateway(入口閘道器)和Egress Gateway(出口閘道器)。前者控制從外部進入網格的訪問,配合VirtualService使用;後者控制從網格內訪問外部服務,配合DestionationRule和ServiceEntry使用。它還支援對外的mTLS。Gateway和普通邊車代理一樣都使用Envoy作為代理來進行流量控制。外部訪問流量進入網格再出網格的路徑如圖7-2所示。

 

要為customer服務設定Istio閘道器,建立Gateway宣告以及VirtualService宣告:

    apiVersion: networking.istio.io/v1alpha3

    kind: Gateway

    metadata:

      name: customer-gateway

      namespace: tutorial

    spec:

      selector:

        istio: ingressgateway # use istio default controller

      servers:

      - port:

          number: 80

          name: http2

          protocol: HTTP

 hosts:

   - "*"

    ---

    apiVersion: networking.istio.io/v1alpha3

    kind: VirtualService

    metadata:

      name: customer

      namespace: tutorial

    spec:

      hosts:

      - "*"

      gateways:

      - customer-gateway

      http:

      - match:

        - uri:

            exact: /

        route:

        - destination:

            host: customer

            port:

              number: 8080

 

然後應用這個宣告:

oc apply -f istiofiles/gateway-customer.yml

 

上面這個Gateway宣告暴露了名為“http2”的80埠,允許HTTP協議(備註:80埠在Ingress Gateway服務被安裝時就被配置了且名為“http2”,因此,實質上,這個Gateway物件只是重用這個已有的埠。如果要使用新埠的話,則需要修改istio-ingressgateway服務以增加新的埠然後再使用它)。這個物件會被應用到由spec.selector欄位指定的pod上,在當前環境中即istio-system名稱空間中的istio-ingressgateway pod。然後,Istio會配置這個pod中的代理(Envory)在80埠上開始監聽,準備接收發過來的請求。這個埠會被對映為一個Kubernetes叢集的節點埠(NodePort),用於從叢集外訪問。上面這個VirtualService物件會被繫結到由spec. gateways欄位指定的Gateway物件上,它負責控制Gateway收到請求後的轉發目標,其由route.desitionaion欄位指定,本例中為customer:80。

前面說過,Istio Gateway服務為這個Gateway物件暴露了一個NodePort,用於從叢集外通過該埠訪問VirtualService物件所指向的服務。該埠繫結在minishift或minikube的IP地址上。通過下面的命令獲得這個NodePort埠號,然後拼裝成閘道器URL:

     INGRESS_PORT=$(oc -n istio-system get service  \                                       istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')

    GATEWAY_URL=$(minishift ip):$INGRESS_PORT

    curl http://${GATEWAY_URL}/

通過閘道器URL發起一個curl命令,輸出正常,如圖7-5所示:

 

圖7-5.使用Istio customer 閘道器後的三個終端

 

清理環境:

    oc delete -n tutorial -f istiofiles/gateway-customer.yml

    oc delete -n tutorial -f istiofiles/destination-rule-tls.yml

    oc delete -n tutorial -f istiofiles/authentication-enable-tls.yml

 

回到最初的通過OpenShift Route暴露出的customer服務的地址:

    oc expose service customer

    curl customer-tutorial.$(minishift ip).nip.io

 

小結一下,客戶端通過雙向TLS呼叫服務端的主要流程如下:

  1.  Istio 將出站流量從客戶端重新路由到客戶端的本地Envoy代理。

  2. 客戶端 Envoy與伺服器端Envoy開始雙向TLS握手。在握手期間,客戶端Envoy還從伺服器端的X.509證書中獲取服務賬戶名稱,並與Pilot已下發的安全命名資訊資料進行比對,以進行安全命名檢查,以驗證伺服器證書中顯示的服務帳戶是否被授權執行到目標服務。

  3. 客戶端Envoy和伺服器端Envoy建立了一個雙向的TLS連線,Istio將流量從客戶端 Envoy轉發到伺服器端Envoy。

  4. 伺服器端對客戶端進行訪問授權檢查,伺服器端Envoy代理從客戶端的X.509證書中獲取服務賬戶名稱,並與已有授權策略核對,以確定客戶端有訪問伺服器端功能的許可權。確認後,伺服器端Envoy通過本地TCP連線將流量轉發到伺服器服務。

 

客戶端通過雙向TLS呼叫服務端的過程中,伺服器端會對客戶端進行授權檢查。Istio提供RBAC認證功能,用於定義哪些服務可以被哪些使用者訪問;還提供Mixer Policy,用於定義服務的訪問控制列表(ACL)。

7.2 訪問授權 - 基於角色的訪問控制(RBAC)

Istio RBAC定義了ServiceRole和ServiceRoleBinding兩個型別。ServiceRole定義了一組規則(rule),每條規則包括service(目標服務)、method(訪問服務的方法)、path(HTTP路徑或gRPC方法)等欄位。ServiceRoleBinding物件則包括指向某ServiceRole物件的roleRef,以及一組實體(subject,比如使用者),用於向實體分配ServiceRole物件所定義的訪問許可權。

 

請注意,使用Istio RBAC之前要啟用mTLS,因為伺服器端要利用mTLS獲取客戶端的身份資訊。

 

首先確保你的環境中沒有以下物件:

    oc get destinationrule -n tutorial

    oc get virtualservice -n tutorial

    oc get gateway -n tutorial

    oc get policy -n tutorial

 

啟用Istio RBAC支援很簡單,只需要使用RbacConfig物件:

    apiVersion: "rbac.istio.io/v1alpha1"

    kind: RbacConfig

    metadata:

      name: default

    spec:

      mode: 'ON_WITH_INCLUSION'

      inclusion:

        namespaces: ["tutorial"]

 

其中mode可以是以下值:

  • OFF:禁用Istio RBAC認證

  • ON:對網格中的所有服務啟用Istio RBAC認證

  • ON_WITH_INCLUSION:只對inclusion欄位指定的服務和名稱空間啟用Istio RBAC認證

  • ON_WITH_EXCLUSION:對除了由exclusion欄位指定的服務和名稱空間外的所有服務啟用Istio RBAC認證

 

建立RbacConfig物件:

    oc create -f istiofiles/authorization-enable-rbac.yml   -n tutorial

 

現在,用curl訪問customer服務端點,你會收到如下錯誤:

    curl customer-tutorial.$(minishift ip).nip.io

    RBAC: access denied

 

Istio RABC預設使用拒絕策略,意味著除非顯式宣告對某個使用者的訪問許可權,其它訪問都不被允許。要重新開啟customer服務端點的訪問,要建立ServiceRole和ServiceRoleBinding宣告:

    apiVersion: "rbac.istio.io/v1alpha1"

    kind: ServiceRole

    metadata:

      name: service-viewer

      namespace: tutorial

    spec:

      rules:

      - services: ["*"]

        methods: ["GET"]

        constraints:

        - key: "destination.labels[app]"

          values: ["customer", "recommendation", "preference"]

    ---

    apiVersion: "rbac.istio.io/v1alpha1"

    kind: ServiceRoleBinding

    metadata:

      name: bind-service-viewer

      namespace: tutorial

    spec:

      subjects:

      - user: "*"

      roleRef:

        kind: ServiceRole

        name: "service-viewer"

應用它:

    oc -n tutorial apply -f istiofiles/namespace-rbac-policy.yml

 

再嘗試curl命令:

    curl customer-tutorial.$(minishift ip).nip.io

    customer => preference => recommendation v1 from

'66b7c9779c': 36

Istio的ServiceRole物件允許你通過名字或使用destionation.labels[app]欄位來指定被保護的目標服務。你還可以在methods欄位中指定允許的操作,比如GET或POST。ServiceRoleBinding物件允許你指定哪些使用者(由其spec. subjects. user欄位指定)被允許以什麼方式(由ServiceRole的methods欄位指定)訪問什麼服務(由ServiceRole的services欄位指定)。上面的示例中,user欄位的值為“*”,這意味著任何使用者都可以訪問這些服務。

 

請注意,Istio RBAC在Istio 1.5及以後的版本中就不再推薦使用了,而且會在1.6版本中被移除,取而代之的是Authorization Policy。更新資訊,請訪問Istio Security網站(https://istio.io/docs/concepts/security/)。

7.3 訪問策略 - 通過Mixer Policy進行訪問控制

Istio的Mixer Policy服務允許你建立ACL規則去保證應用中的各微服務都遵循一個被允許的呼叫路徑。在示例程式中,只允許customer服務呼叫preference服務,preference服務呼叫recommendation服務。其它路徑都需要被阻止:

  • Customer服務不允許呼叫recommendation服務

  • Preference服務不允許呼叫customer服務

  • Recommendation服務不允許呼叫customer服務

  • Recommendation服務不允許呼叫preference服務

 

Istio為這種訪問控制提供了幾個物件或型別,包括denier、checknothing和rule。在使用denier和rule之前,我們來看一下這三個服務之間無序呼叫帶來的風險,這往往容易被忽視。

 

首先,獲得recommendation服務pod的名稱和id,然後進入業務容器:

    RECPOD=$(oc get pod -n tutorial -l app=recommendation -o \

    'jsonpath={.items[0].metadata.name}')

    oc exec -it $RECPOD -n tutorial -c recommendation /bin/bash

 

然後,通過curl訪問customer服務,這能成功:

    curl customer:8080

    customer => preference => recommendation v2 from  '7cbd9f9c79': 23

 

再通過curl訪問preference服務:

    curl preference:8080

    preference => recommendation v1 from '66b7c9779c': 152

 

實際上,業務模式已經確定了只有customer->preference->recommendation這條呼叫路徑是正確的。你可以使用denier和rule來把其它路徑禁止掉。這些規則有些長,在應用之前要記得檢視它們的宣告。

 

Rule語法的很直接,基於源(source)和目標(destination)。檢視pod標籤:

    oc get pods -n tutorial --show-labels

 

輸出如下:

     NAME                READY STATUS   LABELS

    customer-6564ff969f          2/2   Running  app=customer, version=v1

    preference-v1-5485dc6f49     2/2   Running  app=preference,version=v1

    recommendation-v1-66b7c9779c 2/2   Running  app=recommendation,  version=v1

    recommendation-v2-7cbd9f9c79 2/2   Running  app=recommendation, version=v2

應用這些規則:

oc -n tutorial apply -f istiofiles/acl-deny-except-

customer2preference2recommendation.yml

現在,再進入recommendation服務,用curl命令訪問preference服務:

    curl preference:8080

    PERMISSION_DENIED:do-not-pass-go.denier.tutorial:

    Customer -> Preference -> Recommendation ONLY

 

檢查正常呼叫路徑沒受到影響:

    curl customer-tutorial.$(minishift ip).nip.io

    customer => preference => recommendation v2 from

'7cbd9f9c79': 238

使用oc或kubectl命令檢視rules:

    oc get rules

    NAME                              AGE

    no-customer-to-recommendation     3m

    no-preference-to-customer         3m

    no-recommendation-to-customer     3m

    no-recommendation-to-preference   3m

    oc describe rule no-preference-to-customer

    ...

    Spec:

      Actions:

        Handler:  do-not-pass-go.denier

        Instances:

          just-stop.checknothing

      Match:  source.labels["app"]=="preference" &&

       destination.labels["app"] == "customer"

    Events:   <none>

 

你可以刪除這些規則以恢復之前的狀態:

    oc delete rules --all -n tutorial

 

Istio Mixer還支援whitelist(白名單)和blacklist(黑名單)機制,通過listchecker和listentry物件。如果你感興趣,可查閱Istio Tutorial文件(https://github.com/redhat-developer-demos/istio-tutorial)或Istio文件(https://istio.io/docs/)。

 

請注意,Mixer Policy在Istio 1.5及以後的版本中就不再推薦使用了。

7.4 結論

前面我們快速學習了關於Istio服務網格安全方面的一些知識。你看到了Istio是如何解決在雲原生環境中的分散式系統的問題的,還有關於Istio可觀測性、彈性、混沌注入等方面的知識。這些都可以被立即應用到你的應用中。

 

而且,Istio還有很多本書未介紹的功能。如果感興趣,我們建議你深入學習以下方面的知識:

  • Policy enforcement(策略增強)

  • Mesh expansion(網格擴充套件)

  • Hybrid deployments(混合部署)

  • Phasing in Instio in an existing environment(在已有系統中使用Istio)

  • Gateway/Advanced ingress(閘道器和高階入口)

 

Istio一直處於快速更新中。要跟進最新進展,建議你關注Istio官網(https://istio.io/)和Red Hat會一直更新的Istio Tutorial網站(https://github.com/redhat-developer-demos/istio-tutorial)。

書籍英文版下載連結為 https://developers.redhat.com/books/introducing-istio-service-mesh-microservices/,作者 Burr Sutter 和 Christian Posta。 

本中文譯稿版權由本人所有。水平有限,錯誤肯定是有的,還請海涵。 

 感謝您的閱讀,歡迎關注我的微信公眾號:

&n