Step-by-Step Guide: Creating a Kubernetes User for kubectl
By default, Kubernetes does not have users like Linux. Instead, authentication is managed using TLS certificates, service accounts, OpenID, etc.
If you carefully see the Kube Config file you will see that the default admin account that is created has two important fields:
controlplane $ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.30.1.2:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
client-certificate-data
client-key-data
We will see a step by step procedure to generate it and then create the context and switch to use it.
In this guide, we will create a new user named “devuser” and authenticate using a TLS certificate.
Step 1: Generate a Private Key
openssl genrsa -out devuser.key 2048
What it does?
Creates a 2048-bit RSA private key (
devuser.key
).This key will be used to generate a certificate signing request (CSR).
Step 2: Create a Certificate Signing Request (CSR)
openssl req -new -key devuser.key -out devuser.csr -subj "/CN=devuser/O=group1"
What it does?
Uses
devuser.key
to generate a certificate signing request (CSR).-subj "/CN=devuser/O=group1"
means:CN=devuser
: This is the username.O=group1
: This is the group (useful for RBAC).
Step 3: Convert CSR to Base64
cat devuser.csr | base64 | tr -d '\n'
What it does?
Converts the CSR to Base64 encoding (required for Kubernetes).
Removes newlines (
\n
) to ensure it's in a single line.
Step 4: Create a CertificateSigningRequest (CSR) in Kubernetes
Create a csr.yaml
file with the following contents:
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: devuser
spec:
request: BASE64_CSR_output
signerName: kubernetes.io/kube-apiserver-client
usages:
- client auth
Explanation:
request: BASE64_CSR
: Replace this with the Base64 CSR output from Step 3.signerName:
kubernetes.io/kube-apiserver-client
: This tells Kubernetes to sign it as a client certificate.usages: client auth
: This is used for client authentication (kubectl).
Apply the CSR request:
kubectl apply -f csr.yaml
Step 5: Approve the CSR in Kubernetes
kubectl certificate approve devuser
What it does?
Approves the certificate request (
devuser
).Kubernetes will sign it using its internal Certificate Authority (CA).
Step 6: Retrieve the Signed Certificate
kubectl get csr devuser -o jsonpath='{.status.certificate}' | base64 --decode >> devuser.crt
What it does?
Retrieves the signed certificate from Kubernetes.
Decodes it and saves it as
devuser.crt
.
Final Steps: Configure kubectl
for New User
Now that we have devuser.key
and devuser.crt
, we need to configure kubectl
to use them.
Add a New User in kubectl
Config
kubectl config set-credentials devuser --client-certificate=devuser.crt --client-key=devuser.key
What it does?
Adds a new
kubectl
user (devuser
) that will use:devuser.crt
(certificate)devuser.key
(private key)
Add a Context for the New User
kubectl config set-context devuser-context --cluster=kind-k8sexp --user=devuser
What it does?
Creates a new context (
devuser-context
) using:The existing
<CLUSTER_NAME>
(Replace this with your cluster name).The new user (
devuser
).
Switch to the New Context
kubectl config use-context devuser-context
What it does?
- Switches
kubectl
to usedevuser-context
(i.e., authenticates asdevuser
).
Now, when you run:
kubectl get pods
You may see an error like:
Error from server (Forbidden): pods is forbidden: User "devuser" cannot list resource "pods" in API group "" in the namespace "default"
Why?
By default, Kubernetes users have NO PERMISSIONS!
You need to bind an RBAC role (explained in the previous response).
Let’s create a role file for Role and RoleBinding
We will get into details of Role and Rolebinding in future blogs but for now just to understand Role tells what can be done and Rolebinding binds it to a user who can perform this.
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: saiyam
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Let’s see another interesting thing: REST API Call to API server
To create a pod through a direct REST API call to API server instead of kubectl
, you can do as shown below.
You need to first create a SA account with the correct role and role binding and Token to authenticate it against the API server.
controlplane $ kubectl create serviceaccount sam --namespace default
serviceaccount/sam created
ontrolplane $ kubectl create clusterrolebinding sam-clusteradmin-binding --clusterrole=cluster-admin --serviceaccount=default:sam
clusterrolebinding.rbac.authorization.k8s.io/sam-clusteradmin-binding created
controlplane $ kubectl create token sam
TOKEN=
controlplane $ APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
controlplane $ $APISERVER
bash: https://172.30.1.2:6443: No such file or directory
controlplane $ curl -X GET $APISERVER/apis/apps/v1/namespaces/default/deployments -H "Authorization: Bearer $TOKEN" -k
{
"kind": "DeploymentList",
"apiVersion": "apps/v1",
"metadata": {
"resourceVersion": "2455"
},
"items": []
}controlplane $ kubectl create deployment nginx --image=nginx --dry-run=client -o json > deploy.json
controlplane $ ls
deploy.json filesystem snap
controlplane $ cat deploy.json
{
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": {
"name": "nginx",
"creationTimestamp": null,
"labels": {
"app": "nginx"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "nginx"
}
},
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"app": "nginx"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx",
"resources": {}
}
]
}
},
"strategy": {}
},
"status": {}
}
This is how you can create deployment through API call:
controlplane $ curl -X POST $APISERVER/apis/apps/v1/namespaces/default/deployments \
> -H "Authorization: Bearer $TOKEN" \
> -H 'Content-Type: application/json' \
> -d @deploy.json \
> -k
You can now get the pod details too through API call. This is the output you should get and you can get each field as you would usually get through kubectl
curl -X GET $APISERVER/api/v1/namespaces/default/pods \
> -H "Authorization: Bearer $TOKEN" \
> -k
Read the output fields carefully, you would have all the details in JSON format.
Do the above and explore the topic in depth!!
Happy Learning!! 😁