乐趣区

关于cloud-native:Open-Policy-AgentOPA-入门实践

大家好,我是张晋涛。

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

在具体聊 OPA 之前,咱们先来聊一下为什么须要一个通用策略引擎,以及 OPA 解决了什么问题。

OPA 解决了什么问题

在理论的生产环境中很多场景中都须要策略管制,比方:

  • 须要策略管制用户是否可登陆服务器或者做一些操作;
  • 须要策略管制哪些项目 / 哪些组件可进行部署;
  • 须要策略管制如何拜访数据库;
  • 须要策略管制哪些资源可部署到 Kubernetes 中;

然而对于这些场景或者软件来说,配置它们的策略是须要与该软件进行耦合的,彼此是不对立,不通用的。治理起来也会比拟凌乱,带来了不小的保护老本。

OPA 的呈现能够将各处配置的策略进行对立,极大的 升高了保护老本。以及将策略与对应的软件 / 服务进行解耦,不便进行移植 / 复用。

OPA 的倒退过程

OPA 最后是由 Styra 公司在 2016 年创立并开源的我的项目,目前该公司的次要产品就是提供可视化策略管制及策略执行的可视化 Dashboard 服务的。

OPA 首次进入 CNCF 并成为 sandbox 级别的我的项目是在 2018 年,在 2021 年的 2 月份便曾经从 CNCF 毕业,这个过程相对来说还是比拟快的,由此也能够看出 OPA 是一个比拟沉闷且利用宽泛的我的项目。

OPA 是什么

后面咱们曾经介绍过 Open Policy Agent (OPA) 是一种开源的通用策略引擎,可在整个堆栈中实现对立、上下文感知的策略管制。

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 语句。

图 4,Rego 残缺规定示例

  • 局部规定是生成一组值并将该组调配给变量的 if-then 语句。

图 5,Rego 局部规定示例

  • 逻辑或是要在 Rego 中定义多个具备雷同名称的规定。(查问中将多个表达式连贯在一起时,示意的是逻辑 AND)

图 6,Rego 规定或的齐全规定和局部规定示例图

OPA 的应用

OPA 的应用还是比较简单的,咱们来看一下。

装置 OPA

二进制形式

咱们能够间接从 OPA 的 release 页面下载其二进制进行应用

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

容器

咱们能够应用其官网镜像

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

OPA 交互

opa eval

最简略的命令是 opa eval,当然咱们除了能应用它进行策略的执行外,还能够用来做表达式计算。

图 7,opa eval 的应用帮忙

➜  ~ opa eval "6+6"
{
  "result": [
    {
      "expressions": [
        {
          "value": 12,
          "text": "6+6",
          "location": {
            "row": 1,
            "col": 1
          }
        }
      ]
    }
  ]
}

opa run

opa run 会启动一个交互式 shell(REPL)。咱们能够应用 REPL 来试验策略并构建新的原型。

➜  ~ opa run
OPA 0.35.0 (commit a54537a, built at 2021-12-01T02:11:47Z)

Run 'help' to see a list of commands and check for updates.

> true
true
> ["Hello", "OPA"]
[
  "Hello",
  "OPA"
]
> pi := 3.14
Rule 'pi' defined in package repl. Type 'show' to see rules.
> show
package repl

pi := 3.14
> pi > 1
true

咱们也能够将策略间接加载进去,或者 将 OPA 作为一个服务运行并通过 HTTP 执行查问。默认状况下,OPA 监会监听在 8181 端口。

➜  ~ opa run --server
{"addrs":[":8181"],"diagnostic-addrs":[],"level":"info","msg":"Initializing server.","time":"2021-12-07T01:12:47+08:00"}

关上浏览器也能够看到一个简略的查问窗口

opa 作为 go 的库应用

OPA 能够作为库嵌入到 Go 程序中。将 OPA 嵌入为库的最简略办法是导入 github.com/open-policy-agent/opa/rego 包。通过 rego.New 函数用来创立一个能够筹备或评估的对象, PrepareForEval() 以获取可执行查问。

以下是一个简略的示例:

  • 目录构造
➜  opa tree 
.
├── data
├── go.mod
├── go.sum
├── input.json
├── k8s-label.rego
└── main.go

1 directory, 5 files
  • 策略文件

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

package kubernetes.validating.existence

deny[msg] {
    not input.request.object.metadata.labels.domain
    msg := "Every resource must have a domain label"
}


deny[msg] {
    value := input.request.object.metadata.labels.domain
    not startswith(value, "moelove-")
    msg := sprintf("domain label must start with `moelove-`; found `%v`", [value])
}
  • INPUT 文件,以 Kubernetes 的 AdmissionReview 为例
{
    "kind": "AdmissionReview",
    "request": {
        "kind": {
            "kind": "Pod",
            "version": "v1"
        },
        "object": {
            "metadata": {
                "name": "myapp",
                "labels": {"domain": "opa"}
            },
            "spec": {
                "containers": [
                    {
                        "image": "alpine",
                        "name": "alpine"
                    }
                ]
            }
        }
    }
}
  • main.go 文件
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "os"

    "github.com/open-policy-agent/opa/rego"
)

func main() {ctx := context.Background()

    // Construct a Rego object that can be prepared or evaluated.
    r := rego.New(rego.Query(os.Args[2]),
        rego.Load([]string{os.Args[1]}, nil))

    // Create a prepared query that can be evaluated.
    query, err := r.PrepareForEval(ctx)
    if err != nil {log.Fatal(err)
    }

    // Load the input document from stdin.
    var input interface{}
    dec := json.NewDecoder(os.Stdin)
    dec.UseNumber()
    if err := dec.Decode(&input); err != nil {log.Fatal(err)
    }

    rs, err := query.Eval(ctx, rego.EvalInput(input))
    if err != nil {log.Fatal(err)
    }

    fmt.Println(rs)
}

执行后果如下:

➜  opa go run main.go k8s-label.rego "data" < input.json
[{[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 的场景。敬请期待。


欢送订阅我的文章公众号【MoeLove】

退出移动版