欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Open Policy Agent(OPA) 入门实践

程序员文章站 2022-04-08 19:30:17
...

本篇我来为你介绍一个我个人很喜欢的,通用策略引擎,名叫 OPA,全称是 Open Policy Agent。

在具体聊 OPA 之前,我们先来聊一下为什么需要一个通用策略引擎,以及 OPA 解决了什么问题。
OPA 解决了什么问题
在实际的生产环境中很多场景中都需要策略控制,比如:

需要策略控制用户是否可登陆服务器或者做一些操作;
需要策略控制哪些项目/哪些组件可进行部署;
需要策略控制如何访问数据库;
需要策略控制哪些资源可部署到 Kubernetes 中;
Open Policy Agent(OPA) 入门实践
但是对于这些场景或者软件来说,配置它们的策略是需要与该软件进行耦合的,彼此是不统一,不通用的。管理起来也会比较混乱,带来了不小的维护成本。

OPA 的出现可以将各处配置的策略进行统一,极大的降低了维护成本。以及将策略与对应的软件/服务进行解耦,方便进行移植/复用。

Open Policy Agent(OPA) 入门实践
OPA 的发展过程
前面我们已经介绍过 Open Policy Agent (OPA) 是一种开源的通用策略引擎,可在整个堆栈中实现统一、上下文感知的策略控制。

OPA 可将策略决策与应用程序的业务逻辑分离(解耦),透过现象看本质,策略就是一组规则,请求发送到引擎,引擎根据规则来进行决策。
Open Policy Agent(OPA) 入门实践
图 3 ,OPA 的策略解耦示例
OPA 并不负责具体任务的执行,它仅负责决策,需要决策的请求通过 JSON 的方式传递给 OPA ,在 OPA 决策后,也会将结果以 JSON 的形式返回。

Rego
OPA 中的策略是以 Rego 这种 DSL(Domain Specific Language) 来表示的。

