接入HTTP协议的自定义授权服务

当您需要对使用HTTP协议通信的服务进行细粒度的访问控制时,可以使用自定义授权服务功能,根据特定业务需求定制授权机制,在服务间互相通信时加入鉴权流程,确保只有经过认证和授权的请求才能访问相应的服务资源,提高服务间通信的安全性。本文以sleep与httpbin应用为例,介绍如何实现接入HTTP协议的自定义授权服务。

前提条件

已添加集群到ASM实例

步骤一:部署自定义授权服务

在ACK集群中部署自定义授权服务,该服务需遵循Istio自定义鉴权服务接口规范,支持HTTP和gRPC协议,用于实现自定义鉴权逻辑。本文使用的示例服务要求请求必须带有x-ext-authz: allow请求头,才能通过鉴权访问成功。

说明

本文提供了自定义授权服务示例,您也可以参考本示例应用的代码,创建自己的自定义授权服务。具体内容,请参见自定义授权

  1. 通过kubectl连接集群,使用以下内容,创建ext-authz.yaml

    关于如何通过kubectl连接集群,请参见获取集群KubeConfig并通过kubectl工具连接集群

    展开查看ext-authz.yaml

    # Copyright Istio Authors
    #
    #   Licensed under the Apache License, Version 2.0 (the "License");
    #   you may not use this file except in compliance with the License.
    #   You may obtain a copy of the License at
    #
    #       http://www.apache.org/licenses/LICENSE-2.0
    #
    #   Unless required by applicable law or agreed to in writing, software
    #   distributed under the License is distributed on an "AS IS" BASIS,
    #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #   See the License for the specific language governing permissions and
    #   limitations under the License.
    
    # Example configurations for deploying ext-authz server separately in the mesh.
    
    apiVersion: v1
    kind: Service
    metadata:
      name: ext-authz
      labels:
        app: ext-authz
    spec:
      ports:
      - name: http
        port: 8000
        targetPort: 8000
      - name: grpc
        port: 9000
        targetPort: 9000
      selector:
        app: ext-authz
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ext-authz
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: ext-authz
      template:
        metadata:
          labels:
            app: ext-authz
        spec:
          containers:
          - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/asm-ext-authz-example:0.6
            imagePullPolicy: IfNotPresent
            name: ext-authz
            ports:
            - containerPort: 8000
            - containerPort: 9000
    ---
  2. 执行以下命令,在集群中部署自定义授权服务。

    kubectl apply -f ext-authz.yaml
  3. 执行以下命令,查看Pod部署情况。

    kubectl get pod

    预期输出:

    NAME                         READY   STATUS    RESTARTS   AGE
    ext-authz-6b5db88f86-2m7c6   2/2     Running   0          79m
  4. 执行以下命令,验证应用是否正常工作。

    kubectl logs "$(kubectl get pod -l app=ext-authz -n default -o jsonpath={.items..metadata.name})" -n default -c ext-authz

    预期输出:

    2023/12/20 08:15:39 Starting gRPC server at [::]:9000
    2023/12/20 08:15:39 Starting HTTP server at [::]:8000

    返回以上结果,说明应用正常工作,自定义授权服务部署成功。

  5. 获取ext-authz授权服务的HTTP协议端口。

    1. 登录容器服务管理控制台,在左侧导航栏选择集群

    2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择网络 > 服务

    3. 服务页面,单击ext-authz

      端点区域,可以看到HTTP协议的端口为8000。访问该服务的HTTP地址为ext-authz.default.svc.cluster.local:8000

