Auteur: Fabrice JAMMES (LinkedIn).
Date: Nov 18, 2025 路 10 min read
A simple lab to explore Service Accounts and their tokens in Kubernetes.
Prerequisites
A Kubernetes cluster (kind, minikube, or any cluster)
kubectl configured
jq installed (for JSON parsing)
Lab Overview
Understanding default Service Accounts
Creating custom Service Accounts
Exploring Service Account tokens (JWT)
Using tokens to authenticate
Token mounting behavior
Part 1: Default Service Account
Every namespace automatically gets a default service account.
# Create a lab namespaceNS=<ID>-$NS
kubectl create namespace $NS
# View the default service accountkubectl get serviceaccount -n $NS
kubectl describe serviceaccount default -n $NS
# Check what secrets are associated (may vary by Kubernetes version)kubectl get serviceaccount default -n $NS -o yaml
Part 2: Create a Pod with Default Service Account
# Create a simple podcat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: default-sa-pod
namespace: $NS
spec:
containers:
- name: nginx
image: nginx:alpine
command: ["sleep", "3600"]
EOF# Wait for the pod to be readykubectl wait --for=condition=ready pod/default-sa-pod -n $NS --timeout=60s
# Check which service account the pod useskubectl get pod default-sa-pod -n $NS -o jsonpath='{.spec.serviceAccountName}'echo
# View the mounted service account fileskubectl exec -it default-sa-pod -n $NS -- ls -la /var/run/secrets/kubernetes.io/serviceaccount/
What you’ll find:
token - JWT token for authentication
ca.crt - Certificate authority for the cluster
namespace - The namespace the pod is running in
Part 3: Explore the Token
# Read the tokenkubectl exec default-sa-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# Save the token to a variableTOKEN=$(kubectl exec default-sa-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)# View the tokenecho $TOKEN
# Decode the JWT payload (it's base64 encoded)# JWT format: header.payload.signatureecho $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .
# Read the namespace filekubectl exec default-sa-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
Part 4: Create Custom Service Accounts
# Create two custom service accountscat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: $NS
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitor-sa
namespace: $NS
EOF# List all service accountskubectl get serviceaccount -n $NS
# Describe a custom service accountkubectl describe serviceaccount app-sa -n $NS
Part 5: Use Custom Service Account in a Pod
# Create a pod with the custom service accountcat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: $NS
spec:
serviceAccountName: app-sa
containers:
- name: app
image: nginx:alpine
command: ["sleep", "3600"]
EOF# Wait and verifykubectl wait --for=condition=ready pod/app-pod -n $NS --timeout=60s
kubectl get pod app-pod -n $NS -o jsonpath='{.spec.serviceAccountName}'echo
# Compare tokens between podsecho "=== Default SA Token (first 50 chars) ==="kubectl exec default-sa-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -c1-50
echo ""echo "=== App SA Token (first 50 chars) ==="kubectl exec app-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -c1-50
Notice: Different service accounts have different tokens!
Part 6: Use Token from Outside the Pod
You can extract and use the token directly with kubectl.
# Get the tokenAPP_TOKEN=$(kubectl exec app-pod -n $NS -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)# Get the API server URL (adjust if needed)APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')# Try to list pods using the tokenkubectl --token="$APP_TOKEN" --server="$APISERVER" --insecure-skip-tls-verify get pods -n $NS
# This might fail with "Forbidden" because the service account has no permissions by default