OPA,全称OpenPolicyAgent, 底层用Go实现,它灵活而强大的声明式语言全面支持通用策略定义。
而且,2019年4月2号OPA正式进入了CNCF,作为孵化级托管项目,详见声明
目前国内资料还比较少。
个人因为工作接触比较多,打算陆续分享些教程介绍下。
私以为策略引擎的技术选型完全可以多这个选择~~
什么是OPA
具体看官方文档 OPA philosophy docs
主要关键词是:
- 轻量级的通用策略引擎
- 可与服务共存
- 集成方式可以是sidecar、主机级守护进程或库引入

文字图片还是不够生动,看看OPA作者怎么说:
OPA: The Cloud Native Policy Engine - Torin Sandall, Styra
优点
强大的声明式策略
- 上下文感知
- 表达性强
- 快速
- 可移植
输入和输出支持任意格式
配合强大的声明式策略语言Rego,描述任意规则都不是问题
- 全面支持规则和系统解耦

- 集承方式多
- Daemon式服务
- Go类库引入
- 决策快
- 应用广泛
除了集成做Auth外,还可以应用到k8s,terraform,docker,kafka,sql,linux等上做规则决策
- 工具齐全
- 有命令行,有交互式运行环境
- 支持测试,性能分析(底层
Go实现) - 有强大的交互式编辑器扩展vscode-opa
- 有playground分享代码
下面从一个RBAC鉴权例子来了解下OPA
一个RBAC例子
以下json配置了role能操作的资源和user的绑定关系
1 | // data.json |
当一个请求读取widgets的user(如下json)过来操作资源,怎么判定他是否可以呢?
1 | // input.json |
可能你习惯性在想用自己趁手的语言和框架,一顿遍历循环搞定。
且慢,OPA告诉我们:
几行代码就可以!(当然代码少不是重点。。。)
这里是可以在线运行的代码示例

我们先抛开语法,代码其实就是描述了一条规则:
用户是否有角色,角色是否有权限操作的资源
下面我们开始学习OPA如何定义这条规则
基本语法
OPA基于一种数据查询语言Datalog实现了描述语言Rego
OPA的Rego基本语法如下表:
| 语法 | 例子 |
|---|---|
| 上下文 | data |
| 输入 | input |
| 索引取值 | data.bindings[0] |
| 比较 | “alice” == input.subject.user |
| 赋值 | user := input.subject.user |
| 规则 | < Header > { < Body > } |
| 规则头 | < Name > = < Value > { … } 或者 < Name > { … } |
| 规则体 | And运算的一个个描述 |
| 多条同名规则 | Or运算的一个规则 |
| 规则默认值 | default allow = false |
| 函数 | fun(x) { … } |
| 虚拟文档 | doc[x] { … } |
一点也不多。函数和虚拟文档我们后边再开文章展开,今天主要看明白他的规则定义。
首先输入会挂在input对象下,用到的上下文(就是规则决策基于的源数据)会挂在data对象下
rule
当定义规则时:
每条规则都会有返回值
- 格式1:
< Name > { ... }
不声明返回值,则只返回true或false
- 格式2
< Name > = < Value > { ... }
声明返回值
< Value >则返回其值- 格式1:
规则体内每条描述会逐条
And运算,全部成立才会返回值多条同名规则相互之间是
Or运算,满足其一即可
具体到代码中规则allow, 默认值是false
要求user_has_role和role_has_permission同时满足
两者的role_name也是一样。
你可能发现,局部变量role_name 没声明啊!
Rego里可以省略声明局部变量, 直接使用。
Tips: 但要这样的变量可以被同名的全局变量修改。
局部变量必要时还是应该使用some声明
如some role_name
1 | default allow = false |
然后其中user_has_role[role_name]这种带参数的结构不是规则,叫虚拟文档(文档:可被查询的集合)
1 | # check user role binding exist |
Tips: 仔细同学会发现,线上运行版有
with:role_binding = data.bindings[_] with data.bindings as data_context.bindings
with 是用来替换输入input或者上下文data里的数据。
因为线上版没法指定上边的data.json, 所以通过变量data_context替换传入的。
集合里边role_binding = data.bindings[_]是遍历data.bindings
Rego的遍历语法类似python,这里遍历流程是
将data.bindings一个值赋值给role_binding
进行后续处理,处理完后再赋下一个值
Tips:
_是特殊变量名,当需要变量占位又不需要后边引用时使用(类似Go的_)
至于role_binding.role = role_name这条你应该能猜到是判断请求过来的role名是否和配置一致
可是为什么是=操作符,不应该是==?
这里是一个有趣的点!
unification
Rego中实际只有=,而且作用是为变量赋值使等式成立,叫Unification
而:=局部变量赋值,==比较,是=的语法糖,为了实现局部变量赋值和比较,和编译错误更容易区分
所以=更像是数据查询。(毕竟Rego是一个数据查询语言嘛)
这里举个例子就好理解了:
1 | [x, "world"] = ["hello", y] |
总结一下,本文介绍什么是OPA,并借一个简单的RBAC例子初探了Rego强大的声明规则语法。
下一篇,将会介绍如何本地优雅的开发OPA,感兴趣同学可以先在OPA的playground玩玩。
了解更多:OPA的Rego文档
本文代码详见:NewbMiao/opa-koans
如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com本文链接 : https://newbmiao.github.io/2020/03/13/opa-quick-start.html
本文链接 : 