Hi everyone, this is Zhang Jintao.
In this article, I will introduce to you a general policy engine that I personally like very much, called OPA, and its full name is Open Policy Agent.
Before talking about OPA in detail, let's talk about why we need a general policy engine and what problems OPA solves.
What problem does OPA solve
In the actual production environment, policy control is required in many scenarios, such as:
- Need policies to control whether users can log in to the server or do some operations;
- Need strategies to control which projects/components can be deployed;
- Need strategies to control how to access the database;
- Need policies to control which resources can be deployed in Kubernetes;
But for these scenarios or software, the strategy for configuring them needs to be coupled with the software, and they are not unified and not universal. The management will also be messy, which brings a lot of maintenance costs.
OPA's appearance can be configured for unified policy throughout, great reduces maintenance costs . And decoupling the strategy from the corresponding software/service to facilitate transplantation/reuse.
The development process of OPA
OPA was originally an open source project created by Styra in 2016. At present, the company's main product is to provide a visual dashboard service for visual strategy control and strategy execution.
OPA entered CNCF for the first time and became a sandbox-level project in 2018. It has already graduated from CNCF in February 2021. This process is relatively fast. It can also be seen that OPA is a relatively active and application. A wide range of projects.
What is OPA
We have already introduced that Open Policy Agent (OPA) is an open source general policy engine that can implement unified, context-aware policy control in the entire stack.
OPA can separate (decouple) policy decisions from the business logic of the application. looks at the essence through phenomena. A policy is a set of rules. Requests are sent to the engine, and the engine makes decisions based on the rules.
Figure 3, OPA's strategy decoupling example
OPA is not responsible for the execution of specific tasks. It is only responsible for decision-making . Requests that require decision-making are passed to OPA through JSON. After OPA makes a decision, the result will also be returned in JSON.
Rego
The strategy in OPA is expressed in a DSL (Domain Specific Language) such as Rego.
Rego is inspired by Datalog ( https://en.wikipedia.org/wiki/Datalog) , and extends Datalog's support for structured document models to facilitate data processing in JSON.
Rego allows policymakers to focus on the query that returns content rather than how to execute the query. At the same time, OPA also has built-in optimization when executing rules, and users can use it by default.
Rego allows us to encapsulate and reuse logic using rules (if-then), which can be complete or partial.
Each rule is composed of a head and a body. In Rego, if the body of the rule assigns a value to some variables as true, then we say that the rule header is true. Any rule loaded into OPA can be referenced by absolute path to query its value. The path of the rule is always: data.<package-path>.<rule-name> (All values generated by the rule can be queried through the global data variable. For example, data.example.rules.any_public_networks
- The complete rule is an if-then statement that assigns a single value to a variable.
Figure 4, Rego complete rule example
- Part of the rule is an if-then statement that generates a set of values and assigns that set to a variable.
Figure 5, some examples of Rego rules
- Logic or to define multiple rules with the same name in Rego. (When multiple expressions are connected together in the query, it means logical AND)
Figure 6, Example diagram of complete rules and partial rules of Rego rules or
Use of OPA
The use of OPA is relatively simple, let's take a look.
Install OPA
Binary way
We can download its binary directly from OPA's release page for use
➜ ~ 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
container
We can use its official mirror
➜ ~ 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 interaction
opa eval
The simplest command is opa eval
. Of course, we can use it for strategy execution and expression calculations.
Figure 7, the use of opa eval help
➜ ~ opa eval "6+6"
{
"result": [
{
"expressions": [
{
"value": 12,
"text": "6+6",
"location": {
"row": 1,
"col": 1
}
}
]
}
]
}
opa run
opa run
will start an interactive shell (REPL). We can use the REPL to experiment with strategies and build new prototypes.
➜ ~ 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
We can also load the policy directly, or run OPA as a service and execute queries via HTTP. By default, the OPA supervisory committee monitors on port 8181.
➜ ~ opa run --server
{"addrs":[":8181"],"diagnostic-addrs":[],"level":"info","msg":"Initializing server.","time":"2021-12-07T01:12:47+08:00"}
You can also see a simple query window when you open the browser
opa is used as a library for go
OPA can be embedded in Go programs as a library. The easiest way to embed OPA as a library is to import the github.com/open-policy-agent/opa/rego
package. The rego.New
function is used to create an object that can be prepared or evaluated, and PrepareForEval()
to obtain an executable query.
The following is a simple example:
- Directory Structure
➜ opa tree
.
├── data
├── go.mod
├── go.sum
├── input.json
├── k8s-label.rego
└── main.go
1 directory, 5 files
- Policy file
The policy file here is to verify whether the label named domain is included in INPUT, and whether the label starts 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 file, take Kubernetes' AdmissionReview as an example
{
"kind": "AdmissionReview",
"request": {
"kind": {
"kind": "Pod",
"version": "v1"
},
"object": {
"metadata": {
"name": "myapp",
"labels": {
"domain": "opa"
}
},
"spec": {
"containers": [
{
"image": "alpine",
"name": "alpine"
}
]
}
}
}
}
- main.go file
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)
}
The execution results are as follows:
➜ 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[]}]
Summarize
The above is a general introduction to OPA. There are many application scenarios of OPA. In the follow-up articles, I will share the application of OPA in Kubernetes and the scenarios of OPA application and CI/CD pipeline. Stay tuned.
Welcome to subscribe to my article public account【MoeLove】
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。