The Kubernetes API call is coming from inside the cluster!

Sometimes its useful to access the Kubernetes API from inside of a Pod. Kubernetes attempts to make this easier by mounting a serviceaccount secret to each pod.

To explore this I have created a Kubernetes cluster using minikube and started an interactive alpine Linux pod:

$ minikube start
$ kubectl run -i --tty alpine --image=alpine --restart=Never -- sh
/ #

You will find several files in the path /var/run/secrets/kubernetes.io/serviceaccount that can be used to authenticate to the Kubernetes API (an auth token, a ca certificate and a namespace):

/ # ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token
/ # cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZ...
...
l25cDJo2xgnz02W-FVznBJ46A/

However none of these files provide the URL of the Kubernetes API, thankfully this is available via environment variables:

/ # env | grep KUBE
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1

Using a combination of these you will be able to authenticate and access the Kubernetes API. Unfortunately the wget that ships with alpine cannot access TLS endpoints so you’ll need to have curl or similar installed before you can access it:

/ # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
v3.7.0-46-g1061d3a8d5 [http://dl-cdn.alpinelinux.org/alpine/v3.7/main]
v3.7.0-45-g32b1d3363c [http://dl-cdn.alpinelinux.org/alpine/v3.7/community]
OK: 9046 distinct packages available
/ # apk add curl
(1/4) Installing ca-certificates (20171114-r0)
(2/4) Installing libssh2 (1.8.0-r2)
(3/4) Installing libcurl (7.57.0-r0)
(4/4) Installing curl (7.57.0-r0)
Executing busybox-1.27.2-r6.trigger
Executing ca-certificates-20171114-r0.trigger
OK: 6 MiB in 15 packages

With curl installed into our alpine container we can do some very basic API operations to find out information.

/ # K8S=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
/ # TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
/ # CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
/ # NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
/ # curl -H "Authorization: Bearer $TOKEN" --cacert $CACERT $K8S/healthz
ok
/ #

You can check to see if a service exists by asking curl to just provide the http code (200 exists, 404 doesn’t exist) which you can easily use in a while loop or as a test in a bash script:

/ # curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" --cacert $CACERT \
$K8S/api/v1/namespaces/$NAMESPACE/services/kubernetes
200
/ #/ # curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" --cacert $CACERT $K8S/api/v1/namespaces/$NAMESPACE/services/not-a-real-service
404

If you have jq installed (run apk add jq if you want to try this) you can parse the API response to get interesting information, for instance to get the IP of a given Pod:

/ # curl -s -H "Authorization: Bearer $TOKEN" \
--cacert $CACERT $K8S/api/v1/namespaces/$NAMESPACE/pods/alpine \
| jq -r ".status.podIP"
172.17.0.6

Using curl to access the Kubernetes API can be fine for basic stuff but you may need to construct more complex API queries in which case you may want to look at writing a small go binary to do your bidding or install kubectl inside your pod which knows how to use the serviceaccount secret to talk to the API.

I’ve found this to be super useful when trying to get a piece of shi^H^H^H^H^H^H legacy application working inside Kubernetes, or to create while loops in an init container to wait for a another pod or service to be set up before starting the application itself.