ASM提供了自定义外部授权服务,在服务间互相通信时加入鉴权流程,以确保只有得到授权的情况下,才能访问关键服务。本文以httpbin应用为例,介绍如何实现自定义外部授权。

前提条件

步骤一:创建外部授权应用

在集群中创建外部授权应用,该应用实现外部鉴权逻辑。本文使用的示例应用要求请求必须带有x-ext-authz: allow请求头,才能通过鉴权访问成功。

  1. 通过kubectl工具连接集群
  2. 使用以下内容,创建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: istio/ext-authz:0.6
            imagePullPolicy: IfNotPresent
            name: ext-authz
            ports:
            - containerPort: 8000
            - containerPort: 9000
    ---
  3. 执行以下命令,在集群中部署外部授权服务。
    kubectl apply -f ext-authz.yaml

    预期输出:

    service/ext-authz created
    deployment.apps/ext-authz created
  4. 执行以下命令, 验证应用是否正常工作。
    kubectl logs "$(kubectl get pod -l app=ext-authz -n default -o jsonpath={.items..metadata.name})" -n default -c ext-authz

    预期输出:

    2021/01/07 22:55:47 Starting HTTP server at [::]:8000
    2021/01/07 22:55:47 Starting gRPC server at [::]:9000

    返回以上结果,说明外部授权服务部署成功。

  5. 获取ext-authz应用的GRPC协议端口。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择网络 > 服务
    5. 服务页面单击ext-authz。
      端点区域可以看到GRPC协议的端口为9000。

步骤二:创建示例应用

  1. 使用以下内容,创建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
    # 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

步骤三:管理外部授权服务

您需要将步骤一创建的应用声明到服务网格中,使服务网格可以使用该应用进行鉴权。

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格详情页面左侧导航栏选择零信任安全 > 授权策略
  5. 授权策略页面单击管理外部授权服务,单击创建
  6. 创建外部授权页面设置参数,然后单击创建
    参数 描述
    名称 自定义外部授权服务名称,本文设置为test。
    协议 选择外部授权应用的协议,本文设置为GRPC。
    服务地址 输入外部授权应用的服务地址<应用名称>.<命名空间名称>.svc.<集群域名>,本文设置为ext-authz.default.svc.cluster.local
    服务端口 输入外部授权应用的服务端口,本文设置为9000。
    超时时间 如果鉴权应用未在该时间内返回,则认为鉴权服务不可用,本文设置为10秒。

步骤四:创建授权策略

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

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格详情页面左侧导航栏选择零信任安全 > 授权策略
  5. 授权策略页面单击创建
  6. 在创建页面配置参数,然后单击确定
    参数 描述
    命名空间 选择示例应用部署的命名空间,本文设置为default。
    名称 自定义授权策略名称,本文设置为test1。
    策略 设置授权策略,本文设置为RULES。
    动作 设置授权动作,本文设置为CUSTOM。
    Provider Name 选择外部授权服务,本文设置为test。
    工作负载标签选择 设置授权策略生效的工作负载。

    打开工作负载标签选择开关,单击新增匹配标签,设置名称为app,为httpbin。

    请求操作 设置需要鉴权的请求操作。

    打开请求操作开关,单击增加请求操作到列表中,单击增加请求操作,设置请求操作域为path,为/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

    返回以上结果,说明没有触发鉴权。

  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

    预期输出:

    denied by ext_authz for not found header `x-ext-authz: allow` in the request

    返回以上结果,说明触发鉴权,但是鉴权未通过。

  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:8000",
        "User-Agent": "curl/7.76.0-DEV",
        "X-B3-Parentspanid": "430f770aeb7ef215",
        "X-B3-Sampled": "0",
        "X-B3-Spanid": "60ff95c5acdf5288",
        "X-B3-Traceid": "fba72bb5765daf5a430f770aeb7e****",
        "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=e5178ee79066bfbafb1d98044fcd0cf80db76be8714c7a4b630c7922df520bf2;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/sleep"
      }
    }

    返回以上结果,说明触发鉴权,并且鉴权通过。

    根据以上结果,可以看到访问httpbin.default:8000/headers时,请求中必须带有x-ext-authz: allow才能访问成功,说明自定义外部授权成功。