Open Policy Agent(OPA) rego使用

赫连卓
2023-12-01

下载opa

文档地址https://www.openpolicyagent.org/docs/latest/#1-download-opa

curl -L -o opa https://openpolicyagent.org/downloads/v0.33.1/opa_linux_amd64_static
chmod a+x opa
mv opa /usr/local/bin

非交互式运行

package r1   #必须有包名

pi = 3.14

运行:

opa eval -d r1.rego "data.r1.pi"    
// -d为代码文件,data字段固定,r1为package名,pi为变量名,该行为获取pi的值

如果需要用到input文件:


opa eval -i input.json -d r1.rego "data.r1.xx"

交互式运行

input.json

{
  "servers": [
    {"id": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
    {"id": "db", "protocols": ["mysql"], "ports": ["p3"]},
    {"id": "cache", "protocols": ["memcache"], "ports": ["p3"]},
    {"id": "ci", "protocols": ["http"], "ports": ["p1", "p2"]},
    {"id": "busybox", "protocols": ["telnet"], "ports": ["p1"]}
  ],
  "networks": [
    {"id": "net1", "public": false},
    {"id": "net2", "public": false},
    {"id": "net3", "public": true},
    {"id": "net4", "public": true}
  ],
  "ports": [
    {"id": "p1", "network": "net1"},
    {"id": "p2", "network": "net3"},
    {"id": "p3", "network": "net2"}
  ]
}
opa run input.json

>data.servers[0].id    // 用 []加索引 取列表,用 . 取key
"app"


或者不使用input,直接

opa run

rego语言使用

input.json如下

{
  "servers": [
    {
      "id": "app",
      "ports": [
        "p1",
        "p2",
        "p3"
      ],
      "protocols": [
        "https",
        "ssh"
      ]
    },
    {
      "id": "db",
      "ports": [
        "p3"
      ],
      "protocols": [
        "mysql"
      ]
    },
    {
      "id": "cache",
      "ports": [
        "p3"
      ],
      "protocols": [
        "memcache"
      ]
    },
    {
      "id": "ci",
      "ports": [
        "p1",
        "p2"
      ],
      "protocols": [
        "http"
      ]
    },
    {
      "id": "busybox",
      "ports": [
        "p1"
      ],
      "protocols": [
        "telnet"
      ]
    }
  ]
}

进入交互式界面

opa run input.json

取值操作

OPA 0.33.1 (commit 64359ae, built at 2021-10-04T11:26:46Z)

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

> data.servers
[
  {
    "id": "app",
    "ports": [
      "p1",
      "p2",
      "p3"
    ],
    "protocols": [
      "https",
      "ssh"
    ]
  },
  {
    "id": "db",
    "ports": [
      "p3"
    ],
    "protocols": [
      "mysql"
    ]
  },
  {
    "id": "cache",
    "ports": [
      "p3"
    ],
    "protocols": [
      "memcache"
    ]
  },
  {
    "id": "ci",
    "ports": [
      "p1",
      "p2"
    ],
    "protocols": [
      "http"
    ]
  },
  {
    "id": "busybox",
    "ports": [
      "p1"
    ],
    "protocols": [
      "telnet"
    ]
  }
]
> 
> 
> data.servers[0]
{
  "id": "app",
  "ports": [
    "p1",
    "p2",
    "p3"
  ],
  "protocols": [
    "https",
    "ssh"
  ]
}
> 
> 
> data.servers[0].ports
[
  "p1",
  "p2",
  "p3"
]
> 
> data.servers[0].ports[0]
"p1"
> 
> 

 比较操作

> data.servers[0].ports[0] == "p2"
false
> 
> data.servers[0].ports[0] == "p1"
true
> 
> data.servers[0].ports[0] > "p0"
true
> 
> data.servers[0].ports[0] > "p2"
false
> 
> 
> data.servers[0].ports[0] != "p2"
true
> 
> data.servers[0].ports[0] >= "p1"
true
>
> count(data.servers[0].protocols) >= 1
true
>

赋值操作

> p1 = 1
Rule 'p1' defined in package repl. Type 'show' to see rules.
> 
> p2 {p1=1}    # 意思是p2==true如果p1等于1,否则为undefined,不会有false,
# 如果使用opa eval -d r1.rego "data.r1.p2"去取值的话,如果不满足条件返回{}
Rule 'p2' defined in package repl. Type 'show' to see rules.
> 
> p1
1
> 
> p2
true
> 
> p3 {x=1; y=2; x+y=3}
Rule 'p3' defined in package repl. Type 'show' to see rules.
> 
> p3
true
>
> p4 {x=1; y=2; x+y=4}   # 如果条件不成立p4结果为undefined
Rule 'p4' defined in package repl. Type 'show' to see rules.
> 
> p4
undefined
 

注意1:顺序无所谓

s {
    x == y + 1
    y = 41
    x = 42
}

# 结果还是为true

注意2:= 与 :=的区别

z {
    y := 41
    y := 42
    43 > y
}

# 会报错 var y assigned above,y已经被定义过了,不能重复定义,
# 演示的时候为了重复使用,没有使用:=赋值,推荐使用:=赋值

注意3:不可使用未声明的变量

z {
    abc == 2
}

# 会报错var abc is unsafe

遍历

/*
[{"msg": i, "index": j}]表示返回值的格式,表示包含多个{"msg": i, "index": j}的集合

z6 = [{"a":1}, {"a":1}, {"b":2}]

z7 [{"msg": v}]{
    v = z6[_]   # _的含义再后面说明
}
此时z7的结果中只有[{"a":1},{"b":2}]


z8 [{"msg": v, "index": i}]{
    v = z6[i]
}
*/


z5 [{"msg": i, "index": j}]{    
    i = input.servers[0].ports[j] == "p1"   # 使用了input文件,则数据从input.xxxx取
}

# opa eval -i input.json -d r1.rego "data.r1.z5"
结果为

{
  "result": [
    {
      "expressions": [
        {
          "value": [
            {
              "index": 0,
              "msg": true
            },
            {
              "index": 1,
              "msg": false
            },
            {
              "index": 2,
              "msg": false
            }
          ],
          "text": "data.r1.z5",
          "location": {
            "row": 1,
            "col": 1
          }
        }
      ]
    }
  ]
}

 多维数组遍历

z5 [{"msg": msg, "index":[i, j]}]{
    msg = input.servers[i].ports[j] == "p1"
}

或者不关注索引
z5 [{"msg": msg}]{
    msg = input.servers[_].ports[_] == "p1"
}

 _ 的含义

Rules

满足全部条件的rule

r1 {
    n1 = 1
    n2 = 3
    n1 + n2 = 4
}

# {}中的条件全部满足,则返回true
# 交互式命令行满足条件返回true,不满足返回undefined
# 非交互式满足条件返回true,不满足返回{}

满足部分条件的rule

r2_data = [{"a":true, "id":1}, {"a": false, "id":2}]

r2 [r2_rlt.id]{
    r2_rlt = r2_data[_]  # 遍历r2_data,忽略索引
    r2_rlt.a  # 如果列表中有元素的a字段为true,则满足条件,不需要全部满足,存在即可
}

violation(不一定非要用这个名称,只是violation即表示违反的意思)

input.json:

{
  "servers": [
    {
      "id": "busybox",
      "protocols": ["http", "telnet"]
    },
    {
      "id": "db",
      "protocols": ["mysql", "ssh"]
    },
    {
      "id": "web",
      "protocols": ["https"]
    }
  ]
}

想要达到的效果是协议包含https和ssh的server不能被暴露,如果servers中存在这样的主机,则不被允许 

allow {
    count(violation) == 0
}

violation[server.id] {
    server = input.servers[_]
    server.protocols[_] == "https"
}

violation[server.id] {
    server = input.servers[_]
    server.protocols[_] == "ssh"
}
opa eval -i input.json -d r1.rego "data.r1.allow"

列表操作

列表生成式

sites = [
    {"region": "west", "name": "name1"},
    {"region": "west", "name": "name2"},
    {"region": "not-west", "name": "name3"}
]

region := "west"
names = [name | some i; sites[i].region == region; name := sites[i].name]
# 可以不用声明变量i,直接使用,其他字符也可,比如,b,c,d,e,f
names2 = [name | sites[i].region == region; name := sites[i].name]
# 不可以使用_,_表示存在region="west"的即满足
names2 = [name | sites[_].region == region; name := sites[_].name]


# 如同python中
names = [site.name for site in sites if site.region == "west"]

集合操作

集合生成式、字典操作

ipt = {"metadata": {"name": "ns-test", "labels": {"a": "b", "gatekeeper": "abc", "b": "c"}}}

# {}表示集合,此行表示获取labels所有的key,而不是value,会获取到{"a", "gatekeeper", "b"}
provided := {label | ipt.metadata.labels[label]}  


# 获取所有val, 用key去获取即可
provided2 := {ipt.metadata.labels[label] | ipt.metadata.labels[label]}


# 集合可以做减操作,c == {"c"},不可做加操作
a = {"a", "b", "c"}
b = {"a", "b"}
c = a - b


# 验证字典中是否有某个key
viok[{"msg": msg}]{
    kv := {"a": 1, "b": 2}
    k := "a"
    not kv[k]
    # v := object.get(kv, k, "abc")  如果不存在取默认值abc
    # v == "abc"
    msg = sprintf("msg: not k %v", [k])
}
# 内置函数https://www.openpolicyagent.org/docs/latest/policy-reference/#objects-2

函数

trim_and_split(s) = x {
     t := trim(s, " ")
     x := split(t, ".")
}

trim_and_split("   foo.bar ")

[
  "foo",
  "bar"
]

Rego在线编辑器The Rego Playground

 类似资料: