單體應用拆分爲微服務之後,提高了開發效率,增加系統系統穩定性,提高運維效率等等一系列的好處,但隨之也帶來了安全方面的風險,之前都是本地調用,現在都改爲走網絡協議調用接口,今天着重介紹的是微服務架構中的新貴Istio中安全模塊分析,Istio安全的三大目標:
- 默認安全(Security by default):應用程序代碼和基礎結構無需更改。
- 深度防禦(Defense in depth):與現有安全系統集成,提供多層防禦。
- 零信任網絡(Zero-trust network):在不受信的網絡上,構建安全解決方案。
架構
如上圖,Istio安全涉及到組件有:
- Citadel 用於密鑰和證書的管理。
- Proxy 實現客戶端與服務器端安全通信。
- Pilot 將授權策略和安全命名信息分發給代理。
- Mixer:校驗授權和審計。
Istio 身份
身份信息
身份信息是安全基礎架構的基本概念,在服務和服務的通信開始前,雙方必須用其身份信息交換憑證,以達到相互認證的目的,根據安全信息達到鑑權的目的,同時根據身份信息可以進行審計,在kubernetes的環境下Istio身份標識使用Service Account。
PKI
PKI(Public Key Infrastructure)建立在Istio citadel 之上,Istio 使用 X.509 證書來攜帶 SPIFFE 格式的身份信息,PKI 還可以大規模自動化地進行密鑰和證書輪換。
證書生成流程
基於kubernetes環境下證書生成流程如下:
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握手期間,客戶端envoy還進行安全命名檢查,驗證服務器證書中提供的服務賬戶是否有權限運行目標服務。
認證架構
通過yaml文件配置身份認證策略,部署後策略保存在Istio配置存儲中,Pilot監聽配置存儲,當發生變化後Pilot將策略轉變爲適當的配置(envoy識別的配置等),再通知envoy如何執行身份認證機制,Pilot提供Istio系統管理的密鑰和證書的路徑,並將它們安裝到應用程序mesh以進行相互TLS。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進行測試,訪問過程如下圖所示:
部署官網示例,步驟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上本地執行。
授權架構
用戶使用.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/),官網是第一手資料,同時如果本文中存在的問題,請隨時聯繫馬上改正,謝謝。