Skip to content

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

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 asset related to a host (vulnerability, advisory, compliance report, etc.) and the service already workspace-aware? 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)? —> 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 Pattern Summary section in KSL-016: Migrating host and organization level permissions for more information about the patterns.

The patterns identified for each application are tracked in Management Fabric Integrations

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 app parameter should be the name of the application.
  • The resource parameter should be the singular name of the resource type.
  • The verb parameter should be the the verb.
  • The v2_perm parameter 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 -> view
    • write -> edit

Example of a permission definition using the @rbac.add_v1_based_permission() extension:

version 0.1
namespace 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');

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_system_all_read OR patch_system_all_all OR all_all_all
patch_system_view = inventory_host_view AND patch_system_view_assigned

Notice 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.1
namespace patch
import rbac
import 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');

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.

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 RHCLOUD-35891 for additional details.

The implementation part typically consists of the following steps:

  1. Import the Kessel client library
  2. Implement the authorization check in the application code
  3. Add tests
  4. Add configuration options for the Kessel client

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).

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 /v2/workspaces?type=root
GET /v2/workspaces?type=default

The default/root workspace for a given organization is guaranteed to never change and is therefore easily cacheable. However, in the future the need to look up the default/root workspace ID will be resolved transparently on the Kessel side. There is therefore no need to implement the caching code in the application code at this time.

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 Default workspace for additional information.

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 Native, workspace-level list for additional information.

TBD

Example of a testcase for Kessel integration

The application should add the following configuration options for Kessel:

  • KESSEL_ENABLED
  • KESSEL_URL
  • KESSEL_AUTH_ENABLED
  • KESSEL_AUTH_CLIENT_ID
  • KESSEL_AUTH_CLIENT_SECRET
  • KESSEL_AUTH_OIDC_ISSUER
  • KESSEL_INSECURE

The ClowdApp template should also be updated accordingly to enable environment-specific configuration. See Config Manager’s configuration for an example.

The following section describes how to deploy Kessel and related services in an ephemeral environment and use them to validate the changes made to an integrating application.

insights-service-deployer should be used to deploy Kessel and other related services in an ephemeral environment. Follow the instructions in the README to reserve a namespace and set up the environment variables and then run the following command to deploy Kessel and related services and seed them with sample data:

Terminal window
./deploy.sh deploy_with_hbi_demo

Applications using Kessel for access control need to authenticate with Kessel and RBAC in stage/prod environments. The following steps enable authentication with Kessel so that it can be validated in the ephemeral environment.

  1. Navigate to Identity & Access Management in Stage to create a new service account and obtain the client ID and client secret. This is a service account that our application will use to authenticate with Kessel and RBAC.

  2. Start a new shell and store the client ID and client secret as environment variables:

    Terminal window
    CLIENT_ID=<client_id>
    CLIENT_SECRET=<client_secret>
  3. Next, we need to determine the subject (sub claim) for the service account:

    Terminal window
    SUB=$(curl -s https://sso.stage.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token -d grant_type=client_credentials -d client_id=${CLIENT_ID} -d client_secret=${CLIENT_SECRET} | jq -r '.access_token' | awk -F. '{print $2}' | base64 --decode 2>/dev/null | jq -r '.sub')
  4. Now, we’ll update the configuration of the RBAC service to enable authentication. With the RBAC service running in the ephemeral environment (see steps above), run the following command to grant unlimited access to the service account created in the first step:

    Terminal window
    oc create secret generic system-users --dry-run=client -o yaml --from-literal="system-users.json={
    \"$SUB\": {
    \"admin\": true,
    \"is_service_account\": true,
    \"allow_any_org\": true
    }
    }" | kubectl apply -f -
  5. Next, reconfigure the RBAC service to validate access tokens using Stage SSO:

    Terminal window
    oc get clowdapp rbac -o json | \
    jq '(.spec.deployments[] | select(.name=="service") | .podSpec.env[] | select(.name=="IT_BYPASS_TOKEN_VALIDATION") | .value) = "False" |
    (.spec.deployments[] | select(.name=="service") | .podSpec.env[] | select(.name=="IT_SERVICE_HOST") | .value) = "sso.stage.redhat.com"' | \
    oc apply -f -
  6. Furthermore, we also need to update the Kessel Inventory service configuration to enable authentication. Run oc edit cm inventory-api-config to edit the kessel-inventory configuration ConfigMap and in the editor update the authn section like this:

    authn:
    oidc:
    authn-server-url: https://sso.stage.redhat.com/auth/realms/redhat-external
    client-id: foo
    skip-client-id-check: true
    insecure-client: false
  7. Finally, restart the Kessel service:

    Terminal window
    oc delete pod -l pod=kessel-inventory-api

With Kessel running in the ephemeral environment, you can forward the ports to enable the application running locally to connect to RBAC and Kessel.

  1. Use oc port-forward svc/rbac-service 8000:8000 to forward port 8000 to the RBAC service
  2. Use oc port-forward svc/kessel-inventory-api 9091:9000 to forward port 9091 to the Kessel Inventory service

With port forwarding running, you can now configure your application to use the local Kessel and RBAC ports and start it.

For example, for Config Manager, the command to configure and run the application looks like this:

Terminal window
go run main.go --log-level trace --kessel-enabled=true --kessel-url=localhost:9091 --kessel-auth-enabled=true --kessel-auth-client-id=${CLIENT_ID} --kessel-auth-client-secret=${CLIENT_SECRET} --kessel-auth-oidc-issuer=https://sso.stage.redhat.com/auth/realms/redhat-external --rbac-url=http://localhost:8000 http-api

Follow Using Kessel in production to set up your service to use Kessel in stage and prod.