Active Nerds
Module #45 · intermediate Article

ServiceAccounts vs. User Accounts: Identity, Token Auth, and Pod Authorization

ServiceAccounts are identities for pods and processes running inside Kubernetes; user accounts are for humans accessing the cluster from outside — ServiceAcc...

4 min read+15 XPPublished 2026-05-19

Quick Answer: ServiceAccounts are identities for pods and processes running inside Kubernetes; user accounts are for humans accessing the cluster from outside — ServiceAccounts are K8s objects, user accounts are external (OIDC, certs, IAM).

Detailed Answer:

# ServiceAccount — identity for a pod/process
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: production
  annotations:
    # IRSA on EKS — bind to IAM role (no static credentials needed)
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
automountServiceAccountToken: false  # Disable auto-mount if app doesn't call K8s API
                                     # Best practice: disable by default, enable explicitly
# Using a ServiceAccount in a Pod
spec:
  serviceAccountName: my-app-sa     # Assign the SA to the pod
  automountServiceAccountToken: true # Override SA-level setting if needed
  containers:
  - name: app
    image: myapp:v1
    # The SA token is automatically mounted at:
    # /var/run/secrets/kubernetes.io/serviceaccount/token
    # /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    # /var/run/secrets/kubernetes.io/serviceaccount/namespace

How Pods Use ServiceAccount Tokens:

# Inside a pod — access K8s API using mounted SA token
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)

# Call K8s API from inside pod (e.g., list configmaps in own namespace)
curl -s --cacert $CACERT \
  -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/configmaps

# This is how operators, controllers, and monitoring tools
# interact with the K8s API from inside the cluster

IRSA — The Right Way to Access AWS from Pods on EKS:

# Without IRSA — dangerous anti-pattern:
# Hardcode AWS credentials as environment variables or secrets
# Credentials can be stolen from etcd, logs, or pod introspection

# With IRSA — the correct pattern:
# 1. Create IAM role with trust policy for the ServiceAccount
# 2. Annotate ServiceAccount with IAM role ARN
# 3. Pod automatically gets temporary credentials via OIDC token exchange

# Step 1: Create IAM role trust policy
aws iam create-role \
  --role-name my-app-role \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E:sub":
            "system:serviceaccount:production:my-app-sa"
        }
      }
    }]
  }'

# Step 2: Attach permissions to IAM role
aws iam attach-role-policy \
  --role-name my-app-role \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Step 3: Annotate ServiceAccount (Terraform equivalent shown below)
kubectl annotate serviceaccount my-app-sa \
  -n production \
  eks.amazonaws.com/role-arn=arn:aws:iam::123456789012:role/my-app-role

# Verify IRSA is working from inside pod
kubectl exec -it my-pod -- \
  aws sts get-caller-identity
# Should show the IAM role ARN, not EC2 instance role
# Terraform — create IRSA role and annotate SA
module "my_app_irsa" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  version = "~> 5.0"

  role_name = "my-app-production"

  role_policy_arns = {
    s3_read = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  }

  oidc_providers = {
    main = {
      provider_arn               = module.eks.oidc_provider_arn
      namespace_service_accounts = ["production:my-app-sa"]
    }
  }
}

resource "kubernetes_service_account" "my_app" {
  metadata {
    name      = "my-app-sa"
    namespace = "production"
    annotations = {
      "eks.amazonaws.com/role-arn" = module.my_app_irsa.iam_role_arn
    }
  }
  automount_service_account_token = false
}

Key Takeaway: Every pod should have a dedicated ServiceAccount with only the permissions it needs — never use the default ServiceAccount in production, and use IRSA on EKS instead of static AWS credentials.


🔴 ADVANCED LEVEL — Questions 41–80 (mapped to Q151–250 in original plan)

Production Readiness & Enterprise Patterns


Finished reading?

Mark as complete to claim your +15 XP and track progress.