开发基于HTTP协议的自定义授权服务

服务网格 ASM(Service Mesh)支持完善的认证、授权配置,还提供了灵活的扩展能力,支持对接HTTP协议和gRPC协议的自定义授权服务,并接入网格的鉴权流程。本文介绍如何开发一个基于HTTP协议的自定义授权服务。

背景信息

通过服务网格,您可以在网关上配置JWT认证(RequestAuthentication)完成对请求身份的认证;对于网格内的请求,默认通过网格提供的mTLS证书完成请求身份的认证。确认了请求身份之后,您可以通过授权策略(AuthorizationPolicy)限制请求的行为。在提供了上述标准能力的同时,服务网格还支持接入自定义授权服务。自定义授权服务的整体流程大致如下。

image

当您配置使用HTTP协议对接自定义授权服务时,网格代理(网关或Sidecar)会将收到的请求信息填充到HTTP协议的鉴权请求中,然后发往自定义授权服务,由自定义授权服务判断是否放行该请求。

  • 自定义授权服务返回200表示通过验证,需要放行请求。

  • 自定义授权服务返回5xx代表授权服务异常,需要根据配置决定放行或拒绝请求。

  • 其他状态码均会被视为验证不通过,需要拒绝该请求。

配置介绍

ASM支持使用界面关联一个自定义授权服务,关联之后您可以在授权策略中配置要使用鉴权服务的网格代理。具体操作,请参见使用HTTP协议对接自定义授权服务

开发HTTP自定义授权服务

ASM兼容开源Istio服务网格,Istio社区提供了自定义授权服务的开发示例。这部分代码同时实现了HTTP和gRPC两种协议的自定义授权服务。本文涉及的HTTP部分主要逻辑在ServeHTTP函数中:

// ServeHTTP implements the HTTP check request.
func (s *ExtAuthzServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	body, err := io.ReadAll(request.Body)
	if err != nil {
		log.Printf("[HTTP] read body failed: %v", err)
	}
	l := fmt.Sprintf("%s %s%s, headers: %v, body: [%s]\n", request.Method, request.Host, request.URL, request.Header, returnIfNotTooLong(string(body)))
	if allowedValue == request.Header.Get(checkHeader) {
		log.Printf("[HTTP][allowed]: %s", l)
		response.Header().Set(resultHeader, resultAllowed)
		response.Header().Set(overrideHeader, request.Header.Get(overrideHeader))
		response.Header().Set(receivedHeader, l)
		response.WriteHeader(http.StatusOK)
	} else {
		log.Printf("[HTTP][denied]: %s", l)
		response.Header().Set(resultHeader, resultDenied)
		response.Header().Set(overrideHeader, request.Header.Get(overrideHeader))
		response.Header().Set(receivedHeader, l)
		response.WriteHeader(http.StatusForbidden)
		_, _ = response.Write([]byte(denyBody))
	}
}

可以看出,此函数对请求的header进行了判断。如果header的值是allowedValue就返回200(允许通过),其他情况返回403(不允许通过)。

配置自定义授权服务

在ACK集群中部署上一步开发的自定义授权服务后,您可以开始自定义授权服务的接入。具体操作,请参见接入HTTP协议的自定义授权服务

重要

由于此授权服务中用到了checkHeader这个变量对应的header,所以您需要在网格中导入自定义授权服务时配置在鉴权请求中携带header配置项,否则无法获取到对应的返回结果。