深度解析Istio系列之安全模塊篇

單體應用拆分爲微服務之後,提高了開發效率,增加系統系統穩定性,提高運維效率等等一系列的好處,但隨之也帶來了安全方面的風險,之前都是本地調用,現在都改爲走網絡協議調用接口,今天着重介紹的是微服務架構中的新貴Istio中安全模塊分析,Istio安全的三大目標:

  • 默認安全(Security by default):應用程序代碼和基礎結構無需更改。
  • 深度防禦(Defense in depth):與現有安全系統集成,提供多層防禦。
  • 零信任網絡(Zero-trust network):在不受信的網絡上,構建安全解決方案。

架構

深度解析Istio系列之安全模塊篇

如上圖,Istio安全涉及到組件有:

  • Citadel 用於密鑰和證書的管理。
  • Proxy 實現客戶端與服務器端安全通信。
  • Pilot 將授權策略和安全命名信息分發給代理。
  • Mixer:校驗授權和審計。

Istio 身份

身份信息

身份信息是安全基礎架構的基本概念,在服務和服務的通信開始前,雙方必須用其身份信息交換憑證,以達到相互認證的目的,根據安全信息達到鑑權的目的,同時根據身份信息可以進行審計,在kubernetes的環境下Istio身份標識使用Service Account。

PKI

PKI(Public Key Infrastructure)建立在Istio citadel 之上,Istio 使用 X.509 證書來攜帶 SPIFFE 格式的身份信息,PKI 還可以大規模自動化地進行密鑰和證書輪換。

證書生成流程

基於kubernetes環境下證書生成流程如下:

深度解析Istio系列之安全模塊篇

citadel同時也會監聽每個證書的生命週期,通過重寫 Kubernetes secret 自動輪換證書。Pilot生成安全信息即授權信息,Pilot將授權信息分發給envoy每個命名空間下有個default serviceaccount,citadel會爲它創建一個名爲istio.default的secret。

[root@kube01 ~]$ kubectl -n foo get secrets 
NAME                  TYPE                                  DATA      AGE 
default-token-bdpmg   kubernetes.io/service-account-token   3         8d 
istio.default         istio.io/key-and-cert                 3         8d

默認在該命名空間下新建的istio應用都使用該secret。

istio-certs:
     Type:        Secret (a volume populated by a Secret)
     SecretName:  istio.default
     Optional:    true

當爲deployment指定了非默認serviceaccount,則祕鑰也將使用新的secret。

istio-certs:
     Type:        Secret (a volume populated by a Secret)
     SecretName:  istio.bookinfo-productpage
     Optional:    true

認證

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

  • 傳輸身份驗證,也稱爲服務到服務身份驗證:驗證直接客戶端進行連接。Istio提供相互TLS 作爲傳輸身份驗證的完整堆棧解決方案。
  • 源身份驗證,也稱爲最終用戶身份驗證:驗證將請求作爲最終用戶或設備的原始客戶端。

具體信息參見官網,本文章主要介紹第一種。

相互 TLS身份驗證(mTLS)

mTLS本身流程如下圖所示:

深度解析Istio系列之安全模塊篇

在Istio握手期間,客戶端envoy還進行安全命名檢查,驗證服務器證書中提供的服務賬戶是否有權限運行目標服務。

認證架構

通過yaml文件配置身份認證策略,部署後策略保存在Istio配置存儲中,Pilot監聽配置存儲,當發生變化後Pilot將策略轉變爲適當的配置(envoy識別的配置等),再通知envoy如何執行身份認證機制,Pilot提供Istio系統管理的密鑰和證書的路徑,並將它們安裝到應用程序mesh以進行相互TLS。Istio通過異步發送配置到目標端點,代理收到消息後,新的身份認證立即生效。

認證架構如下圖所示:

深度解析Istio系列之安全模塊篇

部署影響

在部署Istio平臺時,通過yaml則是istio-demo-auth.yaml,該yaml安裝爲所有控制面板中envoy添加了tls相關信息,在helm中則是global.controlPlaneSecuretyEnable: true,開啓後實質是爲controlPlaneAuthPolicy: MUTUAL_TLS,分析添加該屬性後發生的變化如下:

控制面板

Istio-policy,istio-telemetry,istio-pilot,三個組件在istio-proxy中envoy進程啓動時會指定配置文件,依次爲envoy_policy.yaml,envoy_telemetry.yaml,envoy_pilot.yaml,在這些文件中相同都增加如下:


"tls_context": {
         "common_tls_context": {
 "alpn_protocols": "h2", 
"tls_certificates": {
 "certificate_chain": {
 "filename": "/etc/certs/cert-chain.pem"
 },
 "private_key": {
 "filename": "/etc/certs/key.pem"
 }
 }, 
"validation_context": { 
"trusted_ca": { 
"filename": "/etc/certs/root-cert.pem"
 }, "verify_subject_alt_name": [
 "spiffe://cluster.local/ns/istio-system/sa/istio-pilot-service-account"
 ] 
} 
}

在模板文件地址 實質判斷controlPlaneAuthPolicy的值,如果這個值設置None,則當開啓網格策略mtls後,控制面板各個組件不支持tls,導致轉發失敗,必須設置到控制面板目標規則mode爲DISABLE纔可以,否則會導致握手失敗,報錯503,如下所示:


[2018-10-23 02:55:45.510][19][debug][connection] external/envoy/source/common/network/connection_impl.cc:466] [C127] connected 

[2018-10-23 02:55:45.510][19][debug][connection] external/envoy/source/common/ssl/ssl_socket.cc:113] [C127] handshake error: 2 

[2018-10-23 02:55:45.510][19][debug][connection] external/envoy/source/common/ssl/ssl_socket.cc:113] [C127] handshake error: 5 

[2018-10-23 02:55:45.510][19][debug][connection] external/envoy/source/common/network/connection_impl.cc:133] [C127] closing socket: 0

數據面板

數據面板中該值controlPlaneAuthPolicy:None不影響mtls功能使用(如果有誤請聯繫更改)。

認證策略

Istio可以在命名空間範圍或網格範圍中存儲身份認證策略,使用官網實例sleep訪問httpbin進行測試,訪問過程如下圖所示:

深度解析Istio系列之安全模塊篇

部署官網示例,步驟1:

$ kubectl create ns foo 
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo 
$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo 
$ kubectl create ns bar 
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n bar 
$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n bar 
$ kubectl create ns legacy 
$ kubectl apply -f samples/httpbin/httpbin.yaml -n legacy 
$ kubectl apply -f samples/sleep/sleep.yaml -n legacy

此時執行,步驟2:

for from in "foo" "bar"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done

返回結果皆爲200,目前是未開啓mtls。

網格範圍策略

使用MeshPolicy,沒有目標選擇器部分。可以有最多一個網格範圍的策略在網格中,啓用mtls配置如下:

cat <<EOF | kubectl apply -f - 
apiVersion: "authentication.istio.io/v1alpha1" 
kind: "MeshPolicy" 
metadata:
   name: "default" 
spec:
   peers:
   - mtls: {} 
EOF

此時再次執行步驟2則結果都爲503,需要增加目標規則配置,如下所示:

cat <<EOF | kubectl apply -f - 
apiVersion: "networking.istio.io/v1alpha3" 
kind: "DestinationRule" 
metadata:
   name: "default"
   namespace: "default" 
spec:
   host: "*.local"   trafficPolicy:
     tls:
       mode: ISTIO_MUTUAL 
EOF

此時再次執行步驟2則結果爲200正常。

分析開啓mtls後envoy發生的變化,在listeners的filter鏈中增加了tlsContext相關配置。

//使用istioctl proxy-config分析envoy istioctl proxy-config listeners -n foo httpbin-66dc4dd499-8fjgb --port 8000 -o json 

//輸出如下: 

... 

