在ASM中实现动态更新OPA策略

服务网格ASM集成了开放策略代理(OPA)插件,通过OPA定义访问控制策略,可以使您的应用实现细粒度的访问控制。v1.8.6.41及以上版本的ASM还支持通过配置ConfigMap将OPA策略自动推送到容器中,实现动态更新OPA策略。本文介绍如何在ASM中实现动态更新OPA策略。

前提条件

背景信息

作为由CNCF托管的一个孵化项目,开放策略代理(OPA)是一个策略引擎,可用于为您的应用程序实现细粒度的访问控制。OPA作为通用策略引擎,可以与微服务一起部署为独立服务。为了保护应用程序,必须先授权对微服务的每个请求,然后才能对其进行处理。为了检查授权,微服务对OPA进行API调用,以确定请求是否被授权。OPA

步骤一:启用OPA插件

  1. 登录ASM控制台

  2. 在左侧导航栏,选择服务网格 > 网格管理

  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理

  4. 基本信息页面单击右上角的功能设置

  5. 功能设置更新页面中选中启用OPA插件

  6. 单击确定

    基本信息页面可以看到OPA插件的状态变为开启

步骤二:设置ConfigMap

服务网格ASM支持动态更新OPA策略,您可以配置包含openpolicyagent.org/policy=rego标签的ConfigMap,该策略会被自动推送到所有命名空间中注入了OPA代理容器的Pod。移除该ConfigMap时,策略也会从Pod中移除。

重要
  • OPA定义一个Pod的OPA策略时,只允许有一条default allow字段。如果多个OPA策略相关的ConfigMap生效于一个Pod,且每个策略中都定义了default allow字段,则多条default allow字段会导致动态更新失败。

  • OPA代理容器启动时需要依赖名为opa-policy的ConfigMap。如果该ConfigMap被删除,相应的OPA策略也会在OPA代理容器中被移除。重建该ConfigMap并不会使之生效,只能重建Pod。

  1. 步骤二:选择集群凭证类型

  2. 创建名为opa-policy的ConfigMap。

    OPA代理容器启动时需要依赖名为opa-policy的ConfigMap。该ConfigMap有动态更新功能,推荐该ConfigMap只做基础的策略配置,后续复杂策略配置通过其他ConfigMap动态添加。

    1. 使用以下内容,创建名为opa-policy的YAML文件。

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: opa-policy
      data:
        policy.rego: | ### 该策略配置内容所允许的path为opa策略动态推送依赖的,如不配置将导致动态更新策略失败。
          package istio.authz
      
          import input.parsed_path
          
          allow {
                  parsed_path[0] = "v1"
                  parsed_path[1] = "policies"
          }
    2. 执行以下命令,创建ConfigMap。

      kubectl apply -f opa-policy.yaml
  3. 创建名为opa-policy-add的ConfigMap。

    使用该ConfigMap定义OPA策略。

    1. 使用以下内容,创建名为opa-policy-add的YAML文件。

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: opa-policy-add
        labels:
          ### 请注意配置label,只有该带label的ConfigMap才会作为policy进行动态更新。
          openpolicyagent.org/policy: rego
      data:
        policy.rego: |  ###以下为示例策略定义,需要替换成实际的策略定义。
          package istio.authz
          import input.attributes.request.http as http_request
          default allow = false
          allow {
              roles_for_user[r]
              required_roles[r]
          }
          roles_for_user[r] {
              r := user_roles[user_name][_]
          }
          required_roles[r] {
              perm := role_perms[r][_]
              perm.method = http_request.method
              perm.path = http_request.path
          }
          user_name = parsed {
              [_, encoded] := split(http_request.headers.authorization, " ")
              [parsed, _] := split(base64url.decode(encoded), ":")
          }
          user_roles = {
              "guest1": ["guest"],
              "admin1": ["admin"]
          }
          role_perms = {
              "guest": [
                  {"method": "GET",  "path": "/productpage"},
              ],
              "admin": [
                  {"method": "GET",  "path": "/productpage"},
                  {"method": "GET",  "path": "/api/v1/products"},
              ],
          }     
      • user_roles:为用户授予角色权限。本例设置guest1拥有guest角色权限,admin1拥有admin角色权限。

      • role_perms:设置角色拥有的权限。本文设置guest角色可以通过/productpage访问应用,admin角色可以通过/productpage/api/v1/products访问应用。

    2. 执行以下命令,创建ConfigMap。

      kubectl apply -f opa-policy-add.yaml
  4. 执行以下命令,查看策略推送结果。

    策略推送结果会更新在ConfigMap的annotations

    kubectl get configmap  opa-policy-add -o yaml  

    在返回结果中查看ConfigMap:

    • 如果推送成功,则会在ConfigMap中看到以下信息。

      openpolicyagent.org/policy-status: '{"status":"ok"}'
    • 如果推送失败,则会有相应错误信息。

