Project | LXD |
Status | Draft |
Author(s) | @monstermunchkin |
Approver(s) | @stgraber @tomp |
Release | 5.15 |
Internal ID | LXXXX |
Abstract
This adds authorization on top of OIDC using OpenFGA Relationship Based Access Control (ReBAC) which allows fine-grained access control.
Rationale
When using OIDC in LXD, there currently is no way of having access control. The already present RBAC feature is tied to Candid authentication. By adding an independent access control service, this limitation will be resolved.
Specification
Design
LXD will use OpenFGA for authorization. It’s a solution that allows building granular access control using an easy-to-read modeling language.
The following configuration keys will be added:
openfga.api.url
openfga.api.key
openfga.store.id
The value of openfga.api.url
represents the API URL of the OpenFGA server. If needed, openfga.api.key
can be set which contains the API token for accessing the OpenFGA server. Lastly, openfga.store.id
is needed as that’s the store containing the relationship tuples.
Setting both openfga.api.url
and openfga.store.id
enables OpenFGA, given that OIDC authentication is used.
Authorization Model
The following definition is the authorization model which is used to define the permission model of a system. The JSON representation of this model is hard-coded in LXD and cannot be configured. The reason is that LXD relies on the correctness of the naming of types and relations.
model
schema 1.1
type user
type group
relations
define member: [user, group#member]
type project
relations
define manager: [user, group#member]
define viewer: [user, group#member] or manager
define instance_manager: [user, group#member] or manager
define instance_operator: [user, group#member] or instance_manager
define instance_viewer: [user, group#member] or instance_operator
define image_manager: [user, group#member] or manager
define image_viewer: [user, group#member] or image_manager
define profile_manager: [user, group#member] or manager
define profile_viewer: [user, group#member] or profile_manager
define network_manager: [user, group#member] or manager
define network_viewer: [user, group#member] or network_manager
define network_acl_manager: [user, group#member] or manager
define network_acl_viewer: [user, group#member] or network_acl_manager
define network_zone_manager: [user, group#member] or manager
define network_zone_viewer: [user, group#member] or network_zone_manager
define storage_volume_manager: [user, group#member] or manager
define storage_volume_viewer: [user, group#member] or storage_volume_manager
type instance
relations
define project: [project]
define manager: [user, group#member] or instance_manager from project
define operator: [user, group#member] or manager or instance_operator from project
define viewer: [user, group#member] or operator or instance_viewer from project
type image
relations
define project: [project]
define manager: [user, group#member] or image_manager from project
define viewer: [user, group#member] or manager or image_viewer from project
type profile
relations
define project: [project]
define manager: [user, group#member] or profile_manager from project
define viewer: [user, group#member] or manager or profile_viewer from project
type network
relations
define project: [project]
define manager: [user, group#member] or network_manager from project
define viewer: [user, group#member] or manager or network_viewer from project
type network_acl
relations
define project: [project]
define manager: [user, group#member] or network_acl_manager from project
define viewer: [user, group#member] or manager or network_acl_viewer from project
type network_zone
relations
define project: [project]
define manager: [user, group#member] or network_zone_manager from project
define viewer: [user, group#member] or manager or network_zone_viewer from project
type storage_volume
relations
define project: [project]
define manager: [user, group#member] or storage_volume_manager from project
define viewer: [user, group#member] or manager or storage_volume_viewer from project
Workflow
Each API request to LXD will be checked, and return HTTP status code 403 (forbidden) if not authorized. If a new entity (instance, profile, etc.) is created, LXD will create a new tuple for it, so that the relationship between projects and other entities are known. This is not done for projects themselves. If an entity is deleted, the tuple will be deleted, too.
Example:
If a new instance c1
is created in project foo
, the following new tuple will be created:
user: project:foo
relation: project
object: instance:foo_c1
Given a user u1
is an instance_manager
in the foo
project, this will allow the user to manage the created instance (or any instance in the project). If this tuple didn’t exist, the user wouldn’t automatically have access to instance c1
.
All entities (except projects) are identified using <projectName>_<entityName>
(e.g. foo_c1
) because entities of the same name can exist in different projects.
Users who need admin privileges, can be added to the special admin
group. LXD will perform the following check for admin privileges:
user: user:alice
relation: member
object: group:admin
If OpenFGA is enabled, and there are existing entities, all required tuples will be automatically created.
API changes
No API changes.
CLI changes
No CLI changes.
Database changes
No database changes.
Upgrade handling
No upgrade handling.
Further information
- Instead of using
<projectName>_<entityName>
we could use<projectName>/<entityName>
. - Should automatic tuple creation be configurable, e.g.
openfga.sync_tuples
?