How to Authenticate a normal user with Client certificate in Kubernetes

How to Authenticate a normal user with Client certificate in Kubernetes

kubernetes_client_auth

Test Environment

Ubuntu 20.04
Kubernetes Cluster v1.23.1

What is Kubernetes

Kubernetes is an Open source container orchestration engine which helps in automating the deployments, scaling and management of containerized applications. Kubernetes can be used to deploy your applications into containerized environment using various different workloads and expose the application to the end user using the various netoworking features available. Today most of the monolithic application are being converted into microservice archtiecture application in which each component is develped as a separate API and deployed into the containered environment. This type of microserviced archtiecture application can be easily managed, upgraded and maintained. Kubernetes orchestration engine helps in automating these containerized application deployment, scaling, upgrading by utilizing the resources in an optimized manner.

Kubernetes API server which is a service deployed on the Master node is the component which exposes the HTTP(S) API endpoint. All the API request to manage the Kubernetes cluster go through the API server. Here in this article will see how the Kubernetes API server can authenticate and authrorize an API request before it provides access to the cluster resources.

Kubernetes API can be accessed using the following methods

  • kubectl
  • client libraries
  • rest api requests

Types of users accessing the API server

  • Human user
  • Service accounts

Supported Authentication Modules

  • Client certificates
  • Passwords
  • Static tokens
  • Bootstrap tokens
  • JSON web tokens (used for service accounts)

Here in this article we will see how we can use the Client certificates to authenticate an API request before it is sent to the authorization module which further examines the request for the permissions that are granted on the resources available in the kubernetes cluster.

If you are interested in watching the video. Here is the youtube video on the same step by step procedure.

Procedure

Step1: Create a private key

As a first step let’s generate a private which for the user with which we are going to access the kubernetes cluster resources.

kubeadmin@kubemaster:~/stack/normal_user_auth$ openssl genrsa -out devuser.key 2048

Step2: Generate CSR request

Now, let’s generate a CSR using the private key as shown below. This will generate a new CSR which needs to sent to the Certificate Authoriy which signs the certificate and provides us with the certificate and signer certificates of the certificate authority.

kubeadmin@kubemaster:~/stack/normal_user_auth$ openssl req -new -key devuser.key -out devuser.csr

Step3: Submit the CSR request to kubernetes for approval

We have generated the CSR in our last step but instead of sending it to the CA we will be generating a CSR request in Kubernetes which will be approved by the Kubernetes signing authority and provides us with the certificate.

Here is the CSR yml definition which we need to apply to submit the CSR which we generated. Note that we are converting the CSR into base64 encoded format and removing any newline characters before being used in the yml definition.

kubeadmin@kubemaster:~/stack/normal_user_auth$ cat devuser.csr | base64 | tr -d "\n"
kubeadmin@kubemaster:~/stack/normal_user_auth$ cat devuser_csr.yml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: devuser 
spec:
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ3lqQ0NBYklDQVFBd2dZUXhDekFKQmdOVkJBWVRBa2xPTVFzd0NRWURWUVFJREFKTlNERVBNQTBHQTFVRQpCd3dHVFhWdFltRnBNUkl3RUFZRFZRUUtEQWx6ZEdGamF5NWpiMjB4RHpBTkJnTlZCQXNNQm1SbGRtOXdjekVRCk1BNEdBMVVFQXd3SFpHVjJkWE5sY2pFZ01CNEdDU3FHU0liM0RRRUpBUllSWkdWMmRYTmxja0J6ZEdGamF5NWoKYjIwd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUM2WUEyb1J6YUhoVU5zUUpBRwpLOFQzblFhNmRIb0t0ZnZPUHBteUhFekFwczdVVG1mRkJnbmlyNVNnK3B4dUNQRTFUdTRRaVEwTVp5NjBLVXVYCkdoYnhvZDRmS0JOTHJncnZxOHpLc3pjeGhDZDA3UE9VZWVzcVBTRUxxRy9sVjFEOXh4MEF1dElZNFpibXRLMzIKL0t2anUvaTErbGdCRE5GN1BkQ1JhWTE3MUF2VGUrUlZrTEUxMXV0a0lYSzhMNGZYNjJYVTZwUURLQWdqdWZIMAorMjVFNldIOG42cGZwZmFRZG5xR2hESUdvTXBWQjlXVE1qUlk0T2NhVFR5UkpmdDA1NzZEaUNTZTFFRWVqZ3hTCmx4bnpXUFZGTys2YWwwcnNTSENVTGQvN3dwMGZNK0VDbG1ZSzB1aUMrcFlpMzFIM0dTNU9zR09iUUovdVFjcFcKL0hrdkFnTUJBQUdnQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQUtYTmVYWjBkZlJiU1lhVjFNMEJBVXpJbgo1RmI5c1ZtU2c5aExBbjM2MW1CREJiUmhreHNCZlNHdXNqUHBIS1hqYkZTbDE2RnJ2UVp4YWFxajJtRjBTeEd0CkZQcjZHRHF6WGcxRzRWMFlKSkd6YmFXZGttbGhtTTdxQXFyS2paM2VPcWwvVXNRU3pycHNKRFh3MVlIK2dwM1kKSmtXdmJ2eFhUMS9JZzJNNnF0eGxCNmtnWi9hNkFRQUlDbW5saHdRdUEyZ0tuZzA3M2c2Rm9JTFRCWml5aXBvNgpVdXBHNUsvZGRoSkZEbWJCYWdNVExRRW5Cc1o0aDhOQ2hmOEFpTU5KV1h1TVRvQWJhTGFmc0JhQVYwWURDY3NxCktYQm5xUXQydUJRdHcvRjRFbEhZZ3crUk01L1JUa1RlN3Z1NUh5dTB1MDMrRkNzdGhvMEhwSzZhQjhyZnZ3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400  # one day
  usages:
  - client auth 

