Role-based access control (RBAC) in Kubernetes

Role-based access control (RBAC) is used to assign access to a computer or network resources in the Kubernetes Cluster.

In this article, we will understand the basics of RBAC and create Role, ClusterRole, RoleBinding and ClusterRoleBinding Objects.

We will then create a kubeconfig file to give limited access to a particular user on a selected namespace.

But before we proceed let's understand the basics first.

  1. A Role or ClusterRole contains a set of permissions.  
  2. A Role sets permissions within a particular namespace and ClusterRole is a non-namespaced resource.
  3. A role binding grants the permissions defined in a role to a user or set of users whereas ClusterRoleBinding grants that access cluster-wide 
  4. 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 RoleBindin
  5. A kubeconfig file is a file used to configure access to Kubernetes from the kubectl command-line tool.

To understand RBAC in detail, visit the official documentation of Kubernetes here.

Note: Refer screenshots to avoid any confusion before executing the commands. ( ubuntu@master = master node and ubuntu@ip-172-31-25-70 = user machine)

Pre-requisites

  1. Kubernetes Cluster with at least 1 worker node.
    If you want to learn to create a Kubernetes Cluster, click here. This guide will help you create a Kubernetes cluster with 1 Master and 2 Nodes on AWS Ubuntu EC2 Instances. 

What will we do?

  1. Create Role, Role Binding, Cluster Role, Cluster Role Binding object files.
  2. Create Role, Role Binding, Cluster Role, Cluster Role Binding objects in the cluster.
  3. Provide access to users using the kubeconfig file.
  4. Summary of kubeconfig file creation.

Create a Role, Role Binding, Cluster Role, Cluster Role Binding object files.

Create a file to create a Role in the "default" namespace that can be used to grant the get, watch and list access to pods. 

vim my-role.yml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

my-role

Create a new file to create a RoleBinding that allows the "pod-reader" Role to the user "jane" within the "default" namespace.

vim my-role-binding.yml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

my-role-binding

Create a file to create a ClusterRole that can be used to grant the get, watch and list access to secrets in any particular namespace, or across all namespaces depending on how it is bound.

vim my-cluster-role.yml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

my-cluster-role

Create a new file to create a ClusterRoleBinding that will allow any user in the group "manager" to read secrets in any namespace.

vim my-cluster-role-binding.yml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

my-cluster-role-binding

Create a Role, Role Binding, Cluster Role, Cluster Role Binding objects.

Get a list of existing Roles and ClusterRoles from the cluster.

kubectl  get roles
kubectl  get clusterroles

get-default-role-clusterrole

Get a list of existing RoleBindings and ClusterRoleBindings from the cluster.

kubectl  get rolebinding
kubectl  get clusterrolebinding

get-default-role-binding-clusterrole-binding

Now create a Role, RoleBinding and ClusterRole ClusterRoleBinding using the files we created in the above steps.

kubectl create -f my-role.yml
kubectl create -f my-role-binding.yml
kubectl create -f my-cluster-role.yml
kubectl create -f my-cluster-role-binding.yml

create-role-and-binding-objects

Using the following commands verify if the objects have been created.

kubectl  get roles | grep pod-reader
kubectl  get rolebinding | grep read-pods
kubectl  get clusterroles | grep secret-reader
kubectl  get clusterrolebinding | grep read-secrets-global

get-role-and-binding-objects

In the above screenshot, you can see that the Role, RoleBinding and ClusterRole, ClusterRoleBinding has been created.

Provide access to users using the kubeconfig(config) file.

Now, in this section, we will create a config file that can be shared with a user. Here, to test this scenario we will create a user "bob" on the Linux server and share this config file with the "bob" user. We will then try to perform operations that are allowed and disallowed to that user. We will bind an admin ClusterRole to the "bob" user which will give access on all objects within "bob" namespace. 

On the master-nodes create a key and certificate signing request (CSR) using openssl.

pwd
mkdir user-bob
cd user-bob/
openssl req -new -newkey rsa:4096 -nodes -keyout bob-k8s.key -out bob-k8s.csr -subj "/CN=bob/O=devops"
cat bob-k8s.csr | base64 | tr -d '\n'

Create a CertificateSigningRequest object definition file containing the CSR we generated in the above step. In the below file add the output of "cat bob-k8s.csr | base64 | tr -d '\n'" command  to "request" property.

vim k8s-csr.yaml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: bob-k8s-access
spec:
  groups:
  - system:authenticated
  request: # replace output of: cat bob-k8s.csr | base64 | tr -d '\n'
  usages:
  - client auth
cat k8s-csr.yaml

create-key-and-create-certificate-signing-request-object-file

Create a CertificateSigningRequest object within Kubernetes containing the CSR we generated in the above step.

kubectl  get csr
kubectl  create -f k8s-csr.yaml
kubectl  get csr

Now we want to approve the CSR (CertificateSigningRequest) object we created in the above step.

kubectl  get csr
kubectl certificate approve bob-k8s-access
kubectl  get csr

approve-certificate-signing-request

In the above screenshot, you can see that the CSR has been Approved, Issued.

Retrieve the certificate available in the ‘status.certificate’ field of the CSR object.

ls -lt
kubectl get csr bob-k8s-access -o jsonpath='{.status.certificate}' | base64 --decode > bob-k8s-access.crt
ls -lt
cat bob-k8s-access.crt

