iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
DevOps

k8s新手船長船難記系列 第 26

[DAY26]Istio延伸功能-External Authorization

External Authorization這功能主要拿來做request的驗證,可以在request裡面加了一個header進行判斷,
如果有符合的header與value時才會讓request導流到後端服務。

有些行為不能直接從request ban掉時,可以透過External Authorization進行加工處理,做到ban ban的動作,這些要怎麼操作就看人啦~~


圖片來源

修改istio configMap

kubectl edit configmap istio -n istio-system
data:
  mesh: |-
    # Add the following content to define the external authorizers.
    extensionProviders: #可以分成http/gRPC二種,這邊的還是以gRPC為主
    - name: "sample-ext-authz-grpc" #名稱後續會用到,要設定一致
      envoyExtAuthzGrpc:
        service: "ext-authz.foo.svc.cluster.local" #external service路徑
        port: "9000"
    - name: "sample-ext-authz-http"
      envoyExtAuthzHttp:
        service: "ext-authz.foo.svc.cluster.local"
        port: "8000"
        includeHeadersInCheck: ["x-ext-authz"]

重啟istiod,重新載入configMap

kubectl rollout restart deployment/istiod -n istio-system

部署external service

可以參考這個作法 grpc_server

主要是透過這幾個lib處理,分成v2跟v3版本
corev2 "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
authv2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
typev2 "github.com/envoyproxy/go-control-plane/envoy/type"
typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"

啟動gRPC server時要做這件事情,不然不會做authzore

authv2.RegisterAuthorizationServer(grpc, &extAuthzServerV2{})
authv3.RegisterAuthorizationServer(grpc, &extAuthzServerV3{})

重點就是這個部份啦,這邊定義了什麼條件才能http 200,什麼條件 403,再依自己的需求進行調整

func (a *AuthorizationServer) Check(ctx context.Context, req *auth.CheckRequest) (*auth.CheckResponse, error) {
	log.Println(">>> Authorization called check()")

	authHeader, ok := req.Attributes.Request.Http.Headers["authorization"]
	if !ok {
		return returnUnAuthenticated("Unable to find Authorization Header"), nil
	}
	var splitToken []string
	log.Printf("Authorization Header %s", authHeader)

	if ok {
		splitToken = strings.Split(authHeader, "Bearer ")
	} else {
		log.Println("Unable to parse Header")
		return returnUnAuthenticated("Unable to parse Authorization Header"), nil
	}
	if len(splitToken) == 2 {
		token := splitToken[1]

		if stringInSlice(token, strings.Split(AUTHZ_ALLOWED_USERS, ",")) {

			var aud []string
			if token == "alice" {
				aud = []string{"http://svc1.default.svc.cluster.local:8080/", "http://be.default.svc.cluster.local:8080/"}
			} else if token == "bob" {
				aud = []string{"http://svc2.default.svc.cluster.local:8080/"}
			} else if token == "carol" {
				aud = []string{"http://svc1.default.svc.cluster.local:8080/"}
			} else {
				aud = []string{}
			}
			claims := MyCustomClaims{
				token,
				aud,
				jwt.StandardClaims{
					Issuer:  AUTHZ_ISSUER,
					Subject: AUTHZ_ISSUER,
					//Audience:  aud,
					IssuedAt:  time.Now().Unix(),
					ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
				},
			}

			log.Println("Using Claim %v", claims)
			token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
			token.Header["kid"] = AUTHZ_SERVER_KEY_ID
			ss, err := token.SignedString(privateKey)
			if err != nil {
				return returnUnAuthenticated("Unable to generate JWT"), nil
			}

			log.Printf("Issuing outbound Header %s", ss)

			return &auth.CheckResponse{
				Status: &rpcstatus.Status{
					Code: int32(rpc.OK),
				},
				HttpResponse: &auth.CheckResponse_OkResponse{
					OkResponse: &auth.OkHttpResponse{
						Headers: []*core.HeaderValueOption{
							{
								Header: &core.HeaderValue{
									Key:   "Authorization",
									Value: "Bearer " + ss,
								},
							},
						},
					},
				},
			}, nil
		} else {
			log.Printf("Authorization Header missing")
			return returnPermissionDenied("Permission Denied"), nil

		}

	}
	return returnUnAuthenticated("Authorization header not provided"), nil
}

部署EnvoyFilter

前面的動作都完成了,剩下最一步部署EnvoyFilter,這邊其實跟Rate Limits很雷同,一樣是定義名稱,指定grpc服務路徑,done

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: my-ext-authz
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: GATEWAY
        listener:
          filterChain:
            filter:
              name: "envoy.http_connection_manager"
              subFilter:
                name: "envoy.router"
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.ext_authz
          typed_config:
            "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz"
            failure_mode_allow: true
            grpc_service:
              envoy_grpc:
                cluster_name: ext_cluster_grpc
                timeout: 0.5s

    - applyTo: CLUSTER
      match:
        cluster:
          service: test-ext.dev-xbb.svc.cluster.local
      patch:
        operation: ADD
        value:
          name: ext_cluster_grpc
          type: STRICT_DNS
          connect_timeout: 1s
          lb_policy: ROUND_ROBIN
          http2_protocol_options: {}
          load_assignment:
            cluster_name: ext_cluster_grpc
            endpoints:
              - lb_endpoints:
                  - endpoint:
                      address:
                        socket_address:
                          address: test-ext.dev-xbb.svc.cluster.local
                          port_value: 9000

以上步驟完成之後就可以實作出External Authorization囉


上一篇
[DAY25]Istio延伸功能-Rate Limits限流
下一篇
[DAY27]GCP-Google Cloud Platform
系列文
k8s新手船長船難記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言