OPA-重新定义规则引擎-入门篇

2020-03-15 20:52:55 +08:00
 newmiao

OPA,全称OpenPolicyAgent, 底层用Go实现,它灵活而强大的声明式语言全面支持通用策略定义。

目前国内资料还比较少。

个人因为工作接触比较多,打算陆续分享些教程介绍下。

私以为规则引擎的技术选型完全可以多这个选择~~

什么是 OPA

具体看官方文档 OPA philosophy docs

主要关键词是:

文字图片还是不够生动,看看 OPA 作者怎么说:

OPA: The Cloud Native Policy Engine - Torin Sandall, Styra

备用国内 B 站地址

优点

配合强大的声明式策略语言Rego,描述任意规则都不是问题

除了集成做Auth外,还可以应用到k8s,terraform,docker,kafka,sql,linux等上做规则决策

下面从一个 RBAC 鉴权例子来了解下OPA

一个 RBAC 例子

以下 json 配置了 role 能操作的资源和 user 的绑定关系

// data.json
{
    "roles": [
        {
            "operation": "read",
            "resource": "widgets",
            "name": "widget-reader"
        },
        {
            "operation": "write",
            "resource": "widgets",
            "name": "widget-writer"
        }
    ],
    "bindings": [
        {
            "user": "inspector-alice",
            "role": "widget-reader"
        },
        {
            "user": "maker-bob",
            "role": "widget-writer"
        }
    ]
}

当一个请求读取 widgets 的 user (如下 json )过来操作资源,怎么判定他是否可以呢?

// input.json
{
    "action":{
        "operation":"read",
        "resource":"widgets"
    },
    "subject":{
        "user":"inspector-alice"
    }
}

可能你习惯性在想用自己趁手的语言和框架,一顿遍历循环搞定。

且慢,OPA告诉我们:

几行代码就可以!(当然代码少不是重点。。。)

这里是可以在线运行的代码示例

我们先抛开语法,代码其实就是描述了一条规则:

用户是否有角色,角色是否有权限操作的资源

下面我们开始学习OPA如何定义这条规则

基本语法

OPA基于一种数据查询语言Datalog实现了描述语言Rego

OPARego基本语法如下表:

| 语法 | 例子 | | ---- | ---- | | 上下文| 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

当定义规则时:

具体到代码中规则allow, 默认值是 false

要求user_has_rolerole_has_permission同时满足

两者的role_name也是一样。

你可能发现,局部变量role_name 没声明啊!

Rego里可以省略声明局部变量, 直接使用。

Tips: 但要这样的变量可以被同名的全局变量修改。 局部变量必要时还是应该使用some声明 如 some role_name

default allow = false

# allow will be true when user has role and role has permission
allow {
  user_has_role[role_name]
  role_has_permission[role_name]
}

然后其中user_has_role[role_name]这种带参数的结构不是规则,叫虚拟文档(文档:可被查询的集合)

# check user role binding exist
user_has_role[role_name] {
  role_binding = data.bindings[_]
  role_binding.role = role_name
  role_binding.user = input.subject.user
}

Tips: 仔细同学会发现,线上运行版有withrole_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是一个数据查询语言嘛)

这里举个例子就好理解了:

[x, "world"] = ["hello", y]
# 之后,x 值为 hello,y 为 world

总结一下,本文介绍什么是OPA,并借一个简单的 RBAC 例子初探了Rego强大的声明规则语法。

下一篇,将会介绍如何本地优雅的开发OPA,感兴趣同学可以先在OPA的 playground 玩玩。

了解更多:OPA 的 Rego 文档 本文代码详见:NewbMiao/opa-koans


文章首发公众号:newbmiao

推荐阅读:OPA 系列

3367 次点击
所在节点    Go 编程语言
3 条回复
simon8410
2020-03-18 15:35:32 +08:00
写得不错,赞一个
newmiao
2020-03-20 21:39:38 +08:00
@simon8410 那是 OPA 真香,用起来很方便
newmiao
2020-03-21 11:03:03 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/653079

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX