-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[Question] Optimization for IAM-like policies #1688
Open
Assignees
Labels
Description
Hello,
I'm trying to design an AWS IAM like system where we have a set of resources which you can "allow" or "deny" for certain roles and users.
My Initial was this:
Initial Option: Model
[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _, _ # Roles permission g2 = _, _ # Data group g3 = _, _ # Action group [policy_effect] # Allow as soon as there one rule allowing #e = some(where (p.eft == allow)) #Deny as soon as there is one rule denying e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] m = g3(r.act, p.act) && g(r.sub, p.sub, r.dom) && keyMatch5(r.obj, p.obj)
Initial Option: Policy
# sub/perm, resource, action p, perm:instance:list, /instance, READ, allow p, perm:instance:delete, /instance/{name}, DELETE, allow p, perm:instance:describe, /instance/{name}, READ, allow p, perm:instance:create, /instance/{name}, POST, allow p, perm:service:list, /service, READ, allow p, perm:proxy:read, /proxy/{name}/*, READ, allow p, perm:proxy:write, /proxy/{name}/*, WRITE, allow p, perm:proxy:crashes, /proxy/{name}/crashes, READ, allow p, block:proxy:crashes, /proxy/{name}/crashes, READ, deny # Adding wild cards in the group does seem to add a lot of time when users grow # #g, role:instance:admin, perm:instance:*, * #g, role:service:admin, perm:service:*, * #g, role:proxy:admin, perm:proxy:*, * # g, role:instance:admin, perm:instance:list, * g, role:instance:admin, perm:instance:delete, * g, role:instance:admin, perm:instance:describe, * g, role:instance:admin, perm:instance:create, * g, role:service:admin, perm:service:list, * g, role:proxy:admin, perm:proxy:read, * g, role:proxy:admin, perm:proxy:write, * g, role:instance:viewer, perm:instance:list, * g, role:instance:viewer, perm:instance:describe, * g, role:service:viewer, perm:service:list, * g, role:proxy:viewer, perm:proxy:read, * g, role:proxy:viewer, block:proxy:crashes, * g, user:admin, role:instance:admin, * g, user:admin, role:service:admin, * g, user:admin, role:proxy:admin, * g, user:alice, role:instance:admin, orgA g, user:alice, role:service:admin, orgA g, user:alice, role:proxy:admin, orgA g, user:alice, role:instance:viewer, orgB g, user:alice, role:service:viewer, orgB g, user:alice, role:proxy:viewer, orgB g, user:bob, role:instance:viewer, orgB g, user:bob, role:service:viewer, orgB g, user:bob, role:proxy:viewer, orgB g, user:nancy, role:proxy:admin, orgA g3, POST, WRITE g3, PUT, WRITE g3, PATCH, WRITE g3, DELETE, WRITE g3, OPTIONS, READ g3, HEAD, READ g3, GET, READ g3, READ, ANY g3, WRITE, ANY
I find it's not ideal because I would need to duplicate every p to have one for deny and one for allow.
So My second option is this:
Tentative Option; Model
[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, obj, eft, dom [role_definition] g = _, _, _ # Roles permission g2 = _, _, _ # Data group g3 = _, _ # Action group [policy_effect] # Allow as soon as there one rule allowing #e = some(where (p.eft == allow)) #Deny as soon as there is one rule denying e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] m = globMatch(r.dom, p.dom) && g(r.sub, p.sub, r.dom) && g2(r.obj, p.obj, r.act)
Tentative Option; Policy
# p, role:instance:admin, perm:instance:list, allow, * p, role:instance:admin, perm:instance:delete, allow, * p, role:instance:admin, perm:instance:describe, allow, * p, role:instance:admin, perm:instance:create, allow, * p, role:service:admin, perm:service:list, allow, * p, role:proxy:admin, perm:proxy:read, allow, * p, role:proxy:admin, perm:proxy:write, allow, * p, role:instance:viewer, perm:instance:list, allow, * p, role:instance:viewer, perm:instance:describe, allow, * p, role:service:viewer, perm:service:list, allow, * p, role:proxy:viewer, perm:proxy:read, allow, * p, role:proxy:viewer, perm:proxy:crashes, deny, * # sub/perm, resource, action g2, /instance, perm:instance:list, GET g2, /instance/{name}, perm:instance:delete, DELETE g2, /instance/{name}, perm:instance:describe, GET g2, /instance/{name}, perm:instance:create, POST g2, /service, perm:service:list, GET g2, /proxy/{name}/*, perm:proxy:read, GET g2, /proxy/{name}/*, perm:proxy:write, POST g2, /proxy/{name}/crashes, perm:proxy:crashes, GET g, user:admin, role:instance:admin, * g, user:admin, role:service:admin, * g, user:admin, role:proxy:admin, * g, user:alice, role:instance:admin, orgA g, user:alice, role:service:admin, orgA g, user:alice, role:proxy:admin, orgA g, user:alice, role:instance:viewer, orgB g, user:alice, role:service:viewer, orgB g, user:alice, role:proxy:viewer, orgB g, user:bob, role:instance:viewer, orgB g, user:bob, role:service:viewer, orgB g, user:bob, role:proxy:viewer, orgB g, user:nancy, role:proxy:admin, orgA
Both work, but the first one is significantly (at least 8 times) faster for a few thousand checks. Is there a way for me to optimize option 2 or am I better off going with my initial design?
Metadata
Metadata
Assignees
Labels
Type
Fields
Give feedbackNo fields configured for issues without a type.