Rego 受 Datalog(https://en.wikipedia.org/wiki...) 的启发,并且扩展了 Datalog 对于结构化文档模型的支持,方便以 JSON 的方式对数据进行处理。

Rego 允许策略制定者可以专注于返回内容的查询而不是如何执行查询。同时 OPA 中也内置了执行规则时的优化,用户可以默认使用。

Rego 允许我们使用规则(if-then)封装和重用逻辑,规则可以是完整的或者是部分的。

每个规则都是由头部和主体组成。在 Rego 中,如果规则主体对于某些变量赋值为真,那么我们说规则头为真。可以通过绝对路径引用任何加载到 OPA 中的规则来查询它的值。规则的路径总是: data.<package-path>.<rule-name> (规则生成的所有值都可以通过全局 data 变量进行查询。 例如,下方示例中的 data.example.rules.any_public_networks
完整规则是将单个值分配给变量的 if-then 语句。
Open Policy Agent(OPA) 入门实践

图 4 ,Rego 完整规则示例
部分规则是生成一组值并将该组分配给变量的 if-then 语句。
Open Policy Agent(OPA) 入门实践
图 5 ,Rego 部分规则示例
逻辑或是要在 Rego 中定义多个具有相同名称的规则。(查询中将多个表达式连接在一起时,表示的是逻辑 AND)
Open Policy Agent(OPA) 入门实践
图 6 ,Rego 规则或的完全规则和部分规则示例图

OPA 的使用
OPA 的使用还是比较简单的,我们来看一下。
安装 OPA
二进制方式
我们可以直接从 OPA 的 release 页面下载其二进制进行使用

  1. ~ wget -q -O ~/bin/opa https://github.com/open-policy-agent/opa/releases/download/v0.35.0/opa_linux_amd64_static
  2. ~ chmod +x ~/bin/opa
  3. ~ opa version
  4. Version: 0.35.0
  5. Build Commit: a54537a
  6. Build Timestamp: 2021-12-01T02:11:47Z
  7. Build Hostname: 9e4cf671a460
  8. Go Version: go1.17.3
  9. WebAssembly: unavailable

容器
我们可以使用其官方镜像

  1. ~ docker run --rm openpolicyagent/opa:0.35.0 version
  2. Version: 0.35.0
  3. Build Commit: a54537a
  4. Build Timestamp: 2021-12-01T02:10:31Z
  5. Build Hostname: 4ee9b086e5de
  6. Go Version: go1.17.3
  7. WebAssembly: available

OPA 交互
opa eval
最简单的命令是 opa eval ,当然我们除了能使用它进行策略的执行外,还可以用来做表达式计算。
Open Policy Agent(OPA) 入门实践
图 7 , opa eval 的使用帮助

  1. ~ opa eval "6+6"
  2. {
  3. "result": [
  4. {
  5. "expressions": [
  6. {
  7. "value": 12,
  8. "text": "6+6",
  9. "location": {
  10. "row": 1,
  11. "col": 1
  12. }
  13. }
  14. ]
  15. }
  16. ]
  17. }

opa run
opa run 会启动一个交互式 shell ( REPL) 。我们可以使用 REPL 来试验策略并构建新的原型。

  1. ~ opa run
  2. OPA 0.35.0 (commit a54537a, built at 2021-12-01T02:11:47Z)
  3. Run 'help' to see a list of commands and check for updates.
  4. > true
  5. true
  6. > ["Hello", "OPA"]
  7. [
  8. "Hello",
  9. "OPA"
  10. ]
  11. > pi := 3.14
  12. Rule 'pi' defined in package repl. Type 'show' to see rules.
  13. > show
  14. package repl
  15. pi := 3.14
  16. > pi > 1
  17. true

我们也可以将策略直接加载进去,或者 将 OPA 作为一个服务运行并通过 HTTP 执行查询。默认情况下,OPA 监会监听在 8181 端口。```java
➜ ~ opa run —server
{“addrs”:[“:8181”],”diagnostic-addrs”:[],”level”:”info”,”msg”:”Initializing server.”,”time”:”2021-12-07T01:12:47+08:00”}

  1. 打开浏览器也可以看到一个简单的查询窗口
  2. ![](https://img.php.cn/upload/image/790/758/944/1642228382418666.png)
  3. opa 作为 go 的库使用
  4. OPA 可以作为库嵌入到 Go 程序中。将 OPA 嵌入为库的最简单方法是导入 github.com/open-policy-agent/opa/rego 包。通过 rego.New 函数用来创建一个可以准备或评估的对象, PrepareForEval() 以获取可执行查询。
  5. 以下是一个简单的示例:
  6. 目录结构
  7. ```java
  8. ➜ opa tree
  9. .
  10. ├── data
  11. ├── go.mod
  12. ├── go.sum
  13. ├── input.json
  14. ├── k8s-label.rego
  15. └── main.go
  16. 1 directory, 5 files

策略文件

这里的策略文件是来校验 INPUT 中是否包含名为 domain 的 label , 以及该 label 是否以 moelove-开头。

  1. package kubernetes.validating.existence
  2. deny[msg] {
  3. not input.request.object.metadata.labels.domain
  4. msg := "Every resource must have a domain label"
  5. }
  6. deny[msg] {
  7. value := input.request.object.metadata.labels.domain
  8. not startswith(value, "moelove-")
  9. msg := sprintf("domain label must start with `moelove-`; found `%v`", [value])
  10. }

INPUT 文件, 以 Kubernetes 的 AdmissionReview 为例

  1. {
  2. "kind": "AdmissionReview",
  3. "request": {
  4. "kind": {
  5. "kind": "Pod",
  6. "version": "v1"
  7. },
  8. "object": {
  9. "metadata": {
  10. "name": "myapp",
  11. "labels": {
  12. "domain": "opa"
  13. }
  14. },
  15. "spec": {
  16. "containers": [
  17. {
  18. "image": "alpine",
  19. "name": "alpine"
  20. }
  21. ]
  22. }
  23. }
  24. }
  25. }

main.go 文件

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "log"
  7. "os"
  8. "github.com/open-policy-agent/opa/rego"
  9. )
  10. func main() {
  11. ctx := context.Background()
  12. // Construct a Rego object that can be prepared or evaluated.
  13. r := rego.New(
  14. rego.Query(os.Args[2]),
  15. rego.Load([]string{os.Args[1]}, nil))
  16. // Create a prepared query that can be evaluated.
  17. query, err := r.PrepareForEval(ctx)
  18. if err != nil {
  19. log.Fatal(err)
  20. }
  21. // Load the input document from stdin.
  22. var input interface{}
  23. dec := json.NewDecoder(os.Stdin)
  24. dec.UseNumber()
  25. if err := dec.Decode(&input); err != nil {
  26. log.Fatal(err)
  27. }
  28. rs, err := query.Eval(ctx, rego.EvalInput(input))
  29. if err != nil {
  30. log.Fatal(err)
  31. }
  32. fmt.Println(rs)
  33. }

执行结果如下:

  1. opa go run main.go k8s-label.rego "data" < input.json
  2. [{[map[kubernetes:map[validating:map[existence:map[deny:[domain label must start with `moelove-`; found `opa`]]]]]] map[]}]

总结
以上便是对于 OPA 的一个大致介绍,希望对大家有所帮助和学习知识,,OPA 的应用场景有很多,后续文章中将为大家分享 OPA 在 Kubernetes 中的应用,和将 OPA 应用与 CI/CD pipeline 的场景。敬请期待。