应用 Open Policy Agent (OPA) 策略

通过中间件对传入请求应用 Open Policy Agent (OPA) 策略

Open Policy Agent (OPA) HTTP 中间件 用于对传入的 Dapr HTTP 请求应用 OPA 策略。这可以用于在应用程序端点上实施可重用的授权策略。

组件格式

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: my-policy
spec:
  type: middleware.http.opa
  version: v1
  metadata:
    # `includedHeaders` 是一个不区分大小写的逗号分隔的头集合,包含在请求输入中。
    # 默认情况下,请求头不会传递给策略。需要明确指定以接收传入请求头。
    - name: includedHeaders
      value: "x-my-custom-header, x-jwt-header"

    # `defaultStatus` 是拒绝响应时返回的状态码
    - name: defaultStatus
      value: 403

    # `readBody` 控制中间件是否在内存中读取整个请求体并使其可用于策略决策。
    - name: readBody
      value: "false"

    # `rego` 是要评估的 open policy agent 策略。必需
    # 策略包必须命名为 http,策略必须设置 data.http.allow
    - name: rego
      value: |
        package http

        default allow = true

        # Allow 也可以是一个对象并包含其他属性

        # 例如,如果您想在策略失败时重定向,可以将状态码设置为 301 并在响应中设置位置头:
        allow = {
            "status_code": 301,
            "additional_headers": {
                "location": "https://my.site/authorize"
            }
        } {
            not jwt.payload["my-claim"]
        }

        # 您还可以允许请求并向其添加其他头:
        allow = {
            "allow": true,
            "additional_headers": {
                "x-my-claim": my_claim
            }
        } {
            my_claim := jwt.payload["my-claim"]
        }
        jwt = { "payload": payload } {
            auth_header := input.request.headers["Authorization"]
            [_, jwt] := split(auth_header, " ")
            [_, payload, _] := io.jwt.decode(jwt)
        }        

您可以使用 官方 OPA playground 来原型和实验策略。例如,您可以在此处找到上面的示例策略

规格元数据字段

字段 详情 示例
rego Rego 策略语言 见上文
defaultStatus 拒绝响应时返回的状态码 "https://accounts.google.com""https://login.salesforce.com"
readBody 如果设置为 true(默认值),则每个请求的主体将完全在内存中读取,并可用于进行策略决策。如果您的策略不依赖于检查请求体,请考虑禁用此功能(设置为 false)以显著提高性能。 "false"
includedHeaders 一个不区分大小写的逗号分隔的头集合,包含在请求输入中。默认情况下,请求头不会传递给策略。需要明确指定以接收传入请求头。 "x-my-custom-header, x-jwt-header"

Dapr 配置

要应用中间件,必须在 配置 中引用。请参阅 中间件管道

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  httpPipeline:
    handlers:
    - name: my-policy
      type: middleware.http.opa

输入

此中间件提供一个 HTTPRequest 作为输入。

HTTPRequest

HTTPRequest 输入包含有关传入 HTTP 请求的所有相关信息。

type Input struct {
  request HTTPRequest
}

type HTTPRequest struct {
  // 请求方法(例如 GET,POST 等)
  method string
  // 原始请求路径(例如 "/v2/my-path/")
  path string
  // 路径分解为部分以便于使用(例如 ["v2", "my-path"])
  path_parts string[]
  // 原始查询字符串(例如 "?a=1&b=2")
  raw_query string
  // 查询分解为键及其值
  query map[string][]string
  // 请求头
  // 注意:默认情况下,不包括任何头。您必须指定要通过 `spec.metadata.includedHeaders` 接收的头(见上文)
  headers map[string]string
  // 请求方案(例如 http, https)
  scheme string
  // 请求体(例如 http, https)
  body string
}

结果

策略必须设置 data.http.allow,可以是 boolean 值,也可以是具有 allow 布尔属性的 object 值。trueallow 将允许请求,而 false 值将拒绝请求,并使用 defaultStatus 指定的状态。以下策略,带有默认值,演示了对所有请求的 403 - Forbidden

package http

default allow = false

这与以下相同:

package http

default allow = {
  "allow": false
}

更改拒绝响应状态码

拒绝请求时,您可以覆盖返回的状态码。例如,如果您想返回 401 而不是 403,可以执行以下操作:

package http

default allow = {
  "allow": false,
  "status_code": 401
}

添加响应头

要重定向,请添加头并将 status_code 设置为返回结果:

package http

default allow = {
  "allow": false,
  "status_code": 301,
  "additional_headers": {
    "Location": "https://my.redirect.site"
  }
}

添加请求头

您还可以在允许的请求上设置其他头:

package http

default allow = false

allow = { "allow": true, "additional_headers": { "X-JWT-Payload": payload } } {
  not input.path[0] == "forbidden"
  // 其中 `jwt` 是另一个规则的结果
  payload := base64.encode(json.marshal(jwt.payload))
}

结果结构

type Result bool
// 或
type Result struct {
  // 是否允许或拒绝传入请求
  allow bool
  // 覆盖拒绝响应状态码;可选
  status_code int
  // 设置允许请求或拒绝响应的头;可选
  additional_headers map[string]string
}

相关链接