一文了解Istio外部授權

語言: CN / TW / HK

初識Istio Authorization

我們都知道認證(Authentication、Authn)與授權(Authorization、Authz)一同構建了起網路應用安全的基本屏障。通常,授權對認證有一定的依賴。比如我們熟悉的OAuth或者基於JWT Token的授權。

Istio授權充分利用了Envoy的授權外掛,基本覆蓋業界主流的訪問控制策略。Istio Authz功能提供了服務網格、名稱空間以及工作負載範圍的訪問控制,多種粒度的訪問控制具有以下好處:

1. 工作負載到工作負載以及終端使用者到工作負載授權

2. 簡單的API: Istio利用宣告式的AuthorizationPolicy,對使用者來說簡單易用。

3. 靈活的語法: 使用者可以基於Istio的訪問屬性(身份,命名控制等)自定義控制策略(ALLOW, DENY,CUSTOM)

4. 高效能: Istio本地授權在envoy程序內部執行

5. 靈活擴充套件性: 外部授權支援更加複雜的訪問控制,外部授權支援HTTP、gRPC協議,後面重點講解。

Istio Authorization架構及原理

授權架構

授權策略強制對伺服器端 Envoy 代理中Inboud流量進行訪問控制。每個 Envoy 代理執行一個授權引擎,在執行時授權請求。當請求到達代理時,授權引擎根據當前授權策略評估請求上下文,並返回授權結果,允許或拒絕。使用者可以通過AuthorizationPolicy指定訪問控制策略。

本地授權基於Envoy RBAC(Role Based Access Control)過濾器實現。RBAC Filter可以用來對識別到的下游客戶(Principals)進行授權。這對於顯式管理應用程式的呼叫者並保護應用程式很有用。RBAC本地授權支援使用連線屬性(IP,埠,SSL subject)以及HTTP頭配置ALLOW或DENY列表。

External Authorization

External Authorization

Envoy (v1.7.0+) 支援外部授權過濾器,它呼叫授權服務來檢查傳入請求是否被授權。External Authz可以將授權決策委託給外部服務,並將請求上下文傳遞給授權服務。請求上下文包含諸如網路請求的來源、網路請求的目的地、網路請求(例如http請求)等資訊。外部服務可以使用所有這些資訊來對 Envoy 收到的傳入請求的命運做出明智的決定。

Envoy外部授權過濾器有兩種:一種是 TCP的網路過濾器 ,另一種是 HTTP過濾器 。理論上可以配置任意的外部過濾器,當請求被TCP的外部授權拒絕時,網路連線自動斷開,當網路請求被HTTP外部授權過濾器拒絕時,則會返回403 HTTP響應碼。

目前有一些成熟的開源外部授權伺服器實現,比如Open Policy Agent, oauth2-proxy。當然也可以實現自己的授權伺服器,參考Istio例子: https://github.com/istio/istio/blob/master/samples/extauthz/src/main.go

外部授權介面支援gRPC和HTTP兩種協議。Envoy通過CheckRequest定義了傳遞給授權服務的請求上下文屬性,供其進行決策。請求上下文主要分為:

●  Source : 源地址,身份principal(Istio從x509證書SAN中提取)

●   Destination: 目的地址,身份principal(Istio從x509證書SAN中提取)

●   Request: 網路請求,HTTP頭,方法,請求引數,協議等資訊

授權API External AuthorizationPolicy

AuthorizationPolicy與Istio其他的API一樣也是通過CRD定義的,宣告式的API。其優點是簡單易用。首先簡單瞭解一下AuthorizationPolicy:

