Calling the Kubernetes API with curl: Understanding the REST API Behind kubectl
- Kubernetes version: v1.35.5
- Docker Engine: v29.5.3
- Test environment: Docker Desktop built-in Kubernetes, using kind as the provisioning method, with 2 nodes
When working with Kubernetes, the tool we use most often is probably kubectl, for example:
kubectl get po
kubectl describe po <pod-name>
kubectl apply -f deployment.yaml
kubectl is very convenient because it handles many details for us, such as reading the kubeconfig, handling authentication, sending requests to the API Server, and finally formatting the response into a more readable output.
However, Kubernetes itself exposes its functionality through the API Server. Besides using kubectl, we can also interact with Kubernetes resources through client libraries or by sending REST requests directly.
Let’s start with a basic question: if we do not use kubectl get pods, can we query the Kubernetes API Server directly with curl?
The answer is: yes.
In this article, I will use a local Kubernetes cluster and a short-lived ServiceAccount token to read the Pod list from a specific namespace.
Checking kubeconfig
I am currently using the built-in Kubernetes feature provided by Docker Desktop to create a local test cluster. In the Cluster settings, I selected kind as the provisioning method. After the cluster is created, the kubeconfig is also configured automatically.
Check the current context:
kubectl config current-context
We can also check the cluster, user, and API Server endpoint used by the current context:
kubectl config view --minify
Output:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://127.0.0.1:65469
name: docker-desktop
contexts:
- context:
cluster: docker-desktop
user: docker-desktop
name: docker-desktop
current-context: docker-desktop
kind: Config
users:
- name: docker-desktop
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
serveris the address of the Kubernetes API Servercertificate-authority-datais the CA used to verify the API Server certificateclient-certificate-dataandclient-key-dataare the client certificate and key used by the current kubeconfig user
However, in this article, we will not directly use the admin client certificate from kubeconfig. Instead, we will create a ServiceAccount with limited permissions, only for reading Pods.
Creating test resources
First, create a test.yaml file:
# Create the test namespace
apiVersion: v1
kind: Namespace
metadata:
name: test
---
# Create the pod-reader ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
namespace: test
---
# Create a Role that can only read Pods in the test namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: test
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
---
# Bind the Role to the pod-reader ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader
namespace: test
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-reader
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: test
---
# Create an nginx Pod so we have something to query from the API
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: test
spec:
containers:
- name: nginx
image: nginx:1.27
Apply the resources:
kubectl apply -f test.yaml
Check whether the Pod is running:
kubectl get po -n test
Testing RBAC permissions
Next, let’s check the permissions of the pod-reader ServiceAccount.
It should be allowed to list Pods in the test namespace:
# Expected output: yes
kubectl auth can-i list pods -n test --as=system:serviceaccount:test:pod-reader
It should not be allowed to list Pods across all namespaces:
# Expected output: no
kubectl auth can-i list pods -A --as=system:serviceaccount:test:pod-reader
This means RBAC is working as expected. The pod-reader ServiceAccount is namespace-scoped, and it only receives permission to read Pods in the test namespace through the RoleBinding.
Testing the API
Now, let’s open a terminal and use curl to test the API. You can also use other tools such as Postman; the idea is the same.
Before sending the request, prepare the API Server endpoint, CA certificate, and token:
# Get the API Server endpoint used by the current kubeconfig context
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
# Save the CA from kubeconfig into a file
kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
# Create a short-lived token for the pod-reader ServiceAccount
TOKEN=$(kubectl create token pod-reader -n test --duration=10m)
List the Pods in the test namespace. Kubernetes should return a PodList:
curl --cacert ca.crt \
--header "Authorization: Bearer $TOKEN" \
-X GET "$APISERVER/api/v1/namespaces/test/pods"
Now, let’s intentionally try to list Pods across all namespaces. This request should fail with a Forbidden error:
curl --cacert ca.crt \
--header "Authorization: Bearer $TOKEN" \
-X GET "$APISERVER/api/v1/pods"
From this, we can see that kubectl get pods is not magic. Behind the scenes, it also talks to the API Server, receives a JSON response, and formats it into the table output that we usually see.