retrieve-the-certificate

Retrieve cluster CA certificate which is the next requirement for Bob’s kubeconfig file and save it to "k8s-ca.crt" file.

ls -lt
kubectl config view -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' --raw | base64 --decode - > k8s-ca.crt
ls -lt
cat k8s-ca.crt

get-cluster-CA-certificate

Set up the cluster configuration in Bob’s kubeconfig file. All these details will be put from our existing kubeconfig using the command below.

ls -lt
kubectl config set-cluster $(kubectl config view -o jsonpath='{.clusters[0].name}') --server=$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}') --certificate-authority=k8s-ca.crt --kubeconfig=bob-k8s-config --embed-certs
ls -lt
cat bob-k8s-config

set-up-the-cluster-configuration-for-bob

Setup the user which will import Bob’s key and cert into the config file.

ls -lt
kubectl config set-credentials bob --client-certificate=bob-k8s-access.crt --client-key=bob-k8s.key --embed-certs --kubeconfig=bob-k8s-config
ls -lt
cat bob-k8s-config

set-up-the-user-bob-configuration

Create a context for "Bob's" config file using the following command.

ls -lt
kubectl config set-context bob --cluster=$(kubectl config view -o jsonpath='{.clusters[0].name}') --namespace=bob --user=bob --kubeconfig=bob-k8s-config
ls -lt
cat bob-k8s-config

set-up-the-contex-for-bob-configuration

Create a namespace for Bob

kubectl  get ns
kubectl create ns bob
kubectl  get ns -o wide
kubectl label ns bob user=bob env=sandbox
kubectl  get ns -o wide

create-a-namespace-and-label-it

Specify the context that Bob will use for his kubectl commands.

cat bob-k8s-config
kubectl config use-context bob --kubeconfig=bob-k8s-config
cat bob-k8s-config

set-current-context

Copy "bob-k8s-config" from the master node to ".kube/config" file in Bob's home directory and test Bob’s kubeconfig by running the ‘kubectl version’.

vim .kube/config #All the output of "cat bob-k8s-config" command ran on the master node and save it to /home/bob/.kube/config on the user machine.
kubectl version #Execute this on the user machine

share-the-config-file-with-bob-user

Test permissions by executing the following commands from the user machine.

kubectl  get nodes
kubectl  get pods
kubectl  get ns
kubectl  get deployments
kubectl  get all

bob-does-not-have-permissions-yet

In the above screenshot you can see that "Bob" user is unable to perform any operation as no access has been given to it.

Assign the default ‘admin’ cluster role to Bob to create most types of Kubernetes objects within his namespace. This role "bob-admin" will give admin access to "Bob" user on "bob" namespace using "admin" ClusterRole.

Eecute the following command on the master node.

kubectl create rolebinding bob-admin --namespace=bob --clusterrole=admin --user=bob 
kubectl  get rolebinding
kubectl  get clusterrole | grep  admin

create-rolebinding-with-admin-clusterrole-for-bob-namespace-only

Get namespaces created to Bob.

Now, execute all the following commands from the user machine.

kubectl  get ns
kubectl  get ns bob

validate-access-on-bob-namespace

In the above screenshot you can see the "Bob" user  is unable to list "namespace" resources.

Create a Pod in  "bob" namespace set as the default namespace in Bob's kubeconfig file. 

kubectl  run nginx --image=nginx
kubectl  get pods
kubectl  get pods -o wide

Check the current namespace set as the default namespace

kubectl config get-contexts

create-pod-in-allowed-namespace

In the above screenshot, you can see that "Bob" is able to create a Pod in "bob" namespace as we have bound "admin" role to "Bob" user for "bob" namespace.

Try to create a pod in a "default" namespace on which Bob does not have any permissions. Since we have allowed "Bob" user to be able to create objects only in "bob" namespace, Bob user will not be able to create any of the resources in any namespace other than "bob".

kubectl  run nginx-2 --image=nginx --namespace=default

pod-creation-fails-in-other-namespace

Check the namespace set as the default namespace in the kubeconfig file. This shows that "bob" namespace is set as the default namespace in the config file.

kubectl config view --minify | grep namespace:

check-current-namespace-set-as-default-namespace-in-config-file

Summary of Kubeconfig file creation

  1. Create a key and certificate signing request (CSR) using openssl.
  2. Create a CertificateSigningRequest object definition file.
  3. Create a CertificateSigningRequest object.
  4. Approve the CSR (CertificateSigningRequest).
  5. Retrieve the certificate of the CSR object.
  6. Retrieve cluster CA certificate.
  7. Set up the cluster configuration in kubeconfig file.
  8. Setup the user.
  9. Create a context.
  10. Create a namespace for the user.
  11. Specify the context in the kubeconfig file.
  12. Pass on the kubeconfig to the user.
  13. Test the permissions using the user's config file
  14. Assign the role to the user
  15. Test the permissions again using the user's config file.

Conclusion

In this article, we saw the basics of Role, RoleBinding and ClusterRole, ClusterRoleBinding, we also created these objects in our cluster. We then created a config file that allows a particular user to perform operations in a particular namespace. We saw how RBAC can help us restrict access to the Kubernetes cluster. 

Share this page:

0 Comment(s)