How to use service account tokens for Authenticating Subjects 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 containerised applications. Kubernetes can be used to deploy your applications into containerised environment using various different workloads and expose the application to the end user using the various networking features available. Today most of the monolithic application are being converted into microservice architecture application in which each component is developed as a separate API and deployed into the containerised environment. This type of micro serviced architecture application can be easily managed, upgraded and maintained. Kubernetes orchestration engine helps in automating these containerised application deployment, scaling, upgrading by utilising the resources in an optimised 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 are
- 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 Bearer token 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 outline below.
Procedure
Step1: Create a Service Account
In this step we are going to create a service account. service accounts are used by the processes in containers inside the pods to communication with API server.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl create sa bearertokenauth
serviceaccount/bearertokenauth created
Step2: Retrieve the Bearer token from the Service Account
When a service account is created in kubernetes, it automatically associates a secret token (ie. Bearer token). Let us find identify the secret token name with the below command.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get sa bearertokenauth -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2021-12-24T07:59:05Z"
name: bearertokenauth
namespace: default
resourceVersion: "18430"
uid: d15ac857-1034-4341-b339-c99998e07d22
secrets:
- name: bearertokenauth-token-fvmmd
Now, lets extract the secret token from the secret named ‘bearertokenauth-token-fvmmd’ and decode it using the base64.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get secret bearertokenauth-token-fvmmd -o jsonpath='{.data.token}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2b2lwY29ibURNcVBKc2J5bWlicWNVd1RBejRsVFZQV2ZzeXk2d3lJcDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImJlYXJlcnRva2VuYXV0aC10b2tlbi1mdm1tZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJiZWFyZXJ0b2tlbmF1dGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkMTVhYzg1Ny0xMDM0LTQzNDEtYjMzOS1jOTk5OThlMDdkMjIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpiZWFyZXJ0b2tlbmF1dGgifQ.qdP1NHUAhTVUJ_kEpxh6qPQOSW1oTWHno_RZqw6EvgVgNS1hHLj_o4NDnb0Ge01B22Gh88YQhV001GS1OK9diHYZleWJUMqfTd9J3DTk6pYnUJ659DIMggLvUaO51W4P37ioYTdY7mY9tsVdhM3zoknUSw81P5aOBLzB6au3j5VO3htO5mUJei4jC70CiluGR8SvhgvjP7IRrjuFIEqOPdNUDeEQMYn6HwIjq5xPrd317lmX589wL6PMeJZVGNAnNWvvNFXtBR0CproEEbQ4Bq180Nw6ZfCi3x2rDcANwu13HczpoGR2kwPd-zteDtRiiKp3hcuARlR6OJMiO_42xQ
Step3: Add the ‘bearertokenauth’ credentials to kubeconfig
Here we are going to add the decoded bearer token into the kubectl configuration file as shown below.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config set-credentials bearertokenauth --token=eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2b2lwY29ibURNcVBKc2J5bWlicWNVd1RBejRsVFZQV2ZzeXk2d3lJcDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImJlYXJlcnRva2VuYXV0aC10b2tlbi1mdm1tZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJiZWFyZXJ0b2tlbmF1dGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkMTVhYzg1Ny0xMDM0LTQzNDEtYjMzOS1jOTk5OThlMDdkMjIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpiZWFyZXJ0b2tlbmF1dGgifQ.qdP1NHUAhTVUJ_kEpxh6qPQOSW1oTWHno_RZqw6EvgVgNS1hHLj_o4NDnb0Ge01B22Gh88YQhV001GS1OK9diHYZleWJUMqfTd9J3DTk6pYnUJ659DIMggLvUaO51W4P37ioYTdY7mY9tsVdhM3zoknUSw81P5aOBLzB6au3j5VO3htO5mUJei4jC70CiluGR8SvhgvjP7IRrjuFIEqOPdNUDeEQMYn6HwIjq5xPrd317lmX589wL6PMeJZVGNAnNWvvNFXtBR0CproEEbQ4Bq180Nw6ZfCi3x2rDcANwu13HczpoGR2kwPd-zteDtRiiKp3hcuARlR6OJMiO_42xQ
User "bearertokenauth" set.
Step4: Create a new context for the ‘bearertokenauth’
Now, lets create a new context with the user ‘bearertokenauth’ provided access to the cluster ‘kubernetes’ as shown below.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config set-context bearertokenauth --cluster=kubernetes --user=bearertokenauth
Context "bearertokenauth" created.
Step5: Switch to the new context to validate the access
Let’s now switch to the new ‘bearertokenauth’ context and try to access the kubernetes cluster resources.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config use-context bearertokenauth
Switched to context "bearertokenauth".
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get pods
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:bearertokenauth" cannot list resource "pods" in API group "" in the namespace "default"
As you can see the service account does not have access to list the pods in default namespace. Let’s create an role and rolebinding to attach required permissions to the service account in the next steps.
Step6: Create a role
Switch the context to kubernetes-admin@kubernetes ( ie. the default one that is setup when kubernetes cluster is provisioned) with the required privileges.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
Now let us create a role which provides the below level of access to the pods as shown below.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl create role bearertokenauth --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods
role.rbac.authorization.k8s.io/bearertokenauth created
kubeadmin@kubemaster:~/stack/normal_user_auth$
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get role bearertokenauth -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: "2021-12-24T08:27:02Z"
name: bearertokenauth
namespace: default
resourceVersion: "20426"
uid: 45bd0cb8-130d-4976-8bb8-9e6c255c12e8
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- create
- get
- list
- update
- delete
Step7: Create a RoleBinding
Now, lets create a rolebinding to bind the new role ‘bearertokenauth’ with serviceaccount ‘default:bearertokenauth’. Note, you need to append the namespace to which the service account belongs.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl create rolebinding bearertoken-binding-bearertokenauth --role=bearertokenauth --serviceaccount=default:bearertokenauth --namespace=default
rolebinding.rbac.authorization.k8s.io/bearertoken-binding-bearertokenauth created
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get rolebinding bearertoken-binding-bearertokenauth -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: "2021-12-24T08:30:27Z"
name: bearertoken-binding-bearertokenauth
namespace: default
resourceVersion: "20671"
uid: affbdd45-d77c-4443-8760-2fafffad8515
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: bearertokenauth
subjects:
- kind: ServiceAccount
name: bearertokenauth
namespace: default
Step8: Switch to the ‘bearertokenauth’ context and validate access
Again, now let’s switch to the new context ‘bearertokenauth’ which we created earlier and try to list the pods in the default namespace. As you can see now we are not getting the forbidden error anymore. That means we are not able to access the pods resources in the default namespace. Unfortunately i didn’t created any pods in the default namspace so the output is empty. You can create some pods in the default namespace on your end and should be able to see them in the output on your end.
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl config use-context bearertokenauth
Switched to context "bearertokenauth".
kubeadmin@kubemaster:~/stack/normal_user_auth$ kubectl get pods
No resources found in default namespace.
Step9: Bash script to emulate the above procedure
Here is a very basic bash script to emulate the above procedure that we discussed. This script accepts one argument that is the serviceaccount name which we need to pass while running the script. The script execution pauses at each step waiting for you to hit enter to proceed to the next step.
kubeadmin@kubemaster:~/stack/kubernetes_bearertoken_auth$ cat bearertokeauth.sh
#!/bin/bash
serviceaccountName=$1
# Create service account
echo "==========================================================================================================="
echo "kubectl create sa $serviceaccountName"
echo "==========================================================================================================="
kubectl create sa $serviceaccountName
read -p "Press enter to continue"
# Retrieve the bearer token
echo "==========================================================================================================="
echo "kubectl get sa $serviceaccountName -o jsonpath='{.secrets[0].name}'"
echo "kubectl get secret $secretName -o jsonpath='{.data.token}' | base64 -d"
echo "==========================================================================================================="
secretName=`kubectl get sa $serviceaccountName -o jsonpath='{.secrets[0].name}'`
tokenID=`kubectl get secret $secretName -o jsonpath='{.data.token}' | base64 -d`
read -p "Press enter to continue"
# Update kubectl configuration
echo "==========================================================================================================="
echo "kubectl config set-credentials $serviceaccountName --token=$tokenID"
echo "kubectl config set-context $serviceaccountName --cluster=kubernetes --user=$serviceaccountName"
echo "==========================================================================================================="
kubectl config set-credentials $serviceaccountName --token=$tokenID
kubectl config set-context $serviceaccountName --cluster=kubernetes --user=$serviceaccountName
read -p "Press enter to continue"
# Create a role and rolebinding
echo "==========================================================================================================="
echo "kubectl create role $serviceaccountName --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods"
echo "kubectl create rolebinding bearertoken-binding-$serviceaccountName --role=$serviceaccountName --serviceaccount=default:$serviceaccountName --namespace=default"
echo "==========================================================================================================="
kubectl create role $serviceaccountName --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods
kubectl create rolebinding bearertoken-binding-$serviceaccountName --role=$serviceaccountName --serviceaccount=default:$serviceaccountName --namespace=default
read -p "Press enter to continue"
# Validate the access using bearer token
echo "==========================================================================================================="
echo "kubectl config use-context $serviceaccountName"
echo "kubectl get pods"
echo "==========================================================================================================="
kubectl config use-context $serviceaccountName
kubectl get pods
kubeadmin@kubemaster:~/stack/kubernetes_bearertoken_auth$ ./bearertokeauth.sh satestauth
Hope you learned something new in this article. Thank you..
Leave a Reply
You must be logged in to post a comment.