How to Authenticate a normal user with Client certificate in Kubernetes
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..
Leave a Reply
You must be logged in to post a comment.