OPA策略在流水线中的构建产物签名验证与合规阻断 - 第2节

2.1 OPA 整体架构

Open Policy Agent (OPA) 是一个开源的、通用策略引擎,它将策略决策与策略执行解耦。在 DevSecOps 流水线中,OPA 作为“策略决策点”(Policy Decision Point, PDP)独立运行,通过标准的 API 接口接收查询并返回决策结果。

核心架构原则:OPA 不负责执行策略(如阻断流水线),而是负责回答“是否允许”的问题。执行动作由调用方(如 CI/CD 工具、API 网关)根据 OPA 的决策结果自行实施。
OPA 架构核心组件与数据流 CI/CD 流水线 (策略执行点) 查询请求 OPA 策略引擎 (策略决策点) 决策响应 流水线阻断/放行 (执行动作) 外部数据加载 数据文档 Rego 策略加载 策略规则库 决策引擎 决策缓存

2.1.1 架构组件说明

组件 角色 说明
策略执行点 (PEP) 调用方 CI/CD 工具、API 网关、Kubernetes Admission Controller 等,向 OPA 发起查询并执行决策结果
策略决策点 (PDP) OPA 引擎 接收查询请求,结合策略规则与数据文档,返回决策结果(allow/deny)
数据文档 外部数据 JSON/YAML 格式的上下文数据,如构建产物元数据、签名信息、环境标签等
策略规则库 Rego 策略 用 Rego 语言编写的策略规则集合,定义合规条件与阻断逻辑

2.2 Rego 语言核心概念

Rego 是 OPA 专用的声明式策略语言,灵感来源于 Datalog。与通用编程语言不同,Rego 专注于“数据查询”和“规则定义”,而非“流程控制”。

2.2.1 基本语法结构

package pipeline.verify

# 规则定义:签名验证通过
default allow = false

allow {
    input.signature.valid == true
    input.signature.algorithm == "SHA256-RSA"
    input.signature.expiry > time.now_ns()
}

# 规则定义:产物来源可信
allow {
    input.provenance.builder == "trusted-builder"
    input.provenance.slsa_level >= 2
}
关键理解:Rego 规则由 package(包声明)、default(默认值)、规则头 和 规则体 组成。规则体中的多个条件之间是逻辑“与”(AND)关系;多个同名规则之间是逻辑“或”(OR)关系。

2.2.2 数据模型:input 与 data

命名空间 来源 示例
input 查询请求时传入的临时数据 input.signature.hash、input.artifact.name
data OPA 启动时加载的持久化数据 data.trusted_signers、data.allowed_registries
Rego 数据模型:input vs data input 命名空间 查询时传入的临时数据 input.signature.hash input.artifact.name input.pipeline.stage 生命周期:单次查询 data 命名空间 加载的持久化数据 data.trusted_signers data.allowed_registries data.compliance_rules 生命周期:引擎运行期间 对比

2.3 规则模型与决策逻辑

2.3.1 规则类型

类型 语法 用途
完整规则 r = v { ... } 返回一个确定的值,如 allow = true
部分规则 r[x] { ... } 生成一组结果集合,如 violations[msg]
默认规则 default r = v 为规则设置默认值,防止未定义

2.3.2 决策组合模式

在流水线签名验证场景中,通常采用“允许列表 + 阻断列表”的组合决策模式:

package pipeline.verify

# 默认拒绝
default allow = false

# 允许条件:签名有效且签名者受信任
allow {
    input.signature.valid == true
    data.trusted_signers[input.signature.signer]
}

# 阻断条件:签名者被明确列入黑名单
deny[reason] {
    input.signature.valid == true
    data.blocked_signers[input.signature.signer]
    reason := sprintf("签名者 %v 已被列入黑名单", [input.signature.signer])
}

# 阻断条件:签名已过期
deny[reason] {
    input.signature.expiry < time.now_ns()
    reason := "签名已过期,请重新签名"
}
重要实践:在流水线合规阻断中,建议始终使用 default allow = false(默认拒绝)模式。任何未明确允许的请求都应被阻断,这符合安全最小权限原则。同时使用 deny 规则提供详细的阻断原因,便于流水线输出可读的错误信息。

2.4 数据与规则的加载方式

2.4.1 数据加载

  • 启动时加载:通过 --data 参数加载 JSON/YAML 文件,作为 data 命名空间的持久数据
  • API 推送:通过 OPA REST API PUT /v1/data 动态更新数据文档
  • Bundle 服务:从 Bundle 服务器拉取打包的策略与数据,支持版本管理和热更新

2.4.2 策略加载

  • 文件加载:通过 --policy 参数加载 .rego 策略文件
  • Bundle 打包:将多个 .rego 文件打包为 .tar.gz,通过 Bundle API 分发
  • 内联策略:在查询请求中直接携带策略(适用于测试和轻量场景)
流水线集成建议:在生产环境中,推荐使用 Bundle 服务管理策略与数据。将签名验证策略、受信任签名者列表、黑名单等打包为 Bundle,CI/CD 流水线中的 OPA 客户端定期拉取最新版本,确保策略的实时更新与一致性。