Migrate from RBAC v1 to RBAC v2
This is a guide for the first phase of migration of existing Insights applications from RBACv1 to Kessel. In this initial phase, the goal is to start using Kessel for authorization enforcement while keeping parity with the current behaviors. Further âworkspacificationâ (i.e. making assets other than hosts workspace-aware) is out of scope for the initial phase and this guide.
Questions? Please raise them in #mgmt-fabric-insights-integrations
Identify the patterns
Section titled âIdentify the patternsâWe start the migration process by identifying the patterns used by our application. The patterns should be identified individually for each asset type managed by the application. Different asset types may fall under different patterns and hence an application may use a combination of patterns. For example, an application may use a combination of:
- permission protecting access to an org-wide setting
- permission protecting access to inventory group aware assets (e.g. host vulnerabilities)
The following patterns are supported:
For the initial migration of RBACv1 applications to Kessel, the following simplified decision tree can be used to identify the appropriate pattern:
- Is the resource already workspace-aware (e.g. hosts, workspaces)?
- Do queries return fewer than ~10k results, or are more than ~80% of results accessible to the requesting user? â> Use Native
- Do queries return more than ~10k results and fewer than ~80% of results are accessible? â> Use Native, workspace-level list
- Can the resource be conceptualized as an âassetâ (customer manages CRUD; we can imagine it as being placed in a Workspace) but is not yet workspace-aware? â> Use Default workspace
- Is the resource âthe organizationâ as a whole (e.g. managing an organization-wide setting) and the operation is âasset-centricâ (we could imagine it being Workspace level in the future)? â> Use Root workspace
- Is the resource âthe organizationâ as a whole (e.g. managing an organization-wide setting) and the operation is not âasset-centricâ (would never belong at a Workspace level)? â> Use Organization-level
See the Migration Pattern Reference for detailed information about each pattern, including request translations and implementation guidance.
Model the permissions
Section titled âModel the permissionsâAfterwards, we need to convert permission definitions from the legacy RBACv1 format to permission definitions expressed using the ksl language.
These permission definitions are stored in .ksl files in the rbac-config repository.
The @rbac.add_v1_based_permission() extension simplifies the process of converting the legacy RBACv1 permission definitions to Kessel permissions by incorporating support for wildcard permissions.
The following rules apply to the @rbac.add_v1_based_permission() extension:
- The
appparameter should be the name of the application. - The
resourceparameter should be the singular name of the resource type. - The
verbparameter should be the the verb. - The
v2_permparameter should be the name of the compound permission in Kessel. Note that the last segment of the v2 permission name will differ from the verb of the v1 permission. The following naming convention should be used:read->viewwrite->edit
Example of a permission definition using the @rbac.add_v1_based_permission() extension:
version 0.1namespace config_manager
import rbac
@rbac.add_v1_based_permission(app:'config_manager', resource:'profile', verb:'read', v2_perm:'config_manager_profile_view');@rbac.add_v1_based_permission(app:'config_manager', resource:'profile', verb:'write', v2_perm:'config_manager_profile_edit');Permissions that apply to host-centric assets
Section titled âPermissions that apply to host-centric assetsâCertain RBACv1 permissions apply to host-centric assets. These permissions are typically used to protect access to host-specific data, such as host vulnerabilities, advisories, etc.
In RBACv1, these permissions are evaluated in conjunction with the inventory:host:read permission, which can be restricted to specific workspaces (inventory groups) using attribute filters.
In Kessel, this logic can be implemented natively by combining the compound permission with the inventory:host:read permission.
The rbac.add_contingent_permission() extension simplifies this process.
@rbac.add_v1_based_permission(app:'patch', resource:'system', verb:'read', v2_perm:'patch_system_view_assigned');@rbac.add_contingent_permission(first: 'inventory_host_view', second: 'patch_system_view_assigned', contingent: 'patch_system_view');which is equivalent to (metalanguage used for illustration purposes):
patch_system_view_assigned = patch_system_read OR patch_system_all OR patch_all_read OR patch_all_all OR all_all_allpatch_system_view = inventory_host_view AND patch_system_view_assignedNotice that the _assigned suffix is used for the compound permission, which distinguishes it from the contingent permission that incorporates the inventory permission and which should be used for access checks.
Full example:
version 0.1namespace patch
import rbacimport hbi
@rbac.add_v1_based_permission(app:'patch', resource:'system', verb:'read', v2_perm:'patch_system_view_assigned');@rbac.add_contingent_permission(first: 'inventory_host_view', second: 'patch_system_view_assigned', contingent: 'patch_system_view');@hbi.expose_host_permission(v2_perm: 'patch_system_view', host_perm: 'patch_system_view');
@rbac.add_v1_based_permission(app:'patch', resource:'system', verb:'write', v2_perm:'patch_system_edit_assigned');@rbac.add_contingent_permission(first: 'inventory_host_view', second: 'patch_system_edit_assigned', contingent: 'patch_system_edit');@hbi.expose_host_permission(v2_perm: 'patch_system_edit', host_perm: 'patch_system_edit');Checking in the permission definitions
Section titled âChecking in the permission definitionsâOnce the .ksl file(s) are ready, they need to be added to the rbac-config repository by opening a PR.
Note that a separate config exists for each environment (stage, prod), allowing the change to be tested in stage before being released to prod.
There is no need to include the modified zed schema in the PR as an automated job will transpile the new/modified ksl files into a zed schema.
Role definitions
Section titled âRole definitionsâNote that the way these permissions are associated with a role remains the same, i.e. via role files.
In some cases a completely new permission may be defined in the process of onboarding to Kessel. In that case, consider defining this permission in RHEL persona-based roles (e.g. RHEL Viewer) in addition to application-specific roles (e.g. Advisor Viewer). See the Role Definitions section of the Migration Pattern Reference for additional details.
Implement the changes in the application code
Section titled âImplement the changes in the application codeâThe implementation part typically consists of the following steps:
- Import the Kessel client library
- Implement the authorization check in the application code
- Add tests
- Add configuration options for the Kessel client
Importing the client library
Section titled âImporting the client libraryâKessel client libraries are available for these languages:
Implement the authorization check in the application code
Section titled âImplement the authorization check in the application codeâThe authorization check is typically implemented at the middleware layer. The actual implementation differs based on the selected pattern(s).
Root/Default workspace pattern
Section titled âRoot/Default workspace patternâThe implementation of root workspace pattern and default workspace pattern starts with a lookup of the default/root workspace id for the given organization.
The workspace ID can be obtained from the RBAC service via a REST call:
GET /api/rbac/v2/workspaces/?type=root&with_ancestry=trueGET /api/rbac/v2/workspaces/?type=default&with_ancestry=trueThe default/root workspace ID for a given organization is immutable. Cache it aggressively to avoid the latency and reliability cost of an RBAC lookup on every request. See the Caching section of the Migration Pattern Reference for details.
With the workspace id resolved, the authorization check call can be made to Kessel. The actual method called depends on the nature of the operation.
Use CheckForUpdate for performing authorization check for
- any write operations
- highly-sensitive read operations (e.g. access to read credentials for connecting to a customerâs cluster)
In all other cases, use Check.
The parameters provided for the Check/CheckForUpdate request for the Root/Default workspace pattern should be structured as follows:
object := &kesselv2.ResourceReference{ ResourceType: "workspace", ResourceId: defaultOrRootWorkspaceID, Reporter: &kesselv2.ReporterReference{ Type: "rbac", },}
relation = // the permission to check for
subject := &kesselv2.SubjectReference{ Resource: &kesselv2.ResourceReference{ ResourceType: "principal", ResourceId: fmt.Sprintf("redhat/%s", principalID), Reporter: &kesselv2.ReporterReference{ Type: "rbac", }, },}Default workspace pattern implementations and PoCs:
See the Default Workspace pattern reference for additional information.
Native, workspace-level list
Section titled âNative, workspace-level listâThe Native, workspace-level list pattern is typically implemented by resolving the ids of all the workspaces for which the given principal possesses the given permission and then using these workspace ids to filter the assets in application database query.
The workspace ids are obtained using the StreamedListObjects method and its parameters should be defined as follows:
objectType := &kesselv2.RepresentationType{ ResourceType: "workspace", ReporterType: "rbac",},
relation = // the permission to check for
subject := &kesselv2.SubjectReference{ Resource: &kesselv2.ResourceReference{ ResourceType: "principal", ResourceId: fmt.Sprintf("redhat/%s", principalID), Reporter: &kesselv2.ReporterReference{ Type: "rbac", }, },}Native, workspace-level list pattern implementations and PoCs:
See the Native workspace-level list pattern reference for additional information.
Adding tests
Section titled âAdding testsâTests should verify that your application correctly constructs and sends authorization requests to Kessel, and properly handles the responses.
What to test
Section titled âWhat to testâ- Authorization middleware: Verify that your middleware sends the correct
CheckorCheckForUpdaterequest to Kessel for each protected endpoint, including the correct resource type, resource ID, relation (permission), and subject. - Allowed and denied scenarios: Test that a
ALLOWED_TRUEresponse grants access and aALLOWED_FALSEresponse returns 403. - Error handling: Test that Kessel errors (e.g. connection failures, timeouts) result in an appropriate error response (typically 500), not a silent allow.
- Principal types: Test with both user principals (
identity.User.UserId) and service account principals (identity.ServiceAccount.UserId). - Workspace resolution: If using the Default or Root Workspace pattern, test that the workspace ID is resolved correctly and that resolution failures are handled.
- Feature flag fallback: Test that
KESSEL_ENABLED=falsecleanly falls back to existing v1 authorization with no side effects, and that toggling it on produces the same authorization outcomes as v1 for equivalent permissions. - Parity between v1 and v2: During the migration period, verify that enabling Kessel does not change authorization outcomes compared to the existing v1 path. The insights-rbac parity checker demonstrates this pattern by asserting that v1 operations and v2 relation tuples remain consistent after every mutation.
- StreamedListObjects (workspace-level list pattern): Test handling of the streaming response, including partial results, empty streams when the user has no access, and correct use of the returned workspace IDs as database query filters.
- Contingent permissions (host-centric assets): If using
@rbac.add_contingent_permission(), test that a user needs both the inventory host permission and the app-specific permission. A user holding only one should be denied.
How to test
Section titled âHow to testâMock the Kessel gRPC client in your unit tests rather than connecting to a live Kessel instance. Create a mock implementation of the Kessel Inventory Service client that:
- Records the requests it receives for assertion
- Returns configurable responses (
ALLOWED_TRUE,ALLOWED_FALSE) or errors
Then assert that:
- The resource reference in the request has the expected type, ID, and reporter
- The relation matches the permission being checked
- The subject reference contains the correct principal ID and type
Example implementations
Section titled âExample implementationsâ- Config Manager (Go) - Uses a mock gRPC client with table-driven tests covering authorization, error handling, and both principal types.
- insights-rbac (Python) - Uses an in-memory tuple store (
InMemoryTuples) as a SpiceDB substitute, with consistency assertions that verify both database state and relation tuples after each operation.
Add configuration options for the Kessel client
Section titled âAdd configuration options for the Kessel clientâThe application needs to connect to two Kessel services: the Inventory API (for authorization checks) and the Relations API (for relation management). The following configuration options should be added:
KESSEL_ENABLEDâ feature flag to toggle Kessel authorization on/offRELATION_API_SERVERâ gRPC address for the Kessel Relations API (default:localhost:9000)INVENTORY_API_SERVERâ gRPC address for the Kessel Inventory API (default:localhost:9000)- Auth credentials per service (e.g.
RELATION_API_CLIENT_ID,RELATION_API_CLIENT_SECRET,INVENTORY_API_CLIENT_ID,INVENTORY_API_CLIENT_SECRET) - Token URL per service (e.g.
RELATIONS_API_TOKEN_URL,INVENTORY_API_TOKEN_URL)
Clowder service discovery
Section titled âClowder service discoveryâWhen running in a Clowder-managed environment, the Kessel service endpoints can be resolved automatically using Clowderâs DependencyEndpoints instead of manually configuring URLs.
To enable this, declare kessel-inventory and kessel-relations as dependencies in your ClowdApp manifest:
optionalDependencies: - kessel-inventory - kessel-relationsThen resolve the endpoints from Clowder at startup, falling back to the environment variables if the dependency is not available:
from app_common_python import DependencyEndpoints
if CLOWDER_ENABLED: try: hostname = DependencyEndpoints["kessel-inventory"]["api"].hostname INVENTORY_API_SERVER = f"{hostname}:9000" except KeyError: pass # fall back to env var / default
try: hostname = DependencyEndpoints["kessel-relations"]["api"].hostname RELATION_API_SERVER = f"{hostname}:9000" except KeyError: pass # fall back to env var / defaultSee insights-rbacâs settings.py and Config Managerâs configuration for example implementations.
Validate the changes in the ephemeral environment
Section titled âValidate the changes in the ephemeral environmentâDeploy the changes
Section titled âDeploy the changesâFollow Using Kessel in production to set up your service to use Kessel in stage and prod.