-
Notifications
You must be signed in to change notification settings - Fork 930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ReBAC authorization with OpenFGA #11905
Conversation
939ccc4
to
f03eda4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I've left a few comments on things to double check.
One thing I've noticed is that a few handlers have a userHasPermission(fmt.Sprintf("%s_%s", projectName, resourceName))
. I think these might be better as first class functions in the openfga package since the handler shouldn't necessarily know about the naming conventions for FGA resource/relation names.
lxd/cluster/config/config.go
Outdated
@@ -268,6 +268,11 @@ func (c *Config) ClusterHealingThreshold() time.Duration { | |||
return healingThreshold | |||
} | |||
|
|||
// OpenFGA returns all OpenFGA settings need to interact with an OpenFGA server. | |||
func (c *Config) OpenFGA() (string, string, string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not usually a fan of named returns but here it might be useful to know the order of the returns since they are all of the same type.
47d4734
to
6f1ba9f
Compare
@roosterfish @MusicDin hey if you chaps have chance please can you take a look at this before I do a review. Thanks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks good, I just have a question about how we want to structure access handling. We already have access handlers like e.g. allowProjectPermission("containers", "view")
which check RBAC permissions, so I wonder if we can perform the ReBAC
check there instead of adding the ReBACAccessHandler
to endpoint actions.
lxd/daemon.go
Outdated
return resp | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean if a ReBACAccessHandler
is defined for the endpoint action it is always called, regardless of whether or not we have an openfga client configured?
Also, if ReBAC is configured, do we still need to call the standard AccessHandler
?
I wonder if a better pattern would be to remove the ReBACAccessHandler
and perform the check inside the AccessHandler
instead if the client is set. We could have utils for building access handlers e.g. access.NewHandler().Authenticated().AllowedReBACRelations("admin", "images_view").AllowedCandidRoles("admin", "images_view")
or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@monstermunchkin I too am a bit confused by the ramifications of this and am thinking the same as @markylaing above. It seems odd to have 2 access handlers defined for a single API route. Can we not check ReBAC inside the currently access handler?
The problem is that RBAC isn't as fine-grained as ReBAC. For example, ReBAC has separate handlers for networks, network ACLs, and network zones, whereas RBAC only does networks which includes ACLs and zones. If we changed that in the current access handler, it would break things as RBAC doesn't know of network ACLs. Also, for ReBAC, some permission handling is done inside of the actual handler. For example when listing instances, a user should only see the instances which they are allowed to view. RBAC would just show all instances. So, for simplicity and backward compatibility I believe it's best to add |
I'm not suggesting we mangle permissions to make RBAC and ReBAC equivalent, I'm suggesting that we have a configurable
Yeah I guess in practice my suggestion will be more difficult to implement and there is more risk of breaking existing access control, so I'm happy to keep the ReBACAccessHandler 👍 |
@monstermunchkin before I review this more thoroughly, it looks like there is no docs update for this PR? |
https://documentation.ubuntu.com/lxd/en/latest/authentication/ will need some updates in the "OpenID Connect authentication" section and a new section similar to the RBAC section, describing what you can do and what roles there are. |
17c9255
to
b376e2a
Compare
lxd/openfga/client.go
Outdated
func (c *Client) HasPermission(user string, relation Relation, object string) bool { | ||
fullUser := UserObject(user) | ||
|
||
logger.Debug("Checking ReBAC permission", logger.Ctx{"user": fullUser, "relation": relation, "object": object}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you create a new logger here using l := logger.AddContext( logger.Ctx{"user": fullUser, "relation": relation, "object": object})
, then you can use l.Debug()
and include all the context each time, including on line 86.
lxd/openfga/client.go
Outdated
|
||
data, _, err := c.apiClient.OpenFgaApi.Check(context.Background()).Body(body).Execute() | ||
if err != nil { | ||
logger.Debug("Failed checking permissions", logger.Ctx{"err": err}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use l.Debug here
lxd/openfga/client.go
Outdated
fullUser := fmt.Sprintf("user:%s", user) | ||
body := openfga.NewListObjectsRequest(string(objectType), string(relation), fullUser) | ||
|
||
objectResponse, _, err := c.apiClient.OpenFgaApi.ListObjects(context.Background()).Body(*body).Execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For new code we should be accepting context arguments not using context.Background.
lxd/openfga/client.go
Outdated
return objects, nil | ||
} | ||
|
||
func (c *Client) AddTuple(user string, relation Relation, object string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing doc block saying what it does.
lxd/openfga/client.go
Outdated
}, | ||
AuthorizationModelId: openfga.PtrString(c.authModelID)} | ||
|
||
_, _, err := c.apiClient.OpenFgaApi.Write(context.Background()).Body(body).Execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to accept a context argument.
lxd/openfga/client.go
Outdated
}, | ||
AuthorizationModelId: openfga.PtrString(c.authModelID)} | ||
|
||
_, _, err := c.apiClient.OpenFgaApi.Write(context.Background()).Body(body).Execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function need to accept a context argument.
lxd/openfga/client.go
Outdated
} | ||
|
||
// ListObjects returns the a list of objects the user is related to. It also returns whether the user is an admin. | ||
func (c *Client) ListObjects(user string, relation Relation, objectType ObjectType) ([]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also returns whether the user is an admin.
How does it do that?
lxd/openfga/client.go
Outdated
return nil | ||
} | ||
|
||
func (c *Client) DeleteTuple(user string, relation Relation, object string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing doc block
lxd/openfga/model.go
Outdated
@@ -0,0 +1,5 @@ | |||
package openfga | |||
|
|||
// Code generated by Makefile; DO NOT EDIT. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we change this to "Code generated by make update-openfga` so its clear what needs to be run to update it?
lxd/openfga/model.openfga
Outdated
@@ -0,0 +1,61 @@ | |||
model |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this file have comments explaining what it is and what it defines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in the future if we add new entity types to LXD, will this file need to be updated?
What are the ramifications if it isn't does the new resource become inaccessible to these users or does it "fail open"?
lxd/openfga/objects.go
Outdated
"fmt" | ||
) | ||
|
||
func GroupObject(groupName string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please can we change these functions to start with Object
so they are grouped alphabetically by what type of info they return?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like you've done with the Relation constants.
This can be rebased now please |
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
This adds the update-openfga target which generates a go source file containing the JSON representation of the OpenFGA authorization model. Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
b376e2a
to
a991356
Compare
@monstermunchkin @markylaing shall we close this in preference to #12252 ? |
https://discuss.linuxcontainers.org/t/lxd-rebac-authorization-using-openfga/17094