It is common to work with Kubernetes resources from within internal pods. The platform is obviously a great scheduler and orchestrator, but you may have custom logic that needs to make decisions from, or decisions for, Kubernetes resources.

This post will use a simple example. Say you have the requirement to list out all of the pods, but you need to do this from pods in the cluster (maybe you have to programmatically make decisions based on the state of resources). Here’s a simple Dockerfile for a sample image…

Dockerfile

1 2 3 4 5 6 7 8 9 FROM debian:buster RUN apt update && \ apt install -y curl && \ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl && \ chmod +x ./kubectl && \ mv ./kubectl /usr/local/bin/kubectl CMD kubectl get po

This is a way to create a docker image that includes the kubectl bin. And then finally any container created from this image will just run kubectl get po .

Your instinct might be to create a pod with the following config…

pod.yaml

1 2 3 4 5 6 7 8 apiVersion: v1 kind: Pod metadata: name: internal-kubectl spec: containers: - name: internal-kubectl image: trstringer/internal-kubectl:latest

Attempting to run this pod ( $ kubectl apply -f pod.yaml ), you would see the following error from the logs…

Error from server (Forbidden): pods is forbidden: User “system:serviceaccount:default:default” cannot list resource “pods” in API group “” in the namespace “default”

Unless specified otherwise (like above), pods will run under the default service account, which is out-of-the-box for each namespace. As you can tell by the error message, default doesn’t have the right permissions to list the pods.

The underlying force that is preventing us, and that we need to configure correctly, is RBAC. The way to tell Kubernetes that we want this pod to have an identity that can list the pods is through the combination of a few different resources…

service-account.yaml

1 2 3 4 apiVersion: v1 kind: ServiceAccount metadata: name: internal-kubectl

The identity object that we want to assign to our pod will be a service account. But by itself it has no permissions. That’s where roles come in.

role.yaml

1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: modify-pods rules: - apiGroups: [""] resources: - pods verbs: - get - list - delete

The role above specifies that we want to be able to get, list, and delete pods. But we need a way to correlate our new service account with our new role. Role bindings are the bridges for that…

role-binding.yaml

1 2 3 4 5 6 7 8 9 10 11 apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: modify-pods-to-sa subjects: - kind: ServiceAccount name: internal-kubectl roleRef: kind: Role name: modify-pods apiGroup: rbac.authorization.k8s.io

This role binding connects our service account to the role that has the permissions we need. Now we just have to modify our pod config to include the service account…

pod.yaml (new)

1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Pod metadata: name: internal-kubectl spec: serviceAccountName: internal-kubectl containers: - name: internal-kubectl image: trstringer/internal-kubectl:latest

By specifying spec.serviceAccountName this changes us from using the default service account to our new one that has the correct permissions. Running our new pod we should see the correct output…

1 2 3 $ kubectl logs internal-kubectl NAME READY STATUS RESTARTS AGE internal-kubectl 1/1 Running 1 5s

Enjoy!