Istio 中的授权策略详解
本文節選自 ServiceMesher 社區出品的開源電子書《Istio Handbook——Istio 服務網格進階實踐》,閱讀地址:https://www.servicemesher.com/istio-handbook/
授權功能是 Istio 中安全體系的一個重要組成部分,它用來實現訪問控制的功能,即判斷一個請求是否允許通過,這個請求可以是從外部進入 Istio 內部的請求,也可以是在 Istio 內部從服務 A 到服務 B 的請求。可以把授權功能近似地認為是一種四層到七層的“防火墻”,它會像傳統防火墻一樣,對數據流進行分析和匹配,然后執行相應的動作。
本節所有概念和操作都基于 Istio 1.6 版本。
授權功能是通過授權策略 (AuthorizationPolicy) 來進行配置和使用的,下面是一個完整的授權策略示例:
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-policynamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- from:- source:principals: ["cluster.local/ns/default/sa/sleep"]to:- operation:methods: ["GET"]paths: ["/info*"]when:- key: request.auth.claims[iss]values: ["https://foo.com"]這個授權策略的含義是:篩選出?foo?這個 namespace 中含有?app: httpbin?label 的 pod,對發送到這些 pod 的請求進行匹配,如果匹配成功,則放行當前請求,匹配規則如下:發起請求的 pod 的 Service Account 需要是?cluster.local/ns/default/sa/sleep?,請求使用 HTTP 協議,請求的具體方法類型是?GET?,請求的 URL 為?/info*?,并且請求中需要包含由?https://foo.com?簽發的有效的 JWT Token。
從這個例子中可以看出一個授權策略主要包含以下幾個部分:
?name:授權策略的名稱,僅用于標識授權策略本身,不會影響規則的匹配和執行。?namespace:當前授權策略對象所在的 namespace ,可以使用這個字段配置不同作用范圍的授權策略,詳見授權策略的作用范圍[1]。?selector:使用 label 來選擇當前授權策略作用于哪些 pod 上。注意,這里設置的是服務端的 pod ,因為最終這些規則會轉換成 Envoy 規則由服務端的 Envoy Proxy 來具體執行。例如有 client 和 server 兩個 service ,它們的 pod 對應的 label 分別為?app: client?和?app: server?,為了針對從 client 到 server 的請求進行配置授權策略,這里的 selector 應該設置為?app: server。?action:可以為?ALLOW(默認值)或者?DENY。?rules:匹配規則,如果匹配成功,就會執行對應的 action ,詳見授權策略的規則詳解[2]。
授權策略的作用范圍
授權策略可以按照作用域的大小分成三個不同的類型:全局策略、某個 namespace 內的局部策略和具有明確 match label 的授權策略。下面分別進行說明。
全局策略
授權策略位于 istio 的 root namespace 中(例如?istio-system),且匹配所有的 pod。這種規則會作用于整個集群中的所有 pod。
下面的例子中有3個全局策略,第一個是全局?ALLOW?,第二個和第三個是全局?DENY?,后面這兩個作用類似,但又有重要的區別,詳見授權策略的匹配算法。
kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: global-allownamespace: istio-system spec:action: ALLOWrules:- {} EOFkubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: global-denynamespace: istio-system spec:action: DENYrules:- {} EOFkubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: global-denynamespace: istio-system spec:{} EOF某個 namespace 內的局部策略
授權策略位于除了 root namespace 之外的任何一個 namespace 中,且匹配所有的 pod ,這種情況下,這個策略會作用于當前 namespace 中的所有 pod。
下面的例子中是3個 namespace 級別的策略,第一個是?ALLOW?,第二個和第三個是?DENY?,像全局策略一樣,后面這兩個作用類似,但又有重要的區別,詳見授權策略的匹配算法。
kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: foo-namespace-allownamespace: foo spec:action: ALLOWrules:- {} EOFkubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: foo-namespace-denynamespace: foo spec:action: DENYrules:- {} EOFkubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: foo-namespace-denynamespace: foo spec:{} EOF具有明確 match label 的授權策略
這種授權策略僅作用于當前 namespace 下使用?selector?字段匹配到的 pod。
kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-allownamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- {} EOF授權策略的匹配算法
針對某一個請求,會按照一定的匹配算法來執行相應的授權策略:
1.如果有任何一條?DENY?授權策略匹配當前請求,則拒絕當前請求。2.針對當前 pod,如果沒有任何?ALLOW?授權策略,則放行當前請求。3.如果有任何一條?ALLOW?授權策略匹配當前請求,則放行當前請求。4.拒絕當前請求。
也就意味著,如果同時有?ALLOW?和?DENY?策略作用于同一個 pod 上,則?DENY?策略會優先執行,其它的?ALLOW?規則就會被忽略。
注意這個順序非常重要,有時又會比較隱晦,因此在配置比較復雜策略的時候需要多加小心。
在上文授權策略的作用范圍中提到授權策略在配置時,有一些細節上的差異,現結合授權策略的匹配算法進行一些分析。
spec:{}這是一個?DENY?策略,作用于全局策略或者 namespace 級別(取決于策略所在 namespace 是否為 root namespace)。但是它并沒有對當前請求進行匹配,也就意味著按照授權策略的匹配算法在匹配的時候并不會優先匹配到這條規則,因此可以將其作為一個“后備”策略,即全局或者 namespace 級別的一個默認策略。
spec:action: DENYrules:- {}這條規則會真正地匹配當前的請求,又由于它是?DENY?規則,按照授權策略的匹配算法,它會首先得到執行,也就意味著如果配置了一條這種全局或者 namespace 級別的規則,那么所有的其它?ALLOW?規則都不會得到執行。因此這條規則在實際中并沒有什么價值。
spec:action: ALLOWrules:- {}這條規則和上一條規則類似,但是它是?ALLOW?規則,因此按照授權策略的匹配算法,它的優先級會低一些,因此也可以像第一條規則一樣作為一個全局或者 namespace 級別的默認策略。
授權策略的規則詳解
授權策略中最重要的是其中的?rule?字段,它指定了如何針對當前的請求進行匹配。如果一個授權策略中指定了多條?rule?規則,則它們之間是或的關系,即只要其中任意一條規則匹配成功,那么整個授權策略匹配成功,就會執行相應的 action ,下面是概述中提到的一個授權策略的例子:
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-policynamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- from:- source:principals: ["cluster.local/ns/default/sa/sleep"]to:- operation:methods: ["GET"]paths: ["/info*"]when:- key: request.auth.claims[iss]values: ["https://foo.com"]這里的?rules?是一個?rule?的列表。每一條?rule?規則包括三部分:from 、 to 和 when 。類似于防火墻規則,from 和 to 匹配當前請求從哪里來、到哪里去,when 會增加一些額外的檢測,當這些條件都滿足時,就會認為當前規則匹配成功。如果其中某一部分未進行配置,則認為其可以匹配成功。
在?rule?中進行配置時,所有的字符串類型都支持類似于通配符的匹配模式,例如?abc*?匹配 "abc" 和 "abcd" 等,*xyz?匹配 "xyz" 和 "axyz" 等,單獨的?*?匹配非空的字符串。
下面針對具體的字段詳細進行說明。
?from。針對請求的發送方進行匹配,主要包括 principals 、 requestPrincipals 、 namespaces 和 ipBlocks 四個部分。?principals。匹配發送方的身份,在 Kubernetes 中可以認為是 pod 的 Service Account。使用這個字段時,首先需要開啟 mTLS 功能,關于這部分內容可參見對等認證。例如,當前請求是從 default namespace 中的 pod 中發出,且 pod 使用的 Service Account 名為?sleep,針對這個請求進行匹配,可將 principals 配置為[cluster.local/ns/default/sa/sleep]。?requestPrincipals。匹配請求中的 JWT Token 的?<issuer>/<subject>?字段組合。?namespaces。匹配發送方 pod 所在的 namespace。?ipBlocks。匹配請求的源 IP 地址段。?to。針對請求的接收方進行匹配。除了請求接收方,還會對請求本身進行匹配。包括以下字段:?hosts。目的 host。?ports。目的 port。?methods。是指當前請求執行的 HTTP Method。針對 gRPC 服務,這個字段需要設置為?POST。注意這個字段必須在 HTTP 協議時才進行匹配,如果請求不是 HTTP 協議,則認為匹配失敗。?paths。當前請求執行的 HTTP URL Path。針對 gRPC 服務,需要配置為?/package.service/method?格式。?when。這是一個 key/value 格式的 list 。這個字段會針對請求進行一些額外的檢測,當這些檢測全部匹配時才會認證當前規則匹配成功。例如?key: request.headers[User-Agent]?可以匹配 HTTP Header 中的?User-Agent?字段。所有可配置項可參見 Istio 官網上的?Authorization Policy Conditions[3]?說明。
針對以上字段,還有對應的反向匹配操作,即“取反”匹配,包括 notPrincipals、notNamespaces 等。例如?notNamespaces: ["bar"]?表示當發送請求的 pod 不位于 "bar" 這個 namespace 中的時候匹配成功。
另外,在?rule?中會有非常多針對 JWT Token 進行匹配的字段,關于這部分可以查看 JWT 授權里的詳細分析。
下面針對上文列出來的授權策略給出一些實際的例子,一方面可以在實際環境中是如何使用這些策略的,另一方面也可以驗證前文所述的各種匹配字段、授權策略的匹配算法和授權策略的作用域。
操作示例
創建應用
首先,創建客戶端和服務器端的 service 和對應的 pod,使用的例子位于 istio 源代碼的?samples[4]?目錄中:
$ 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確認 pod 已正常運行:
$ kubectl get pod -n foo --show-labels NAME READY STATUS RESTARTS AGE LABELS httpbin-5d5df46d48-jndgh 2/2 Running 0 57s app=httpbin,istio.io/rev=,pod-template-hash=5d5df46d48,security.istio.io/tlsMode=isti o,version=v1 sleep-545684d78b-29x74 2/2 Running 0 56s app=sleep,istio.io/rev=,pod-template-hash=545684d78b,security.istio.io/tlsMode=istio $確認可以正常訪問,且啟用了 mTLS 功能,關于 mTLS 功能的開啟和驗證請參見對等認證:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 200 $ $ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl http://httpbin.foo:8000/headers -s | grep X-Forwarded-Client-Cert"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=e0f2132eb6ae920cec4b2ea16b9baa33ca388b719a2648636f7a75542852ff0e;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"全局策略測試
接下來創建一個全局默認的拒絕策略:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: global-denynamespace: istio-system spec:{} EOF這時再進行驗證連通性:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 403服務拒絕,表明全局拒絕策略生效。
接下來創建一個 httpbin pod 的?ALLOW?策略:
kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-allow-policynamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- to:- operation:methods: ["GET"] EOF按照授權策略的匹配算法,應該可以匹配到第3條規則,因此會執行?ALLOW?動作,運行下面的命令進行驗證:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 200測試 Rule 中的字段
下面我們以 Service Account 為例來進行說明。首先來檢查 sleep pod 所使用的的 Service Account:
$ kubectl get pod -l app=sleep -n foo -o jsonpath={.items...serviceAccountName} sleep根據 namespace 和 Service Account 構造出 principals 字段,更新授權策略:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-allow-policynamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- from:- source:principals: ["cluster.local/ns/foo/sa/sleep"]to:- operation:methods: ["GET"] EOF進行驗證:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 200訪問仍然是放行狀態,說明剛才的授權策略是生效的。
將授權策略中的 Service Account 改為一個其它值:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-allow-policynamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- from:- source:principals: ["cluster.local/ns/foo/sa/other-sa"]to:- operation:methods: ["GET"] EOF $ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 403訪問失敗,因為授權策略中配置 Service Account 字段與實際的 Service Account 不匹配。
同樣地,可以配置 From、To 和 When 中的其它字段進行測試。
授權策略的匹配算法測試
首先,刪除之前創建的名為 httpbin-allow-policy 的授權策略,目前系統中僅存在一個全局的默認?DENY?策略。
kubectl delete authorizationpolicies httpbin-allow-policy -n foo接下來創建一個匹配 "GET" 方法的?ALLOW?策略,名為 httpbin-allow-get:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-allow-getnamespace: foo spec:selector:matchLabels:app: httpbinaction: ALLOWrules:- to:- operation:methods: ["GET"] EOF這時使用 "GET /ip" 請求進行測試,由于可以和 httpbin-allow-get 策略匹配,因此按照授權策略的匹配算法,可以匹配到第3條規則,因此可以正常訪問:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 200使用 "POST /ip" 請求進行測試,與 httpbin-allow-get 策略不能匹配,因此會執默認的全局?DENY?策略:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl -X POST "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 403再創建一個 "/ip" 的?DENY?策略,名為 httpbin-deny-ip-url:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:name: httpbin-deny-ip-urlnamespace: foo spec:selector:matchLabels:app: httpbinaction: DENYrules:- to:- operation:paths: ["/ip"] EOF這時使用 "GET /ip" 請求進行測試:
$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "sleep.foo to httpbin.foo: %{http_code}\n" sleep.foo to httpbin.foo: 403可以看出執行失敗。失敗的原因是 "GET /ip" 請求與我們剛才創建的 httpbin-allow-get 和 httpbin-deny-ip-url 兩個授權策略都會匹配,但是授權策略的匹配算法執行到第1條規則時,會發現匹配 httpbin-deny-ip-url 授權策略,然后就會直接拒絕當前的請求。另一條授權策略 httpbin-allow-get 便無法得到執行。
清理
執行以下操作來清理我們創建過的各種資源:
$ kubectl delete authorizationpolicies httpbin-deny-ip-url -n foo $ kubectl delete authorizationpolicies httpbin-allow-get -n foo $ kubectl delete authorizationpolicies httpbin-allow-policy -n foo $ kubectl delete authorizationpolicies global-deny -n istio-system $ kubectl delete -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo $ kubectl delete -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo $ kubectl delete ns foo總結
本節我們詳細分析了授權策略的概念和用法,也可以看出利用這些規則可以組合出非常復雜的場景,因此在使用復雜的授權策略時需要非常小心。另外,在授權策略中會涉及到很多針對請求中的 JWT Token 進行匹配的規則,相關的概念和用法會在下一節進行詳細闡述。
參考
?Authorization Policy[5]?Authorization Policy Conditions[6]?Authorization[7]
引用鏈接
[1]?授權策略的作用范圍:?#授權策略的作用范圍
[2]?授權策略的規則詳解:?#授權策略的規則詳解
[3]?Authorization Policy Conditions:?https://istio.io/latest/docs/reference/config/security/conditions/
[4]?samples:?https://github.com/istio/istio/tree/master/samples
[5]?Authorization Policy:?https://istio.io/latest/docs/reference/config/security/authorization-policy/
[6]?Authorization Policy Conditions:?https://istio.io/latest/docs/reference/config/security/conditions/
[7]?Authorization:?https://istio.io/latest/docs/tasks/security/authorization/
活動預告
Service Mesh Webinar #2
7 月 22 日(周三)晚 8 點,B 站直播《基于 MOSN 和 Istio Service Mesh 的服務治理實踐》,直播間 https://live.bilibili.com/21954520,點擊閱讀原文查看詳細信息。
Go 夜讀
Go 夜讀第 97 期我們可以從 MOSN 和相關的項目中學習到什么
點擊?閱讀原文?查看更多
總結
以上是生活随笔為你收集整理的Istio 中的授权策略详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这么多Apache顶级项目,SkyWal
- 下一篇: 如何利用Gitlab-CI持续部署到远程