Service MeshASM整合了開放策略代理(OPA)外掛程式,通過OPA定義存取控制策略,可以使您的應用實現細粒度的存取控制。ASM支援在控制平面定義OPA策略,然後推送到資料平面叢集中。本文介紹如何在ASM定義OPA策略實現訪問應用時的許可權細粒度控制,包括對請求URL、要求標頭資訊中的Token等訪問限制。
前提條件
已建立ASM執行個體,且ASM執行個體為1.9.7及以上版本。具體操作,請參見建立ASM執行個體。
已添加叢集到ASM執行個體。具體操作,請參見添加叢集到ASM執行個體。
已為default命名空間注入Sidecar。具體操作,請參見啟用自動注入。
背景資訊
作為由CNCF託管的一個孵化專案,開放策略代理(OPA)是一個策略引擎,可用於為您的應用程式實現細粒度的存取控制。OPA作為通用策略引擎,可以與微服務一起部署為獨立服務。為了保護應用程式,必須先授權對微服務的每個請求,然後才能對其進行處理。為了檢查授權,微服務對OPA進行API調用,以確定請求是否被授權。
步驟一:啟用OPA外掛程式和注入範圍控制
登入ASM控制台,在左側導覽列,選擇 。
在網格管理頁面,單擊目標執行個體名稱,然後在左側導覽列,選擇 。
在OPA策略頁面,選中啟用開放策略代理OPA外掛程式和啟用OPA注入範圍控制,單擊開啟OPA功能,在提示對話方塊,單擊確定。
步驟二:建立OPA策略
在ASM控制平面建立ASMOPAPolicy資源,該資源會被推送到資料平面叢集,然後Pod中OPA引擎使用該資源實現細粒度存取控制。
方式一:通過ASM控制台建立OPA策略
登入ASM控制台,在左側導覽列,選擇 。
在網格管理頁面,單擊目標執行個體名稱,然後在左側導覽列,選擇 。
在OPA策略頁面,單擊建立,選擇default命名空間,設定OPA策略的名稱為bookinfo-opa,單擊新增匹配標籤,設定名稱為version,值為v1,將以下Rego規則複製到文字框,然後單擊建立。
方式二:使用kubectl命令列建立OPA策略
使用以下內容,建立opa.yaml。
說明OPA定義一個Pod的OPA策略時,只允許有一條
default allow
欄位。如果多個OPA策略生效於一個Pod,且每個策略中都定義了default allow
欄位,則多條default allow
欄位會導致動態更新失敗。設定OPA策略時,建議通過標籤控制策略生效的範圍,不恰當的Rego規則會導致無法訪問服務。
OPA執行引擎和業務容器在一個Pod中,需要佔用15081和9191連接埠。
OPA Policy中已經預設設定
default allow = false
,不可以再重複設定default allow
,否則會導致衝突。
欄位
說明
spec
使用Rego文法表述的具體規則。關於Rego文法的更多資訊,請參見Rego。
workloadSelector
設定該OPA策略在此命名空間生效範圍,如果不設定,預設在全部Pod中生效,設定後只生效於符合標籤條件的Pod。
user_roles
為使用者授予角色許可權。本例設定guest1擁有guest角色許可權,admin1擁有admin角色許可權。
role_perms
設定角色擁有的許可權。本文設定guest角色可以通過/productpage訪問應用,admin角色可以通過/productpage和/api/v1/products訪問應用。
執行以下命令,建立OPA策略。
關於如何通過kubectl串連ASM執行個體,請參見通過控制面kubectl訪問Istio資源。
kubectl apply -f opa.yaml
步驟三:注入OPA代理
部署樣本應用Bookinfo到ASM執行個體,確認每個Pod都注入了OPA代理。
部署樣本應用Bookinfo到ASM執行個體。具體操作,請參見在ASM執行個體關聯的叢集中部署應用。
建立入口網關、網關規則和虛擬服務。具體操作,請參見使用Istio資源實現版本流量路由。
檢查每個應用是否都注入OPA代理。
登入Container Service管理主控台,在左側導覽列選擇叢集。
在叢集列表頁面,單擊目的地組群名稱,然後在左側導覽列,選擇
。在容器組頁面,從命名空間下拉式清單中選擇default,單擊目標應用程式容器組的名稱。
在容器頁簽下可以看到容器被注入了Sidecar代理(istio-proxy)和OPA代理(opa-istio)。依次檢查每個應用的容器,確保每個容器都被注入了Sidecar代理和OPA代理。
步驟四:驗證使用OPA定義存取控制策略是否成功
執行以下命令,使用帶有/productpage的URL訪問應用。
curl -X GET http://<入口網關的IP地址>/productpage --user guest1:password -I
預期輸出:
HTTP/1.1 200 OK
執行以下命令,使用帶有
/api/v1/products
的URL訪問應用。curl -X GET http://<入口網關的IP地址>/api/v1/products --user guest1:password -I
預期輸出:
HTTP/1.1 403 Forbidden
可以看到guest1被授予guest角色,並且可以使用帶有
/productpage
的URL訪問應用,但不能使用帶有/api/v1/products
的URL訪問應用。執行以下命令,使用帶有
/productpage
的URL訪問應用。curl -X GET http://{{入口網關的IP地址}}/productpage --user admin1:password -I
預期輸出:
HTTP/1.1 200 OK
執行以下命令,使用帶有
/api/v1/products
的URL訪問應用。curl -X GET http://<入口網關服務的IP地址>/api/v1/products --user admin1:password -I
預期輸出:
HTTP/1.1 200 OK
可以看到admin1被授予admin角色,並且可以使用帶有
/productpage
和/api/v1/products
的URL訪問應用。以上結果表明使用OPA定義存取控制策略成功。
步驟五:動態更新OPA策略
執行以下命令,修改OPA策略。
kubectl edit asmopapolicy bookinfo-opa -n default
在返回結果中編輯OPA策略,使guest1同時用於guest和admin許可權。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: bookinfo-opa
namespace: default
spec:
policy: |
package istio.authz
import input.attributes.request.http as http_request
allow {
roles_for_user[r]
required_roles[r]
}
roles_for_user[r] {
r := user_roles[user_name][_]
}
required_roles[r] {
perm := role_perms[r][_]
perm.method = http_request.method
perm.path = http_request.path
}
user_name = parsed {
[_, encoded] := split(http_request.headers.authorization, " ")
[parsed, _] := split(base64url.decode(encoded), ":")
}
user_roles = {
"guest1": ["guest", "admin"],
"admin1": ["admin"]
}
role_perms = {
"guest": [
{"method": "GET", "path": "/productpage"},
],
"admin": [
{"method": "GET", "path": "/productpage"},
{"method": "GET", "path": "/api/v1/products"},
],
}
user_roles:為使用者授予角色許可權。本例設定guest1同時擁有guest和admin角色許可權,admin1擁有admin角色許可權。
role_perms:設定角色擁有的許可權。本例設定guest角色可以通過/productpage訪問應用,admin角色可以通過/productpage和/api/v1/products訪問應用。
步驟六:驗證動態更新OPA策略是否成功
執行以下命令,使用帶有/productpage的URL訪問應用。
curl -X GET http://<入口網關服務的IP地址>/productpage --user guest1:password -I
預期輸出:
HTTP/1.1 200 OK
執行以下命令,使用帶有/api/v1/products的URL訪問應用。
curl -X GET http://<入口網關服務的IP地址>/api/v1/products --user guest1:password -I
預期輸出:
HTTP/1.1 200 OK
在沒有更新OPA策略之前,guest1隻能使用帶有/productpage的URL訪問應用,但不能使用帶有/api/v1/products的URL訪問應用。更新OPA策略之後,guest1可以使用帶有/productpage和/api/v1/products的URL訪問應用。說明動態更新OPA策略成功。
情境樣本
情境一:JWT請求授權
在接收使用者請求時,認證要求標頭資訊中的JWT Token是否可信,只有符合要求的請求才能訪問到應用。
以下ASMOPAPolicy定義了只有通過get請求的方式,且JWT Token的Role
為guest
,userGroup
是visitor
的請求才能訪問Productpage應用。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-jwt
namespace: default
spec:
policy: |
package istio.authz
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
# set certificate 'B41BD5F462719C6D6118E673A2389'
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == "guest"
claims.userGroup == "visitor"
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}
input.attributes.request.http.method:要求方法,本文設定為
GET
。input.parsed_path[0]:訪問的應用。
claims.Role:對JWT Token進行限制,本文設定
Role
為guest
。claims.userGroup:對JWT Token進行限制,本文設定
userGroup
為visitor
。
使用JWT工具將Role
、userGroup
等請求資訊編碼成JWT字串。
執行以下命令,訪問Productpage應用。
curl --location --request GET 'http://{入口網關服務的IP地址服務IP}/productpage' \
--header 'Authorization: Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3Vlc3QxIiwiUm9sZSI6Imd1ZXN0IiwidXNlckdyb3VwIjoidmlzaXRvciJ9.44OnUFZwOzSWzC7hyVfcle-uYk8byv7q_BBxS10AEWc'
預期輸出:
200
返回200,說明通過get請求的方式,且JWT Token的Role
為guest
,userGroup
是visitor
的請求訪問Productpage應用成功。如果您使用錯誤的JWT Token,或者請求中不包含JWT Token,將會返回403,說明訪問Productpage應用失敗。
情境二:對HTTP請求的請求體body進行限制
對HTTP請求的請求體body進行限制,只有請求體body與JWT中的Role相同的請求才能訪問應用。
以下ASMOPAPolicy定義了只有通過get請求的方式,且請求體body的username
與JWT中的Role
相同,並且userGroup
為manager
的請求才能訪問Productpage應用。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-body
namespace: default
spec:
policy: |
package istio.authz
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == input.parsed_body.username
claims.userGroup == "manager"
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}
input.attributes.request.http.method:要求方法,本文設定為
GET
。input.parsed_path[0]:訪問的應用。
claims.Role:對JWT Token進行限制,本文設定
input.parsed_body.username
,限制請求體body的username
與JWT中的Role必須相同。claims.userGroup:對JWT Token進行限制,本文設定
userGroup
為manager
。
使用JWT工具將Role
、userGroup
等請求資訊編碼成JWT字串。
執行以下命令,訪問Productpage應用。
curl --location --request GET 'http://{入口網關服務的IP地址}/productpage' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3Vlc3QxIiwiUm9sZSI6ImFkbWluIiwidXNlckdyb3VwIjoibWFuYWdlciJ9.pAUvTeONHF-i5Ps-EUYYXk-hnaz-j-ZgP_wXJZMBiR0' \
--header 'Content-Type: application/json' \
--header 'Cookie: session=eyJ1c2VyIjoiYWRtaW4ifQ.YRz90g.GT34_5BqlFTwGqabZk_qGZzxYQ0' \
--data-raw '{
"username":"admin",
"password":"12****"
預期輸出:
200
返回200,說明通過get請求的方式,請求體body的username
與JWT中的Role
相同,並且JWT Token中的userGroup
為manager
的請求訪問Productpage應用成功。如果您使用錯誤的JWT Token,或者請求中不包含JWT Token,將會返回403,說明訪問Productpage應用失敗。
情境三:限制更多上下文資訊
在情境二的基礎上限制更多上下文資訊,限制JWT中的username
資訊必須包含在bookinfo_managers
範圍內。
以下ASMOPAPolicy定義了只有通過get請求的方式,且請求體body的username
與JWT中的Role
相同,JWT中的username
資訊必須包含在bookinfo_managers
範圍內,userGroup
為manager
的請求才能訪問Productpage。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-range
namespace: default
spec:
policy: |
package istio.authz
bookinfo_managers = [{"name": "user1"}, {"name": "user2"}, {"name": "user3"}]
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == input.parsed_body.username
claims.userGroup == "manager"
claims.username == bookinfo_managers[_].name
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}
input.attributes.request.http.method:要求方法,本文設定為
GET
。input.parsed_path[0]:訪問的應用。
claims.Role:對JWT Token進行限制,本文設定
input.parsed_body.username
,限制請求體body的username與JWT中的Role必須相同。claims.userGroup:對JWT Token進行限制,本文設定
userGroup
為manager
。claims.username:對JWT Token進行限制,本文設定
bookinfo_managers[_].name
,限制JWT中的username
資訊必須包含在bookinfo_managers
範圍內。
使用JWT工具將請求資訊編碼成JWT字串。
執行以下命令,訪問productpage應用。
curl --location --request GET 'http://{入口網關服務的IP地址}/productpage' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiUm9sZSI6ImFkbWluIiwidXNlckdyb3VwIjoibWFuYWdlciJ9.2X0Fmb96jBexLcVm_55t8ZY6XveSxUAsQ1j3ar5dI_g' \
--header 'Content-Type: application/json' \
--header 'Cookie: session=eyJ1c2VyIjoiYWRtaW4ifQ.YRz90g.GT34_5BqlFTwGqabZk_qGZzxYQ0' \
--data-raw '{
"username":"admin",
"password":"12****"
}'
預期輸出:
200
返回200,說明通過get請求的方式,請求體body的username
與JWT中的Role
相同,JWT中的username
資訊必須包含在bookinfo_managers
範圍內。並且JWT Token中的userGroup
為manager
的請求訪問Productpage應用成功。如果您使用錯誤的JWT Token,或者請求中不包含JWT Token,將會返回403,說明訪問Productpage應用失敗。
FAQ
如何查看哪些Pod使用OPA策略?
OPA的執行引擎以Sidecar的方式和業務容器部署在同一個Pod中。您需要進入到Pod中,執行以下命令,可以看到應用在Pod上的全部OPA策略。
curl 127.0.0.1:15081/v1/policies
如何對Rego文法進行測試?
OPA Policy Agent官方提供了線上測試載入器,您可以使用該工具對Rego編寫的策略進行測試。
相關文檔
如果您之前使用的是Configmap配置OPA策略,您可以參考以下文檔: