10.3. Service Accounts

A Kubernetes Service Account is an identity used by pods to interact with the Kubernetes API securely. It provides authentication for workloads running inside a cluster, enabling them to access resources such as secrets, config maps, or other API objects. By default, every pod is assigned a service account, but custom service accounts with specific permissions can be created using Role-Based Access Control (RBAC) to enforce security and least privilege principles.

Task 10.3.1: Create a Service Account

Create a file named sa.yaml and define the ServiceAccount:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: pod-reader

and apply this file using:

kubectl apply -f sa.yaml --namespace <namespace>

Task 10.3.2: Create a Role and a Rolebinding

In Kubernetes, Role-Based Access Control (RBAC) is used to manage permissions for users, applications, and system components.

  • A Role defines a set of permissions (such as reading or modifying resources) within a specific namespace. It grants access to resources like pods, services, or config maps.
  • A RoleBinding links a Role to a ServiceAccount, a user, or a group, effectively assigning the permissions defined in the Role to that entity.

In this task, we will create a Role that allows listing pods and bind it to our ServiceAccount so that it has the necessary permissions to query running pods.

Create a file named role.yaml to define a Role with permissions to list Pods:

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]

Now create a rolebinding.yaml file to bind the Role to the ServiceAccount (make sure that the namespace in subject is correctly set to your namespace):

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader-rolebinding
subjects:
  - kind: ServiceAccount
    name: pod-reader
    namespace: <namespace>
roleRef:
  kind: Role
  name: pod-reader-role
  apiGroup: rbac.authorization.k8s.io

and apply both files using:

kubectl apply -f role.yaml --namespace <namespace>
kubectl apply -f rolebinding.yaml --namespace <namespace>

Task 10.3.3: Create a Job That Lists Running Pods

And now finaly we start a Kubernetes Job thas lists all running pods. Create the job.yaml file with the following content:

---
apiVersion: batch/v1
kind: Job
metadata:
  name: list-pods-job
spec:
  template:
    spec:
      serviceAccountName: pod-reader
      containers:
      - name: kubectl-container
        image: bitnami/kubectl
        command: ["kubectl", "get", "pods", "--field-selector=status.phase=Running"]
      restartPolicy: Never
kubectl apply -f job.yaml --namespace <namespace>

Once the job runs, check the logs to see the list of running pods:

kubectl logs -l job-name=list-pods-job --namespace <namespace>

The job should list all running pods in your namespace.

Why is kubectl in the Job Using the Created Service Account?

In Kubernetes, when a Pod runs, it automatically assumes the identity of a ServiceAccount assigned to it. By default, Pods use the default ServiceAccount, which has minimal permissions. However, we explicitly assigned our pod-reader ServiceAccount to the Job using:

serviceAccountName: pod-reader

How This Works:

  1. When a pod is created, Kubernetes automatically mounts a ServiceAccount token inside the pod at /var/run/secrets/kubernetes.io/serviceaccount/token. This token is a JWT (JSON Web Token) used for authenticating with the Kubernetes API.
  2. The RoleBinding connects the pod-reader ServiceAccount to the Role that allows listing pods. When kubectl get pods runs inside the Job’s container, it authenticates using the pod-reader ServiceAccount token.
  3. The kubectl command inside the Pod is executed with the permissions granted by the Role. Since we only gave “get” and “list” permissions on Pods, the job can list Pods but not modify or delete them. This ensures least privilege access, improving security by preventing unnecessary permissions from being granted.

When kubectl runs inside a Pod, it follows Kubernetes in-cluster authentication process. Specifically, it:

  • Checks for the KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT environment variables, which are automatically set inside every Pod to point to the Kubernetes API server.
  • Looks for credentials in ~/.kube/config (like when used locally).
  • If no kubeconfig is found, it falls back to in-cluster authentication, which means it:
    • Reads the token from /var/run/secrets/kubernetes.io/serviceaccount/token
    • Uses the CA certificate at /var/run/secrets/kubernetes.io/serviceaccount/ca.crt to verify the API server
    • Identifies itself as the ServiceAccount assigned to the Pod