Simplify Kubernetes Cluster Resource Access Control using RBAC
Tags: kubernetes, devops
Published: 2023-07-03 09:34:59

#Introduction
Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organization.

RBAC authorization uses the `rbac.authorization.k8s.io` API group to drive authorization decisions, allowing you to dynamically configure policies through the Kubernetes API.

**Enable RBAC**
To enable RBAC, start the API server with the `--authorization-mode` flag set to a comma-separated list that includes RBAC, for example:

`kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options`

> **Note**: From Kubernetes 1.6 onwards, Role-based Access Control is enabled by default. RBAC allows you to specify which types of actions are permitted depending on the user and their role in your organization.


#RBAC API

![K8s Cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmc228vsau7kw28y0sg3.png)

###Role and ClusterRole

An RBAC Role or ClusterRole contains rules that represent a set of permissions. Permissions are purely additive (there are no "deny" rules).

A Role always sets permissions within a particular namespace; when you create a Role, you have to specify the namespace it belongs in.

ClusterRole, by contrast, is a non-namespaced resource. The resources have different names (Role and ClusterRole) because a Kubernetes object always has to be either namespaced or not namespaced; it can't be both.

ClusterRoles have several uses. You can use a ClusterRole to:

1. define permissions on namespaced resources and be granted access within individual namespace(s)
2. define permissions on namespaced resources and be granted access across all namespaces
3. define permissions on cluster-scoped resources

If you want to define a role within a namespace, use a Role; if you want to define a role cluster-wide, use a ClusterRole.

**Role example**

Here's an example Role in the "default" namespace that can be used to grant read access to pods:

```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
```

**ClusterRole example**

A ClusterRole can be used to grant the same permissions as a Role. Because ClusterRoles are cluster-scoped, you can also use them to grant access to:
* cluster-scoped resources (like nodes)
* non-resource endpoints (like /healthz)
* namespaced resources (like Pods), across all namespaces

For example: you can use a ClusterRole to allow a particular user to run `kubectl get pods --all-namespaces`

Here is an example of a ClusterRole that can be used to grant read access to secrets in any particular namespace, or across all namespaces (depending on how it is bound):

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: secret-reader
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Secret
  # objects is "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
```
The name of a Role or a ClusterRole object must be a valid [path segment name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#path-segment-names).

###RoleBinding and ClusterRoleBinding

A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted. A RoleBinding grants permissions within a specific namespace whereas a ClusterRoleBinding grants that access cluster-wide.

A RoleBinding may reference any Role in the same namespace. Alternatively, a RoleBinding can reference a ClusterRole and bind that ClusterRole to the namespace of the RoleBinding. If you want to bind a ClusterRole to all the namespaces in your cluster, you use a ClusterRoleBinding.

The name of a RoleBinding or ClusterRoleBinding object must be a valid [path segment name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#path-segment-names).

**RoleBinding examples**

Here is an example of a RoleBinding that grants the "pod-reader" Role to the user "jane" within the "default" namespace. This allows "jane" to read pods in the "default" namespace.

```
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
  name: jane # "name" is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" specifies the binding to a Role / ClusterRole
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io
```

A RoleBinding can also reference a ClusterRole to grant the permissions defined in that ClusterRole to resources inside the RoleBinding's namespace. This kind of reference lets you define a set of common roles across your cluster, then reuse them within multiple namespaces.

For instance, even though the following RoleBinding refers to a ClusterRole, "dave" (the subject, case sensitive) will only be able to read Secrets in the "development" namespace, because the RoleBinding's namespace (in its metadata) is "development".

```
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "dave" to read secrets in the "development" namespace.
# You need to already have a ClusterRole named "secret-reader".
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # The namespace of the RoleBinding determines where the permissions are granted.
  # This only grants permissions within the "development" namespace.
  namespace: development
subjects:
- kind: User
  name: dave # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