Let’s apply the CSR yml definition and submit the CSR request for approval.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl apply -f devuser_csr.yml 
certificatesigningrequest.certificates.k8s.io/devuser created

Step4: Approve the CSR request

Here in this step, let’s first list the CSR’s that are current present and waiting for approval.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get csr
NAME        AGE   SIGNERNAME                                    REQUESTOR                 REQUESTEDDURATION   CONDITION
csr-z72z4   20h   kubernetes.io/kube-apiserver-client-kubelet   system:bootstrap:rw213t   <none>              Approved,Issued
devuser     71s   kubernetes.io/kube-apiserver-client           kubernetes-admin          24h                 Pending

Now, let’s approve the CSR which is in pending state using the below command.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl certificate approve devuser
certificatesigningrequest.certificates.k8s.io/devuser approved
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get csr
NAME        AGE    SIGNERNAME                                    REQUESTOR                 REQUESTEDDURATION   CONDITION
csr-z72z4   20h    kubernetes.io/kube-apiserver-client-kubelet   system:bootstrap:rw213t   <none>              Approved,Issued
devuser     2m7s   kubernetes.io/kube-apiserver-client           kubernetes-admin          24h                 Approved,Issued

Step5: Retrieve the certificate from the approved CSR

Once the CSR has been approved, it will attach a new certificate to the CSR which we can retrieve using the below command and save it .crt file by decoding backup the base64 encoded format of the certificate.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get csr devuser -o jsonpath='{.status.certificate}' | base64 -d > devuser.crt

Step6: Create a role

Now that we have our normal user client certificates ready, let’s create a role with the required permissions on the kubernetes cluster.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl create role developer --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods
role.rbac.authorization.k8s.io/developer created
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get role developer -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: "2021-12-24T06:49:26Z"
  name: developer
  namespace: default
  resourceVersion: "13428"
  uid: 9ef3dbb0-04c8-4c3a-acb9-89f84dfb4d5f
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - create
  - get
  - list
  - update
  - delete

Step7: Create a RoleBinding

Once the role has been created, we need to bind the new role with the user ‘devuser’ for which we generated the client certificates as shown below using rolebinding.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl create rolebinding developer-binding-devuser --role=developer --user=devuser
rolebinding.rbac.authorization.k8s.io/developer-binding-devuser created

Step8: Add user credentails to the kubeconfig

Now we completed the binding for the user with the role. We need to updated our kubectl configuraiton file which is used by the kubectl client utility to access the kubernetes cluster. We will set the credentials for our new user by providing the client certificate and private key as shown in the below command.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config set-credentials devuser --client-key=devuser.key --client-certificate=devuser.crt --embed-certs=true
User "devuser" set.

Step9: Add the new context

Once the new user credentials are stored in the kubectl configuration file, we can create a new context which defines the cluster which will be accessed by the user defined in the configuration.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config set-context devuser --cluster=kubernetes --user=devuser
Context "devuser" created.

Step10: Test the new context

Let’s now switch to the new context and test the access to the kubernetes cluster resources as defined in the roles definition which was created earlier.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config use-context devuser
Switched to context "devuser".

First we will try to list all pods in all namespaces, but this will be throwing error as this error cannot list pods from all namespaces.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get pods -A
Error from server (Forbidden): pods is forbidden: User "devuser" cannot list resource "pods" in API group "" at the cluster scope

Now, we will try to list all pods from the default namespaces and as you can see it does not show any error this time ans show that no resources are found. So its able to send the API request but could not find anything which is valid.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get pods
No resources found in default namespace.

Now, list all the available contexts as shown below.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         devuser                       kubernetes   devuser            
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

Switch back to your context with admin access once you are done with your validation.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".

As you could see once you switch the context, the * in the first column moves to the row the context you are currently active in.

kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
          devuser                       kubernetes   devuser            
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

Hope you learned something new in this article. Thank you..