KubeConfig and REST API Call: Deep Dive

KubeConfig and REST API Call: Deep Dive

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 use devuser-context (i.e., authenticates as devuser).

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!!

Source

Happy Learning!! 😁