Skip to content

GitLab

  • Menu
Projects Groups Snippets
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • V verify
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 22
    • Issues 22
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Container Registry
    • Infrastructure Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • ran.lu
  • verify
  • Issues
  • #11

Closed
Open
Created Dec 23, 2022 by ran.lu@ran.luMaintainer

分析 iam 使用 opa 做权限检查

以 ai-arts 为例,其它模块应该是类似的。

1. ai-arts 注册 endpoints

func (p *IamService) RegisterEndpoints(req *dto.RegisterEndpointsReq, header http.Header) 

注意,请求体中,有:

type IamPoliciesType struct {
	Module string `json:"module"`
	//SystemAdmin []IamEndPointAction `json:"systemAdmin"`
	OrgAdmin  []IamEndPointAction `json:"orgAdmin"`
	Developer []IamEndPointAction `json:"developer"`
	Public    []IamEndPointAction `json:"public"`
}

note: ai-arts 并不是每一个路由都注册一下,而是收集起来,最后才调用一次。

2. iam 维护策略

	endpoints := v1.Group("/endpoints")
	endpoints.POST("", EndpointsAndPoliciesCreate)

会写入表中: iam.policies 。可能可以多注意下 account 和 statements 字段。

触发 opa 刷新:

	Refresh(*dtoData.TenantId, false, ctx)

(同步或者异步地)走到:

func updateOpa(ctx context.Context, request *policyRefresherMessageRefresh) {

    ...
    pm, err := getPolicyMap(ctx, request.tenantId)
    if err != nil {
	return
    }
    ...
    opa.UpdateStore(ctx, request.tenantId, pm)
    ...
}

getPolicyMap 准备了数据给到 opa, 所有也值得注意(可以看出准备了啥数据)。这个数据会给到 UpdateStore 里面的 pm:

err = opaData.store.Write(ctx, opaData.txn, storage.AddOp, storage.MustParsePath("/policies"), pm)

rego.go 的 opa 规则中,会 import 这个 policies.

3. iam 实施权限检查

func Authz(tenantId int64, action, subject string) 
        ...
	ctx := context.Background()
	r := opaData.partialResult.Rego(
		rego.Input(input),
	)

	rs, err := r.Eval(ctx)
	if err != nil {
		logging.Error(err).Int64("tenant", tenantId).Str("action", action).
			Str("subject", subject).Msgf("opa authz failed")
		return
	}

	result, err = getResultProjects(rs)
        ...

3.1 输入端

用户这边,提供了哪些数据:

        input["action"] = action
	input["subject"] = constants.UserGroupSeparator + subject + constants.UserGroupSeparator
	input["resource"] = "*"
	input["projects"] = []string{"~~ALL-PROJECTS~~"}

这里的 subject ,可以看出,是 user group id.

3.2 规则端

一方面,前面提到, pm 提供了 policies 数据,这其实是一个字典, key 为 policyId, value 是一个处理后的值。如果我们去看func getPolicyMap(ctx context.Context, tenantId int64) ,就会发现, value 是一个多层字典,大概是这样的:

{
			  "members": ";;1;;2;;",
			  "statements": [
			    {
				   "id": 1,
				   "role": "",
				   "actions": ["a:b:c"],
				   "resources": "*",
				   "effect": "allow",
				   "projects": "*",
				}
			  ]
}

另一方面, rego.go 中其实有许多规则,这里的 data.authz.hasProject ,似乎可以让我们知道,应该从哪个规则看起。

        opaData.r = rego.New(
		rego.Query("data.authz.hasProject"),
		rego.Module("example.rego", OPA_MODULE),
		rego.Store(opaData.store),
		rego.Transaction(opaData.txn),
	)

以 hasProject 为起点去看:


hasProject = true {
   count(authorized_project) > 0
}

authorized_project[statement_id] {
	allowed_project[statement_id]
	# not denied_project[project]
}

allowed_project[statement_id] {
	match[["allow", pol_id, statement_id]]
}

match[[effect, pol_id, statement_id]] {
	effect := policies[pol_id].statements[statement_id].effect
	has_member[pol_id]
#	has_resource[[pol_id, statement_id]]
	has_action[[pol_id, statement_id]]
}

has_member[pol_id] {
	pol_sub := policies[pol_id].members
	contains(pol_sub, input.subject)
}

has_action[[pol_id, statement_id]] {
	statement_action := policies[pol_id].statements[statement_id].actions[_]
	action_matches(input.action, statement_action)
}

action_matches(in, stored) {
	# no_wildcard(stored)
	in == stored
}

结合 输入 和 规则,就可以作权限检查了。

参考

  • opa官网
  • opa playground
Edited Dec 23, 2022 by ran.lu
Assignee
Assign to
Time tracking