Open7

Regoの書き方を勉強する

ピン留めされたアイテム
kenfdevkenfdev

主にOPAのSlackから拾ってきたQAのRegoが、Slackとともに履歴から消えていくのがもったいないので勉強がてら残しておく。

kenfdevkenfdev

引用元(Slack):https://openpolicyagent.slack.com/archives/CMHKK5RQF/p1623132617087700?thread_ts=1623117004.085800&cid=CMHKK5RQF

Rego Playground

Terraformで、あるAWSのresourceに必ず foo, bar, baz のタグがついてることを確認する

warn[msg] {
  required_tags := {"foo", "bar", "baz"}
  startswith(resource.type, "aws_")
  resource.change.after.tags_all
  provided_tag_names := {tag | resource.change.after.tags_all[tag]}
  missing_tags := required_tags - provided_tag_names
  count(missing_tags) > 0
	msg := sprintf("Mandatory tag(s) missing on %v (missing tags: %v)", [resource.address, concat(",", missing_tags)])
}
kenfdevkenfdev

Rego Playground

RBACで、RoleにPermissionがマッピングされているとき、 inputrole が渡された場合の、対象となる permission をレスポンスで返す。

groups_to_permissions = {
"admin": ["p1", "p2", "p3", "p4"], 
"sales": ["p1", "p2", "p3", "p4"], 
"shipping": ["p1", "p2"]
}

permissions = {
    "p1": {"action": "read", "resource": "firstname"},
    "p2": {"action": "read", "resource": "lastname"},
    "p3": {"action": "write", "resource": "firstname"},
    "p4": {"action": "write", "resource": "lastname"},
}

# Permissions_for_group...
permissions_for_group[perms] {
    x := groups_to_permissions[input.role][_]
    perms := permissions[x]
}

InputとOutputの例

# Input
# {
#     "role": "shipping"
# }

# Output
# [
#     {
#         "action": "read",
#         "resource": "firstname"
#     },
#     {
#         "action": "read",
#         "resource": "lastname"
#     }
# ]
kenfdevkenfdev

Rego Playground

  1. ユーザーは、自分が owner のリソースにアクセスできる。
  2. group はアクセスできるリソースの type が指定されている。
  3. ユーザーは、自分が所属する group に所属している他ユーザーが owner のリソースの type が、所属している group でアクセス可能であればアクセスできる。

Regoの↓あたりがポイント。

can_access_resource {
    common_groups[group]
    g := group[_]
    objects[input.resource].type == groups[g].access_resources_within_group[_]
}

common_groups[group] {
    owner := objects[input.resource].owner
    client := input.user_id
	
    # owner can access own documents
    owner != client
    
    groups_owner_set := {x | x := user_groups[owner][_]}
    groups_client_set := {x | x := user_groups[client][_]}
    group := groups_owner_set & groups_client_set
}
kenfdevkenfdev

引用元(Slack):https://openpolicyagent.slack.com/archives/C1H19LW4F/p1623967008280200

Rego Playground

あらかじめ使っちゃいけないポートが指定されていて、 input.range で渡されたポート範囲にそのポートが含まれていたら deny にそのポートをレスポンスで返す。

prohibited_ports := {3, 5, 6}

deny[msg] {
    p := prohibited_ports[_]
    my_range[p]  # lookup
    msg := sprintf("prohibited port '%v'", [p])
}

my_range = result_set {
    x := split(input.range, " - ")
    result := numbers.range(to_number(x[0]), to_number(x[1]))
    result_set := {x | x := result[_]}  # convert to set
}
kenfdevkenfdev

Complete Rule Ver.
Multiple Results Ver.

下の2つの例では、Setの差分の抽出方法が違う。

a := [
	{
    	"prop": "a"
    },
    {
    	"prop": "b"
    }
]

set_a = result {
	result := { x | x := a[_].prop }
}
a := [
	{
    	"prop": "a"
    },
    {
    	"prop": "b"
    }
]

set_a[result] {
	result := { x | x := a[_].prop }
}
kenfdevkenfdev

引用元Slack

Rego Playground

ユースケースに疑問があるものの、インプットがnumberだったときだけstringに変換するためのRegoの書き方。

num_to_string(x) = sprintf("%v", [x]) {
	is_number(x)
}