[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罗


<<:  NNI如何搬到云端上玩?

>>:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 20 - invoke 的用法

[今晚我想来点 Express 佐 MVC 分层架构] DAY 28 - node.js 与线程 (上)

node.js 之所以能够运行 JavaScript 程序码,是因为底层依赖 google 在 ch...

使用 Template Message 替 Line Bot 加上同意条款的功能(2)

昨天我们完成了 user sheet 的 query & upsert 功能,今天就要正式将...

Day27 跟着官方文件学习Laravel-Request 生命周期

Laravel 文件中有跟我们介绍一个 request 的生命周期,也就是诞生到结束在 Larave...

Java 开发 WEB 的好平台 -- Grails -- (2) 新增一个 Grails 专案

说明 我在本系列文章中,主要是采用 IntelliJ-IDEA 作为示范。但我不会在文章中跟你讲述如...

Day 04-Azure介绍

在上一篇我们看到,即便我们能不写程序就设定一些自动回覆,仍然相当麻烦,如果需要的功能更多,更无法应付...