type AuthorizationPolicy struct {   

// Optional. The selector decides where to apply the authorization policy. The selector will match with workloads   

// in the same namespace as the authorization policy. If the authorization policy is in the root namespace, the selector  

// will additionally match with workloads in all namespaces.   

//   

// If not set, the selector will match all workloads.   

Selector * v1beta1 . WorkloadSelector

// Optional. A list of rules to match the request. A match occurs when at least one rule matches the request.  

//   

// If not set, the match will never occur. This is equivalent to setting a default of deny for the target workloads if   

// the action is ALLOW.   

Rules []*Rule   

// Optional. The action to take if the request is matched with the rules. Default is ALLOW if not specified.   

Action  AuthorizationPolicy_Action    // Types that are valid to be assigned to ActionDetail:  

// *AuthorizationPolicy_Provider   

ActionDetail           isAuthorization Policy_ActionDetail

Selector: 決定此授權策略應用到哪個工作負載。

授權策略語義分析

Rules: 一組匹配策略,當任意策略匹配上的時候,執行下面的Action。Rule支援三種匹配,

1. From ,即匹配請求源

2. To ,匹配請求的操作,比如訪問路徑,方法

3. When ,指定執行這條規則的條件,例如當request.auth.claims[iss]

Action: 決策結果:ALLOW、DENY、CUSTOM,CUSTOM則是指定外部授權方式,同時必須指定下面的ActionDetail

ActionDetail: 授權服務provider  

apiVersion: security.istio.io/v1beta1

kind: AuthorizationPolicy

metadata:

name: httpbin

namespace: foo

spec:

selector:

matchLabels:

app: httpbin

version: v1

action: ALLOW

rules:

- from:

- source:

principals: ["cluster.local/ns/default/sa/sleep"]

- source:

namespaces: ["default"]

to:

- operation:

methods: ["GET"]

when:

- key: request.auth.claims[iss]

values: ["https://accounts.google.com"]

上面的例子表示Istio使用本地授權,當網路請求使用JWT認證,簽發者是google時,允許服務賬戶為“ cluster.local/ns/default/sa/sleep ”,來自default名稱空間的服務,訪問httbin-v1服務的GET介面。否則,決絕訪問httpbin-v1。

Istio允許使用者為同一個服務指定多個授權策略,為了避免不同的AuthorizationPolicy相互衝突,Istio定義了一定的優先順序策略,如下所示:

1. 首先執行CUSTOM的外部授權 ,如果外部授權結果為ALLOW,則繼續執行本地授權當結果為DENY時,則直接返回DENY決策,退出授權鏈。

2. 執行DENY的本地授權規則 ,當請求匹配DENY條件時,返回DENY決策,退出授權鏈。否則繼續執行ALLOW的本地授權規則

3. 執行ALLOW的本地授權策略 ,當請求匹配ALLOW Rule時,返回ALLOW,否則不能匹配ALLOW Rule,直接返回DENY。

授權策略優先順序

參考連結:

https://www.openpolicyagent.org/docs/latest/envoy-introduction/ 

Open Policy Agent介紹

OPA是為雲原生環境設計的策略引擎,目前已經是CNCF的畢業專案。OPA的願景打造雲原生乃至整個業界策略控制的事實標準。

“停止為您使用的每種產品和服務使用不同的策略語言、策略模型和策略 API。將 OPA 用於雲原生堆疊的統一工具集和策略框架。

無論是針對一項服務還是針對您的所有服務,都可以使用 OPA 將策略與服務程式碼分離,這樣您就可以在不犧牲可用性或效能的情況下發布、分析和審查策略(安全和合規團隊喜歡的策略)。”

OPA是一種宣告式的策略引擎,使用rego表示策略。OPA-Envoy Plugin擴充套件了OPA,實現了Envoy的外部授權gRPC服務。因此Istio外部授權可以直接使用OPA-Envoy外掛。

Istio與OPA整合

將OPA-Envoy以Sidecar的形式部署在應用旁是一種更為推薦的方式,這樣遠端呼叫的時延最小。然而這也不是必須的,OPA也可以中心式部署。

Istio外部授權-整合OPA

OPA官網https://www.openpolicyagent.org/docs/latest/envoy-tutorial-istio/提供瞭如何利用Istio Envoyfilter配置由OPA-Envoy執行授權訪問控制。由於OPA-Envoy以邊車的形式部署,所以沒有使用AuthorizationPolicy,而是直接通過EnvoyFilter,控制外部授權服務的地址。

apiVersion: networking.istio.io/v1alpha3

kind: EnvoyFilter

metadata:

name: ext-authz

namespace: istio-system

spec:

configPatches:

- applyTo: HTTP_FILTER

match:

context: SIDECAR_INBOUND

listener:

filterChain:

filter:

name: "envoy.filters.network.http_connection_manager"

subFilter:

name: "envoy.filters.http.router"

patch:

operation: INSERT_BEFORE

value:

name: envoy.ext_authz

typed_config:

'@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz

transport_api_version: V3

status_on_error:

code: ServiceUnavailable

with_request_body:

max_request_bytes: 8192

allow_partial_message: true

grpc_service:

# NOTE(tsandall): when this was tested with the envoy_grpc client the gRPC

# server was receiving check requests over HTTP 1.1. The gRPC server in

# OPA-Istio would immediately close the connection and log that a bogus

# preamble was sent by the client (it expected HTTP 2). Switching to the

# google_grpc client resolved this issue.

google_grpc:

target_uri: 127.0.0.1:9191

stat_prefix: "ext_authz"

上述Envoyfilter樣例向Envoy中插入一個HTTP外部授權過濾器,控制所有的請求都向127.0.0.1:9191這個服務發起授權請求。實際上這正是OPA-Envoy邊車監聽的地址。

OPA-Envoy的策略宣告如下:

apiVersion: v1

kind: ConfigMap

metadata:

name: opa-policy

data:

policy.rego: |

package istio.authz

import input.attributes.request.http as http_request

import input.parsed_path

default allow = false

allow {

parsed_path[0] == "health"

http_request.method == "GET"

}

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 = {

"alice": ["guest"],

"bob": ["admin"]

}

role_perms = {

"guest": [

{"method": "GET",  "path": "/productpage"},

],

"admin": [

{"method": "GET",  "path": "/productpage"},

{"method": "GET",  "path": "/api/v1/products"},

],

}

●  使用者alice被授予guest角色,具有發起GET請求/productpage的許可權。

● 使用者b ob被授予admin角色,具有發起GET請求/productpage和/api/v1/products的許可權。

在典型的部署中,策略要麼內建到 OPA 容器映像中,要麼通過 Bundle API 動態獲取。在這裡是通過Configmap掛載到容器中。

參考連結

1. https://istio.io/latest/docs/tasks/security/authorization/authz-custom/ 

2. https://istio.io/latest/docs/concepts/security/#authoriz

ation 

3.https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html

4. https://www.openpolicyagent.org/docs/latest/envoy-tutorial-istio/