7 Expert Strategies for Managing RBAC on OpenShift

With the rise of containerization and cloud-based computing, securing infrastructure and managing fine-grained access control can be a significant challenge for organizations using Red Hat OpenShift. This platform provides a vast array of tools and features, but maintaining tight security standards means managing access and permissions at a granular level.
If you’re new to role-based access control (RBAC), it can be difficult to keep track of who has access to which resources and to ensure that users only have access to the resources they need.
In this article, we will explore the best practices for managing fine-grained access in OpenShift.
Authorization in OpenShift is managed using role-based access control (RBAC). OpenShift adopts a deny-by-default approach to RBAC. Roles are established using collections of rules that allow certain actions on objects within the container cluster. These roles can be assigned to users and groups via RoleBindings.
Roles are arranged in a hierarchy, allowing the definition of cluster-wide roles as well as customized, project-specific “local” roles. RBAC evaluation considers this hierarchy, examining cluster-wide roles first and then local roles. This allows cluster-wide roles to be augmented or overridden at the project level if necessary.
RoleBindings also follow the same cluster and local hierarchy, enabling authorization to be scoped using common roles. For example, roles defined at the cluster level can be used by a local binding to apply it to a specific project.
The following section provides seven recommended best practices that can help organizations secure their OpenShift infrastructure while maintaining the flexibility to manage access and permissions effectively.
1. Follow the Principle of Least Privilege.
Granting only the minimum necessary access reduces the risk of accidental or intentional misuse of resources. When defining roles, it is important to only include the verbs and resources that are required for the role to perform its set functions.
It’s also good to regularly review and update roles to ensure that they continue to align with the changing needs of the organization. By following the principle of least privilege — the cornerstone of an overall zero trust approach to security — organizations can minimize the attack surface and ensure the security of their OpenShift cluster.
This strategy requires that a role should only be granted the minimum necessary access to resources.
Here’s an example of a role definition in OpenShift that follows the principle of least privilege:
1 2 3 4 5 6 7 8 9 |
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: developer-role namespace: dev rules: - apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "watch", "list"] |
In this example, the developer-role
is defined for the dev
namespace. The role only has access to the get
, watch
and list
verbs for the pods and services resources. This allows developers to view information about these resources, but not make changes to them.
Here’s another example:
1 2 3 4 5 6 7 8 9 |
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: admin-role namespace: dev rules: - apiGroups: [""] resources: ["pods", "services", "replicationcontrollers", "configmaps", "secrets"] verbs: ["get", "watch", "list", "create", "update", "delete"] |
In this example, the admin-role
is also defined for the dev
namespace, but has more extensive access to resources. The role has access to all verbs for the pods
, services
, replicationcontrollers
, configmaps
and secrets
resources, allowing administrators to perform all actions on these resources.
By defining roles that only include the strictly required verbs, organizations can ensure that users have only the access they need to perform their responsibilities. This helps to minimize the attack surface and increase the security of the OpenShift cluster.
In OpenShift, access control rules are defined using role and RoleBindings. A role defines the permissions (verbs) that a user or service account has on a specific resource type. A RoleBinding maps a role to a user or service account.
Here’s an example of a Role
definition that grants only the strictly required verbs for read access to the pods
resource in a namespace:
1 2 3 4 5 6 7 8 9 |
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-reader namespace: my-namespace rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] |
In this example, the pod-reader role has get
, watch
and list
permissions on the pods resource in the my-namespace
namespace. This means that a user with this role can retrieve information about pods, but cannot modify or delete them.
Next, we can map the role to a user or service account using a RoleBinding:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: pod-reader-binding namespace: my-namespace subjects: - kind: User name: john-doe apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io |
In this example, the pod-reader-binding
maps the pod-reader
role to a user named john-doe
. The user john-doe
now has read access to the pods
resource in the my-namespace
namespace.
By following this approach, you can ensure that the permissions granted to a role are only the strictly required verbs, reducing the risk of accidental or intentional misuse of resources.
2. Exercise Caution When Using ClusterRoles for Namespaced Resources.
ClusterRoles are powerful resources that can provide broad access to resources across the entire cluster. While they can be useful in some cases, they can also pose a security risk if not used properly.
It’s generally a better practice to use local roles, such as roles scoped at a namespace level, to provide access to specific namespaces. This approach offers more granular control and reduces the risk of granting access to unintended resources.
For example, if you want to grant a user access to all resources in a specific namespace, you can create a local role with the necessary permissions and bind it to the user using a RoleBinding:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: namespace-admin namespace: my-namespace rules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: namespace-admin-binding namespace: my-namespace subjects: - kind: User name: john-doe apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: namespace-admin apiGroup: rbac.authorization.k8s.io |
In this example, the namespace-admin
role provides full access to all resources in the my-namespace
namespace. The namespace-admin-binding
maps the namespace-admin
role to the user john-doe
.
By using local roles and avoiding ClusterRoles whenever possible, you can ensure that access to resources is carefully controlled and limited to only the users who need it.
3. Use the Predefined ClusterRoles.
The predefined ClusterRoles are well-tested, maintained and curated by the OpenShift team, which helps ensure that they are secure and stable. By using the predefined ClusterRoles, you can reduce the risk of unexpected behavior, security issues and other problems that might arise from defining custom roles.
For example, instead of defining a custom ClusterRole for a user or service account to have access to the logs of pods in a specific namespace, you can use the predefined cluster-reader
ClusterRole. This ClusterRole provides read access to most resources in the cluster, including logs of pods in a namespace.
Here’s an example of a RoleBinding that maps the cluster-reader
ClusterRole to a user or service account:
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: cluster-reader-binding subjects: - kind: User name: jane-doe apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-reader apiGroup: rbac.authorization.k8s.io |
In this example, the cluster-reader-binding
maps the cluster-reader
ClusterRole to a user named jane-doe
. The user jane-doe
now has read access to most resources in the cluster, including the logs of pods in a namespace.
4. Use a Specific Service Account, Not the Default Service Account.
This practice provides more control over the permissions granted to a pod. When a pod is created, it can be associated with a specific service account. This allows you to grant the pod the exact permissions it needs to perform its prepense function, and nothing more. This helps minimize the potential for unintended access and reduces the attack surface of your cluster.
Here’s an example of how you can create a specific service account for a pod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
apiVersion: v1 kind: ServiceAccount metadata: name: my-service-account namespace: my-namespace --- apiVersion: v1 kind: Pod metadata: name: my-pod namespace: my-namespace spec: serviceAccountName: my-service-account # ... other pod specification |
In this example, a new service account named my-service-account
is created in the namespace my-namespace
. The pod my-pod
is then specified to use the my-service-account
service account by setting the serviceAccountName
field in the pod specification.
It’s a best practice to use a specific service account for each pod instead of relying on the default service account. By using a specific service account, you can ensure that the permissions for each pod are explicit and easily documented, and traced.
5. Manage Bindings via Groups.
By managing bindings via groups, you can centralize the definition of the set of users and make it easier to review and manage the group. This is especially important if you need to remove a user’s binding, as you only need to remove the user from the group instead of updating the binding to remove the individual user.
Here’s an example of how you can use groups to manage bindings:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
kind: Group apiVersion: user.openshift.io/v1 metadata: name: my-group annotations: description: Group for my-app developers users: - developer1 - developer2 --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: my-rolebinding namespace: my-namespace subjects: - kind: Group name: my-group apiGroup: user.openshift.io roleRef: kind: Role name: my-role apiGroup: rbac.authorization.k8s.io |
In this example, a group my-group
is created that contains two users developer1
and developer2
. A role binding my-rolebinding
is then created in the namespace my-namespace
that refers to the group my-group
as the subject and binds it to the role my-role
.
(Note: The apiVersion: user.openshift.io/v1
is used to specify the API version for the Group resource in OpenShift, which is used to manage user and group definitions in the platform).
6. Regularly Review and Test the RBAC Configuration.
This is important to ensure that users have the correct access to the resources they need and that they are not given more permissions than necessary.
Here are some ways to review and test access:
Use the oc get
command to retrieve the ClusterRoles, ClusterRoleBindings, Roles, and RoleBindings in the cluster. For example, to list all the ClusterRoles in the cluster, you can use the following command:
1 |
$ oc get clusterroles |
Use the oc describe
command to display the details of a particular ClusterRole, ClusterRoleBinding, Role, or RoleBinding. For example, to display the details of the cluster-reader
ClusterRole, you can use the following command:
1 |
$ oc describe clusterrole cluster-reader |
Use the oc auth
command to test a user’s access to a particular resource in the cluster. For example, to test if a user can create secrets in the current namespace, you can use the following command:
1 |
$ oc auth can-i create secrets |
Or, to check if a user can create secrets in the default namespace:
1 |
$ oc auth can-i create secrets –n default |
This will return either yes or no, indicating whether the user has the required permissions to perform the action.
These tools can be used to regularly review the RBAC configuration and test access to ensure that users have the correct permissions to perform their tasks in the cluster.
7. Use Impersonation.
Impersonation is a powerful feature that allows administrators to temporarily assume the identity of another user to perform operations as that user. This can be useful for troubleshooting and debugging RBAC issues or for allowing a user to perform actions that would otherwise require elevated privileges.
However, it’s important to use impersonation responsibly and to understand the implications of the actions performed while pretending to be another user.
For example, you can use the following command to test if a user has permission to get the details of a deployment while impersonating another user:
1 |
$ oc auth can-i --as=<impersonated-user> get deployments |
Here’s an example YAML file that creates a group and allows members of the group to impersonate the cluster-admin
role:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
apiVersion: v1 kind: Group metadata: name: impersonators annotations: openshift.io/description: "This group allows members to impersonate cluster-admin for debugging purposes." users: - user1 - user2 --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: impersonators-as-admin roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: impersonators |
With this configuration in place, a member of the “impersonators” group can use the following command to create a secret while impersonating the cluster-admin
role:
1 2 3 |
$ oc adm policy add-role-to-user cluster-admin -z default -n <namespace> $ oc create secret generic <secret-name> --as=cluster-admin -n <namespace> |
(Note: This is a powerful tool and should only be used for debugging purposes. Impersonation should only be granted to trusted users).
By testing access with the oc auth can-i
plugin and impersonation, cluster administrators can verify that the correct permissions have been granted to the right users and ensure that the RBAC policies are functioning as designed.