步骤三:注入OPA代理

部署示例应用Bookinfo到ASM实例,确认每个Pod都注入了OPA代理。

  1. 部署示例应用Bookinfo到ASM实例,请参见部署应用到ASM实例

  2. 定义相应的Istio虚拟服务和入口网关,请参见使用Istio资源实现版本流量路由

  3. 检查每个应用是否都注入OPA代理。

    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏,单击集群
    3. 集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏,选择工作负载 > 容器组
    5. 容器组页面,从命名空间下拉列表中选择default,单击目标应用容器组的名称。

      容器页签下可以看到容器被注入了Sidecar代理(istio-proxy)和OPA代理(opa-istio)。依次检查每个应用的容器,确保每个容器都被注入了Sidecar代理和OPA代理。注入OPA代理

步骤四:验证使用OPA定义访问控制策略是否成功。

  • 执行以下命令,可以看到guest1被授予guest角色,并且可以使用带有/productpage的URL访问应用,但不能使用带有/v1/api/products的URL访问应用。

    curl -X GET http://<入口网关服务的IP地址>/productpage --user guest1:password -I

    预期输出:

    HTTP/1.1 200 OK
    curl -X GET http://<入口网关服务的IP地址>/api/v1/products --user guest1:password -I

    预期输出:

    HTTP/1.1 403 Forbidden
  • 执行以下命令,可以看到admin1被授予admin角色,并且可以使用带有/productpage和/v1/api/products的URL访问应用。

    curl -X GET http://<入口网关服务的IP地址>/productpage --user admin1:password -I

    预期输出:

    HTTP/1.1 200 OK
    curl -X GET http://<入口网关服务的IP地址>/api/v1/products --user admin1:password -I

    预期输出:

    HTTP/1.1 200 OK

    根据以上结果,可以看到OPA定义访问控制策略是成功的。

步骤五:动态更新OPA策略

  1. 在ACK集群上执行以下命令,更新名为opa-policy-add的ConfigMap。

    kubectl replace -n {替换成实际的namespace} -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: opa-policy-add
      labels:
        ### 请注意配置label,只有该带label的ConfigMap才会作为policy进行动态更新。
        openpolicyagent.org/policy: rego
    data:
      policy.rego: |  ###以下为示例策略定义,需要替换成实际的策略定义。
        package istio.authz
        import input.attributes.request.http as http_request
        default allow = false
        allow {
            roles_for_user[r]
            required_roles[r]
        }
        roles_for_user[r] {
            r := user_roles[user_name][_]
        }
        required_roles[r] {
            perm := role_perms[r][_]
            perm.method = http_request.method
            perm.path = http_request.path
        }
        user_name = parsed {
            [_, encoded] := split(http_request.headers.authorization, " ")
            [parsed, _] := split(base64url.decode(encoded), ":")
        }
        user_roles = {
            "guest1": ["guest", "admin"],
            "admin1": ["admin"]
        }
        role_perms = {
            "guest": [
                {"method": "GET",  "path": "/productpage"},
            ],
            "admin": [
                {"method": "GET",  "path": "/productpage"},
                {"method": "GET",  "path": "/api/v1/products"},
            ],
        }
    EOF
    • user_roles:为用户授予角色权限。本例设置guest1同时拥有guest和admin角色权限,admin1拥有admin角色权限。

    • role_perms:设置角色拥有的权限。本例设置guest角色可以通过/productpage访问应用,admin角色可以通过/productpage/api/v1/products访问应用。

  2. 执行以下命令,查看策略推送结果。

    策略推送结果会更新在ConfigMap的annotations

    kubectl get configmap  opa-policy-add -o yaml  

    在返回结果中查看ConfigMap:

    • 如果推送成功,则会在ConfigMap中看到以下信息。

      openpolicyagent.org/policy-status: '{"status":"ok"}'
    • 如果推送失败,则会有相应错误信息。

步骤六:验证动态更新OPA策略是否成功

执行以下命令,可以看到guest1被授予了admin角色,此时可以使用/productpage和/v1/api/products访问应用。

curl -X GET http://<入口网关服务的IP地址>/productpage --user guest1:password -I

预期输出:

HTTP/1.1 200 OK
curl -X GET http://<入口网关服务的IP地址>/api/v1/products --user guest1:password -I

预期输出:

HTTP/1.1 200 OK

在没有更新OPA策略之前,guest1只能使用带有/productpage的URL访问应用,但不能使用带有/v1/api/products的URL访问应用。更新OPA策略之后,guest1可以使用带有/productpage和/v1/api/products的URL访问应用。说明动态更新OPA策略成功。