```
**ClusterRoleBinding example**

To grant permissions across a whole cluster, you can use a ClusterRoleBinding. The following ClusterRoleBinding allows any user in the group "manager" to read secrets in any namespace.

```
apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
```

After you create a binding, you cannot change the Role or ClusterRole that it refers to. If you try to change a binding's `roleRef`, you get a validation error. If you do want to change the `roleRef` for a binding, you need to remove the binding object and create a replacement.

There are two reasons for this restriction:

1. Making `roleRef` immutable allows granting someone `update` permission on an existing binding object, so that they can manage the list of subjects, without being able to change the role that is granted to those subjects.
2. A binding to a different role is a fundamentally different binding. Requiring a binding to be deleted/recreated in order to change the `roleRef` ensures the full list of subjects in the binding is intended to be granted the new role (as opposed to enabling or accidentally modifying only the roleRef without verifying all of the existing subjects should be given the new role's permissions).

The `kubectl auth reconcile` command-line utility creates or updates a manifest file containing RBAC objects, and handles deleting and recreating binding objects if required to change the role they refer to. See [command usage and examples](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#kubectl-auth-reconcile) for more information.

###Examples: Restricting a user account's access using RBAC
The following examples utilize the default ClusterRoles provided with Kubernetes

**Example 1: Grant a user read/write access to a particular namespace**

To restrict a user's access to a particular namespace, we can use either the `edit` or the `admin` role. If your charts create or interact with Roles and Rolebindings, you'll want to use the `admin` ClusterRole.

Additionally, you may also create a RoleBinding with `cluster-admin` access. Granting a user `cluster-admin` access at the namespace scope provides full control over every resource in the namespace, including the namespace itself.

For this example, we will create a user with the `edit` Role. First, create the namespace:

`$ kubectl create namespace test`

Now, create a RoleBinding in that namespace, granting the user the `edit` role.

`$ kubectl create rolebinding sam-edit
    --clusterrole edit \​
    --user sam \​
    --namespace test`

**Example 2: Grant a user read/write access at the cluster scope**

If a user wishes to install a chart that installs cluster-scope resources (namespaces, roles, custom resource definitions, etc.), they will require cluster-scope write access.

To do that, grant the user either `admin` or `cluster-admin` access.

Granting a user `cluster-admin` access grants them access to absolutely every resource available in Kubernetes, including node access with `kubectl drain` and other administrative tasks. It is highly recommended to consider providing the user `admin` access instead, or to create a custom ClusterRole tailored to their needs.

`$ kubectl create clusterrolebinding sam-view
    --clusterrole view \​
    --user sam`

`$ kubectl create clusterrolebinding sam-secret-reader
    --clusterrole secret-reader \​
    --user sam`

**Example 3: Grant a user read-only access to a particular namespace**

You might've noticed that there is no ClusterRole available for viewing secrets. The view ClusterRole does not grant a user read access to Secrets due to escalation concerns. Helm stores release metadata as Secrets by default.

In order for a user to run `helm list`, they need to be able to read these secrets. For that, we will create a special `secret-reader` ClusterRole.

Create the file `cluster-role-secret-reader.yaml` and write the following content into the file:

```
apiVersion: rbac.authorization.k8s.io/v1​
kind: ClusterRole​
metadata:​
  name: secret-reader​
rules:​
- apiGroups: [""]​
  resources: ["secrets"]​
  verbs: ["get", "watch", "list"]
```

Then, create the ClusterRole using

`$ kubectl create -f clusterrole-secret-reader.yaml​`

Once that's done, we can grant a user read access to most resources, and then grant them read access to secrets:

`$ kubectl create namespace dev`

`$ kubectl create rolebinding sam-view
    --clusterrole view \​
    --user sam \​
    --namespace dev`

`$ kubectl create rolebinding sam-secret-reader
    --clusterrole secret-reader \​
    --user sam \​
    --namespace dev`


**Example 4: Grant a user read-only access at the cluster scope**

In certain scenarios, it may be beneficial to grant a user cluster-scope access. For example, if a user wants to run the command `helm list --all-namespaces`, the API requires the user has cluster-scope read access.

To do that, grant the user both `view` and `secret-reader` access as described above, but with a ClusterRoleBinding.

`$ kubectl create clusterrolebinding sam-view
    --clusterrole view \​
    --user sam`

`$ kubectl create clusterrolebinding sam-secret-reader
    --clusterrole secret-reader \​
    --user sam`

##Conclusion
From the above examples, you can observe some behaviors and limitations of RBAC resources:
* Roles and role bindings must exist in the same namespace.
* Role bindings can exist in separate namespaces to service accounts.
* Role bindings can link cluster roles, but they only grant access to the namespace of the role binding.
* Cluster role bindings link accounts to cluster roles and grant access across all resources.
* Cluster role bindings can not reference roles.

With RBAC, you can:
* grant privileged operations (creating cluster-wide resources, like new roles) to administrators
* limit a user's ability to create resources (pods, persistent volumes, deployments) to specific namespaces, or in cluster-wide scopes (resource quotas, roles, custom resource definitions)
* limit a user's ability to view resources either in specific namespaces or at a cluster-wide scope.