步骤二:部署示例应用

  1. 使用以下内容,创建httpbin.yaml

    展开查看httpbin.yaml

    # Copyright Istio Authors
    #
    #   Licensed under the Apache License, Version 2.0 (the "License");
    #   you may not use this file except in compliance with the License.
    #   You may obtain a copy of the License at
    #
    #       http://www.apache.org/licenses/LICENSE-2.0
    #
    #   Unless required by applicable law or agreed to in writing, software
    #   distributed under the License is distributed on an "AS IS" BASIS,
    #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #   See the License for the specific language governing permissions and
    #   limitations under the License.
    
    ##################################################################################################
    # httpbin service
    ##################################################################################################
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: httpbin
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin
      labels:
        app: httpbin
        service: httpbin
    spec:
      ports:
      - name: http
        port: 8000
        targetPort: 80
      selector:
        app: httpbin
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: httpbin
          version: v1
      template:
        metadata:
          labels:
            app: httpbin
            version: v1
        spec:
          serviceAccountName: httpbin
          containers:
          - image: docker.io/kennethreitz/httpbin
            imagePullPolicy: IfNotPresent
            name: httpbin
            ports:
            - containerPort: 80
                            
  2. 执行以下命令,在集群中部署httpbin应用。

    kubectl apply -f httpbin.yaml
  3. 使用以下内容,创建sleep.yaml

    展开查看sleep.yaml

    # Copyright Istio Authors
    #
    #   Licensed under the Apache License, Version 2.0 (the "License");
    #   you may not use this file except in compliance with the License.
    #   You may obtain a copy of the License at
    #
    #       http://www.apache.org/licenses/LICENSE-2.0
    #
    #   Unless required by applicable law or agreed to in writing, software
    #   distributed under the License is distributed on an "AS IS" BASIS,
    #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #   See the License for the specific language governing permissions and
    #   limitations under the License.
    
    ##################################################################################################
    # Sleep service
    ##################################################################################################
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: curlimages/curl
            command: ["/bin/sleep", "3650d"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
    ---
                            
  4. 执行以下命令,在集群中部署sleep应用。

    kubectl apply -f sleep.yaml

步骤三:使用HTTP协议对接自定义授权服务

步骤一部署的服务声明到服务网格中,使服务网格可以使用该服务进行鉴权。

  1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

  2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择网格安全中心 > 自定义授权服务,然后单击关联自定义授权服务

  3. 关联自定义授权服务页面,单击基于envoy.ext_authz实现的自定义授权服务(HTTP或gRPC协议)页签,进行相关配置,然后单击创建

    类型

    配置项

    说明

    必选参数

    协议

    选择自定义授权应用的协议。本文选择HTTP

    名称

    自定义授权服务名称。本文设置为test4http

    服务地址

    输入自定义授权应用的服务地址<应用名称>.<命名空间名称>.svc.<集群域名>,此处必须为全称。本文设置为ext-authz.default.svc.cluster.local

    服务端口

    输入自定义授权应用的服务端口。本文设置为8000

    超时时间

    如果鉴权应用未在该时间内返回,则认为鉴权服务不可用,本文设置为10秒。

    可选参数

    鉴权服务不可用时放行请求

    是否在鉴权服务不可用时放行请求。若启用该选项,则鉴权服务不可用时请求被放行。本文设置为不开启。

    鉴权服务不可用自定义错误码

    该选项仅在关闭鉴权服务不可用时放行请求时可选。若启用该选项,需填写错误码,在鉴权服务不可用时,该错误码将被返回至调用端。本文设置为不开启。

    在鉴权请求中携带header

    启用该选项后,需填写期望携带的Header Key,匹配的Header将被设置于传送至自定义鉴权服务的请求中。本文设置为如在鉴权请求中携带Header所示。

    说明

    仅设置协议HTTP时,才可以设置该参数。

    在鉴权请求中新增header

    启用该选项后,需填写期望在鉴权请求中新增的Header Key和Header Value,这些Header将被增加到鉴权服务的鉴权请求中。

    若Header在请求中已经存在,则覆盖原Header。本文设置为不开启。

    说明

    仅设置协议HTTP时,才可以设置该参数。

    鉴权通过时覆盖Header

    启用该选项后,需填写期望覆盖的Header Key,自定义鉴权通过时,使用鉴权请求Response中匹配的Header覆盖目标服务请求中的Header。本文设置如鉴权通过时覆盖Header所示。

    说明

    仅设置协议HTTP时,才可以设置该参数。

    鉴权失败时覆盖Header

    启用该选项后,需填写期望覆盖的Header Key,自定义鉴权失败时,使用鉴权请求Response中匹配的Header覆盖返回至调用服务Response中的Header。本文设置如鉴权失败时覆盖Header所示。

    说明

    仅设置协议HTTP时,才可以设置该参数。

    在鉴权请求中携带请求Body

    启用该选项后,需填写鉴权请求携带Body的最大长度。若启用允许将不完整消息发往鉴权服务,当被鉴权请求Body大于设置的最大长度时,从最大长度处截取,并将截取后的Body发往鉴权服务。

    图 1. 在鉴权请求中携带Header

    说明

    最后一行为新增x-ext-authz

    在鉴权请求中携带Header

    图 2. 鉴权通过时覆盖Header

    说明

    最后一行为新增x-ext-authz-check-result

    鉴权通过时覆盖Header

    图 3. 鉴权失败时覆盖Header

    说明

    最后一行为新增x-ext-authz-check-result

    鉴权失败时覆盖Header

步骤四:为网格内的服务定义授权策略

您需要创建授权策略来配置需要鉴权的请求操作。

  1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

  2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择网格安全中心 > 授权策略,然后单击创建

  3. 创建页面,进行相关配置,然后单击创建

    配置项

    说明

    名称

    自定义授权策略名称,本文设置为test1

    策略类型

    选择自定义授权服务

    自定义授权服务

    选择httpextauth-test4http(HTTP)

    命名空间

    工作负载生效页签,选择default命名空间。

    生效范围

    选择Service

    工作负载

    选择httpbin

    请求匹配规则

    添加请求目标区域,打开HTTP路径(Paths)开关,设置值为/headers

步骤五:验证服务间的自定义授权是否成功

  1. 执行以下命令,访问httpbin.default:8000/ip

    kubectl exec "$(kubectl get pod -l app=sleep -n default -o jsonpath={.items..metadata.name})" -c sleep -n default -- curl "http://httpbin.default:8000/ip" -s -o /dev/null -w "%{http_code}\n"

    返回200,说明没有触发鉴权。因为访问的路径为/ip,不是授权策略中定义的/headers,所以不被授权策略约束。

  2. 执行以下命令,带有x-ext-authz: deny请求头访问httpbin.default:8000/headers

    kubectl exec "$(kubectl get pod -l app=sleep -n default -o jsonpath={.items..metadata.name})" -c sleep -ndefault -- curl "http://httpbin.default:8000/headers" -H "x-ext-authz: deny" -s -i

    预期输出:

    HTTP/1.1 403 Forbidden
    x-ext-authz-check-result: denied
    content-length: 76
    content-type: text/plain; charset=utf-8
    date: Wed, 20 Dec 2023 09:53:28 GMT
    server: envoy
    x-envoy-upstream-service-time: 10
    
    denied by ext_authz for not found header `x-ext-authz: allow` in the request

    返回以上结果,说明触发鉴权,但是鉴权未通过,返回的结果中存在新定义的响应头x-ext-authz-check-result: denied。因为访问的路径是授权策略中定义的/headers,所以会被授权策略约束。

  3. 执行以下命令,带有x-ext-authz: allow请求头访问httpbin.default:8000/headers

    kubectl exec "$(kubectl get pod -l app=sleep -n default -o jsonpath={.items..metadata.name})" -c sleep -n default -- curl "http://httpbin.default:8000/headers" -H "x-ext-authz: allow" -s

    预期输出:

    {
      "headers": {
        "Accept": "*/*",
        "Host": "httpbin.default:8000",
        "User-Agent": "curl/8.5.0",
        "X-Envoy-Attempt-Count": "1",
        "X-Ext-Authz": "allow",
        "X-Ext-Authz-Check-Result": "allowed",
        "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=c3e5364e87add0f4f69e6b0d029f5961b404c8f209bf9004b3d21a82cf67****;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/sleep"
      }
    }

    返回以上结果,说明触发鉴权,并且鉴权通过,返回的结果中存在新定义的响应头"X-Ext-Authz-Check-Result": "allowed"。因为访问的路径是授权策略中定义的/headers,所以会被授权策略约束。

相关操作