Howto – cURL the kubernetes API server

Kubernetes management is abstracted via a rest API, which is served over a self signed secure connection. Frequently people run into issues when accessing this rest API because of TLS issues, in this case it can be useful to debug your connection with curl, as it will provide a detailed explanation of what is going wrong with your connection.

To have a grasp understanding on how authentication is taking place in kubernetes we will be creating a new service account with the required permission to perform our test with kubernetes.

Kubernetes is using a role based access system

Role Based Access – The Theory

Before we dive too deep, lets first understand the three pieces in a Kubernetes cluster that are needed to make role based access work. These are Subjects, Resources, and Verbs.

  • Subjects – Users or processes that need access to the Kubernetes API.
  • Resources – The k8s API objects that you’d grant access to
  • Verbs – List of actions that can be taken on a resource

These three items listed above are used in concert to grant permissions such that a user (Subject) is allowed access to take an action (verb) on an object (Resource).

Now we need to look at how we tie these three items together in Kubernetes. The first step will be to create a Role or a ClusterRole. Now both of these roles will be used to tie the Resources together with a Verb, the difference between them is that a Role is used at a namespace level whereas a ClusterRole is for the entire cluster.

Once you’ve created your Role or your Cluster Role, you’ve tied the Resource and Verb together and are only missing the Subject now. To tie the Subject to the Role, a RoleBinding or ClusterRoleBinding is needed. As you can guess the difference between a RoleBinding or a ClusterRoleBinding is whether or not its done at the namespace or for the entire Cluster, much like the Role/ClusterRole described above.

It should be noted that you can tie a ClusterRole with a RoleBinding that lives within a namespace. This enables administrators to use a common set of roles for the entire cluster and then bind them to a specific namespace for use.

So for the creation of our user account, we will create a ServiceAccount, ClusterRole and RoleBinding .

$ kubectl create serviceaccount my-service-account

Now we need to create a ClusterRole which will grant us the necessary permissions on our kubernetes cluster.

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: log-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods", "pods/log"]
  verbs: ["get", "watch", "list"]

Bind the ClusterRole to the ServiceAccount in the current namespace (eg. ‘default’).

$ kubectl create rolebinding my-service-account:log-reader --clusterrole log-reader --serviceaccount default:my-service-account

Get the Bearer Token, Certificate and API Server URL

Get the token and certificate from the ServiceAccount’s token secret for use in your API requests. This script relies on the swiss army knife of JSON parsing on the command line, jq. Start by setting the SERVICE_ACCOUNTvariable.

$ SERVICE_ACCOUNT=my-service-account

#-- Get the ServiceAccount's token Secret's name
$ SECRET=$(kubectl get serviceaccount ${SERVICE_ACCOUNT} -o json | jq -Mr '.secrets[].name | select(contains("token"))')

#-- Extract the Bearer token from the Secret and decode
$ TOKEN=$(kubectl get secret ${SECRET} -o json | jq -Mr '.data.token' | base64 -d)

#-- Extract, decode and write the ca.crt to a temporary location
$ kubectl get secret ${SECRET} -o json | jq -Mr '.data["ca.crt"]' | base64 -d > /tmp/ca.crt

#-- Get the API Server location
$ APISERVER=https://$(kubectl -n default get endpoints kubernetes --no-headers | awk '{ print $2 }')

Explore the API

You can see the API documentation at /openapi/v2

$ curl -s $APISERVER/openapi/v2  --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt | less

Use case: Get pod logs

To get the logs of a pod, first list all the pods. Select only the names with the JsonPath .items[].metadata.name.

$ curl -s $APISERVER/api/v1/namespaces/default/pods/ --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt | jq -rM '.items[].metadata.name'

Insert the desired pod name into the request path.

$ curl -s $APISERVER/api/v1/namespaces/default/pods/nginx-5dc7fbd98-hvv6s/log  --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt

That’s it! You can now explore the API using your favourite tool without proxying the server or any other tricks. Remember to assign roles as needed to access various resources.

Published by

Ronny Van den Broeck

I'm a network and system engineer for more than 20 years now. During this period I became a pro in hunting down one's and zero's, with an eager mindset to help people accomplish the same or abstract them away from the matrix.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s