"filterChains": [
             {
                 "tlsContext": {
                     "commonTlsContext": {
                         "tlsCertificates": [
                             {
                                 "certificateChain": {
                                     "filename": "/etc/certs/cert-chain.pem"
                                 },
                                "privateKey": {
                                     "filename": "/etc/certs/key.pem"
                                 }
                             }
                         ],
                         "validationContext": {
                             "trustedCa": {
                                 "filename": "/etc/certs/root-cert.pem"
                             }
                         },
                         "alpnProtocols": [
                             "h2",
                             "http/1.1"
                         ]
                     },
                     "requireClientCertificate": true
                 } 
...

注意:當controlPanleAuth配置爲None時,只是配置這兩個配置訪問依然爲503,原因是控制面板不支持tls配置,如部署影響中有講到,如果爲None時也想讓該示例生效,需要增加目標規則,目標規則中指定到控制面板的數據流不開啓mtls,使用明文訪問,如下所示:

cat <<EOF | kubectl apply -f - 
apiVersion: "networking.istio.io/v1alpha3" 
kind: "DestinationRule" 
metadata:
   name: "istio-system-manage-dr"
   namespace: "istio-system" 
spec:
   host: "*.istio-system.svc.cluster.local"
   trafficPolicy:
     tls:
       mode: DISABLE 
EOF

當擁有sidecar的應用開啓mtls後想要訪問非Istio服務,也是需要配置tls:DISABLE,如下所示:

cat <<EOF | kubectl apply -f - 
apiVersion: networking.istio.io/v1alpha3 
kind: DestinationRule 
metadata:
  name: "httpbin-legacy" 
spec:
  host: "httpbin.legacy.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: DISABLE 
EOF

命名空間範圍策略

具有名稱default且沒有目標選擇器部分。每個命名空間最多只能有一個名稱空間範圍的策略。它使用kind “Policy”,需要指定命名空間名稱,如下所示:

cat <<EOF | kubectl apply -f - 
apiVersion: "authentication.istio.io/v1alpha1" 
kind: "Policy" 
metadata:
   name: "default"
   namespace: "foo" 
spec:
   peers:
   - mtls: {} 
EOF

與網格範圍策略一樣,也需要指定目標規則,如下所示:

cat <<EOF | kubectl apply -f - 
apiVersion: "networking.istio.io/v1alpha3" 
kind: "DestinationRule" metadata:
   name: "default"
   namespace: "foo" 
spec:
   host: "*.foo.svc.cluster.local"
   trafficPolicy:
     tls:
       mode: ISTIO_MUTUAL 
EOF

開啓命名空間範圍策略只是針對當前命名空間,對於控制面板下的服務沒有任何影響,還是使用明文訪問。

授權

Istio授權功能,基於角色的訪問控制(RBAC),爲Istio中服務提供命名空間級別、服務級別、方法級別的訪問控制,特點是:

  • 基於角色的語義,簡單易用。
  • 服務到服務和最終用戶到服務授權。
  • 通過自定義屬性支持(例如條件,角色和角色綁定)提供靈活性。
  • 高性能,因爲Istio授權在Envoy上本地執行。

授權架構

深度解析Istio系列之安全模塊篇

用戶使用.yaml配置Istio授權策略,部署完成後同樣存儲在Istio配置存儲,Pilot監聽Istio授權變化,如果發生變化Pilot獲取更新的授權策略,將Istio授權策略分發給與服務實例位於同一位置的Envoy代理,每個Envoy代理都運行一個授權引擎,該引擎在運行時授權請求。當請求到達代理時,授權引擎根據當前授權策略評估請求上下文,並返回授權結果,ALLOW或DENY。

啓用授權

使用RbacConfig對象啓用Istio授權,是一個單例,固定名稱爲default,在RbacConfig中可以指定一個mode值,該值可以是:

  • OFF:禁用Istio授權。
  • ON:爲網格中的所有服務啓用了Istio授權。
  • ON_WITH_INCLUSION:僅對inclusion字段中指定的服務和命名空間啓用Istio授權。
  • ON_WITH_EXCLUSION:對網格中的所有服務啓用Istio授權,但exclusion 字段中指定的服務和命名空間除外。

開啓授權,如下所示:

apiVersion: "rbac.istio.io/v1alpha1"
kind: RbacConfig 
metadata:
   name: default 
spec:
   mode: 'ON_WITH_INCLUSION'
   inclusion:
     namespaces: ["default"]

當開啓rbacconfig後,正常的bookinfo示例已經不可以訪問,提示“RBAC: access denied“。

授權策略

使用bookinfo測試授權策略,配置策略,使用ServiceRole和ServiceRolebinding:

  • ServiceRole:定義一組訪問服務的權限。
  • ServiceRoleBinding:將權限授權給特定主體,例如用戶,組,服務。

ServiceRole

每條規則都以以下標準字段:

  • services:服務名稱列表。您可以將值設置*爲包括指定命名空間中的所有服務。
  • methods:HTTP方法名稱列表,對於gRPC請求的權限,HTTP謂詞始終是POST。您可以將值設置*爲包括所有HTTP方法。
  • paths:HTTP路徑或gRPC方法。gRPC方法必須採用以下形式/packageName.serviceName/methodName並且區分大小寫。

具體參考官網。

創建一個ServiceRole,如下所示:

apiVersion: "rbac.istio.io/v1alpha1" 
kind: ServiceRole 
metadata:
   name: service-viewer
   namespace: default 
spec:   rules:
   - services: ["*"]
     methods: ["GET"]
     constraints:
     - key: "destination.labels[app]"
       values: ["productpage", "details", "reviews", "ratings"]

允許只讀訪問default命名空間下所有帶有指定標籤的服務。

ServiceRoleBinding

ServiceRoleBinding規範包括兩個部分:

  • roleRef指的是ServiceRole同一名稱空間中的資源。
  • subjects分配給該角色的列表。

如下所示:

apiVersion: "rbac.istio.io/v1alpha1" 
kind: ServiceRoleBinding 
metadata:
   name: bind-service-viewer
   namespace: default 
spec:
   subjects:
   - properties:
       source.namespace: "istio-system"
   - properties:
       source.namespace: "default"
   roleRef:
     kind: ServiceRole
     name: "service-viewer"

將ServiceRole授權給命名空間爲“istio-system”和“default”。

至此Istio安全模塊分析到此結束,其中還有很多點本文未涉及,請到官網學習(https://istio.io/docs/concepts/security/),官網是第一手資料,同時如果本文中存在的問題,請隨時聯繫馬上改正,謝謝

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