Table of Contents

Kubernetes has its own identity and access management system built on RBAC (Role-Based Access Control). Understanding K8s RBAC is essential for securing containerized workloads and implementing Zero Trust in cloud-native environments. This lab covers user authentication, service accounts, roles, bindings, and integration with cloud provider IAM systems.

Lab Overview & Kubernetes Security Model

Kubernetes security is built on multiple layers: authentication (who are you?), authorization (what can you do?), and admission control (should this be allowed?). RBAC is the standard authorization mechanism, defining what actions principals can perform on which resources.

Kubernetes Security Layers

  • Authentication: Verify identity (certificates, tokens, OIDC)
  • Authorization: Check permissions (RBAC, ABAC, Webhook)
  • Admission Control: Validate/mutate requests (Pod Security, OPA)

RBAC Core Concepts

Subjects

Users, Groups, ServiceAccounts - who is making the request

Roles

Role (namespaced) or ClusterRole (cluster-wide) - what permissions exist

Bindings

RoleBinding or ClusterRoleBinding - connects subjects to roles

Resources

pods, services, secrets, configmaps - what is being accessed

Verbs

get, list, create, update, delete, watch - what action is performed

API Groups

core, apps, batch, networking.k8s.io - resource categories

KUBERNETES RBAC FLOW

User/ServiceAccount

Subject making request

API Server

AuthN AuthZ Admission

RBAC Check

Role + RoleBinding

Resource

Allow or Deny

Module 1: Kubernetes Authentication

Module 1: Understanding K8s Authentication Methods

Learn how users and services authenticate to the Kubernetes API server.

45-60 minutes5 steps
1

Understand Authentication Methods

kubectl
Kubernetes Authentication Methods: 1. X.509 Client Certificates - Most common for cluster admins - CN = username, O = groups - Generated via CSR or external CA 2. Bearer Tokens - Service Account tokens (JWT) - Static token file (deprecated) - Bootstrap tokens 3. OIDC (OpenID Connect) - Integrate with IdPs (Okta, Azure AD, Google) - Best for human users - Supports MFA from IdP 4. Webhook Token Authentication - External auth service - Custom authentication logic 5. Authentication Proxy - External proxy adds headers - X-Remote-User, X-Remote-Group
2

View Current Context & User

kubectl
# View current context kubectl config current-context # View all contexts kubectl config get-contexts # View cluster info kubectl cluster-info # Check who you are (authentication test) kubectl auth whoami # View kubeconfig kubectl config view # View specific user details kubectl config view -o jsonpath='{.users[*].name}'
3

Create Certificate-Based User

Terminal
# Generate private key for new user openssl genrsa -out developer.key 2048 # Create Certificate Signing Request (CSR) # CN = username, O = group openssl req -new -key developer.key \ -out developer.csr \ -subj "/CN=developer/O=dev-team" # Base64 encode the CSR CSR_BASE64=$(cat developer.csr | base64 | tr -d '\n') # Create Kubernetes CSR object cat < developer.crt # View the certificate openssl x509 -in developer.crt -text -noout
4

Configure kubeconfig for New User

kubectl
# Add user credentials to kubeconfig kubectl config set-credentials developer \ --client-certificate=developer.crt \ --client-key=developer.key # Create context for user kubectl config set-context developer-context \ --cluster=$(kubectl config view -o jsonpath='{.clusters[0].name}') \ --user=developer \ --namespace=development # Test new user (will fail - no permissions yet) kubectl --context=developer-context get pods # Error: pods is forbidden: User "developer" cannot list resource "pods" # This proves authentication works, but authorization fails!
5

Configure OIDC Authentication (Concept)

YAML
# API Server flags for OIDC (kube-apiserver configuration) # --oidc-issuer-url=https://your-idp.example.com # --oidc-client-id=kubernetes # --oidc-username-claim=email # --oidc-groups-claim=groups # --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.crt # Example kubeconfig with OIDC apiVersion: v1 kind: Config users: - name: oidc-user user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: kubectl args: - oidc-login - get-token - --oidc-issuer-url=https://your-idp.example.com - --oidc-client-id=kubernetes - --oidc-client-secret=secret # Benefits of OIDC: # - Centralized user management # - MFA from IdP # - Group claims for RBAC # - Short-lived tokens

Module 2: Service Accounts

Module 2: Manage Kubernetes Service Accounts

Create and configure service accounts for pod authentication.

45-60 minutes5 steps
6

Understand Service Accounts

kubectl
Service Account Key Concepts: 1. Every namespace has a "default" service account 2. Pods use service accounts for API authentication 3. Service accounts are namespaced (unlike users) 4. Tokens are mounted as projected volumes in pods 5. K8s 1.24+: Tokens are bound, time-limited (not secrets) Service Account Token Flow: Pod Mounted Token API Server RBAC Check Resource Default behavior: - Pods get "default" SA if not specified - Token mounted at /var/run/secrets/kubernetes.io/serviceaccount/ - Contains: token, ca.crt, namespace
7

Create Service Account

kubectl
# Create namespace kubectl create namespace app-team # Create service account kubectl create serviceaccount app-backend -n app-team # Or via YAML cat <
8

Use Service Account in Pod

YAML
# Pod using specific service account cat <
9

Disable Auto-Mount (Security)

YAML
# Disable auto-mount at SA level cat <
10

Create Time-Limited Token

kubectl
# Create token with specific expiration kubectl create token app-backend \ -n app-team \ --duration=1h # Create token bound to specific audience kubectl create token app-backend \ -n app-team \ --audience=my-api \ --duration=30m # For long-running tokens (K8s 1.24+), use projected volumes: cat <

Module 3: Roles and ClusterRoles

Module 3: Define Permissions with Roles

Create Role and ClusterRole resources to define access permissions.

45-60 minutes5 steps
11

Understand Role vs ClusterRole

kubectl
Role vs ClusterRole: ROLE (Namespaced): - Defines permissions within ONE namespace - Cannot grant access to cluster-scoped resources - Use for: namespace-specific access CLUSTERROLE (Cluster-wide): - Defines permissions across ALL namespaces - Can grant access to cluster-scoped resources (nodes, PVs) - Can be bound via RoleBinding (namespace-scoped) or ClusterRoleBinding Common Verbs: - get: Read single resource - list: Read collection of resources - watch: Stream updates - create: Create new resource - update: Modify existing resource - patch: Partial update - delete: Remove resource - deletecollection: Remove multiple resources
12

Create Role (Namespaced)

YAML
# Role: Pod reader in specific namespace cat <
13

Create ClusterRole

YAML
# ClusterRole: Node viewer (cluster-scoped resource) cat <
14

Role with Resource Names

YAML
# Role limited to specific resource names cat <
15

View Built-in ClusterRoles

kubectl
# List all cluster roles kubectl get clusterroles # Important built-in roles: # cluster-admin: Full cluster access (superuser) # admin: Full access within namespace # edit: Read/write to most resources in namespace # view: Read-only access to namespace # Describe built-in role kubectl describe clusterrole admin # Describe view role kubectl describe clusterrole view # See aggregated roles (label-based) kubectl get clusterroles -l rbac.authorization.k8s.io/aggregate-to-admin=true

Module 4: RoleBindings and ClusterRoleBindings

Module 4: Bind Subjects to Roles

Create bindings to connect users, groups, and service accounts to roles.

45-60 minutes5 steps
16

Create RoleBinding

YAML
# Bind Role to User cat <
17

Create ClusterRoleBinding

YAML
# ClusterRoleBinding: Grant cluster-wide access cat <
18

RoleBinding with ClusterRole (Scoped)

YAML
# Use ClusterRole but limit to namespace via RoleBinding # This is useful for reusing common ClusterRoles! cat <
19

Test Permissions

kubectl
# Test as user (using context we created) kubectl --context=developer-context get pods -n app-team # Should work now! kubectl --context=developer-context get pods -n default # Should fail - only have access to app-team # Test what a user CAN do kubectl auth can-i list pods --as=developer -n app-team # yes kubectl auth can-i delete pods --as=developer -n app-team # no kubectl auth can-i create deployments --as=developer -n app-team # no # Test service account permissions kubectl auth can-i list pods \ --as=system:serviceaccount:app-team:app-backend \ -n app-team # yes (if bound to deployment-manager role)
20

Quick RBAC Setup with kubectl

kubectl
# Create role and binding in one command kubectl create role pod-admin \ --verb=get,list,watch,create,update,delete \ --resource=pods \ -n app-team kubectl create rolebinding pod-admin-binding \ --role=pod-admin \ --user=developer \ -n app-team # Create cluster role binding kubectl create clusterrolebinding admin-binding \ --clusterrole=cluster-admin \ --user=admin@example.com # Grant service account a role kubectl create rolebinding sa-binding \ --role=pod-reader \ --serviceaccount=app-team:app-backend \ -n app-team

Module 5: Pod Security & Security Contexts

Module 5: Secure Pod Configuration

Implement pod-level security with security contexts and Pod Security Standards.

45-60 minutes4 steps
21

Security Context Basics

YAML
# Pod with security context cat <
22

Pod Security Standards (PSS)

YAML
# Pod Security Standards define security profiles: # 1. Privileged: Unrestricted (for system pods) # 2. Baseline: Prevent known privilege escalations # 3. Restricted: Heavily restricted (best practice) # Apply Pod Security to namespace via labels cat <
23

Network Policy for Pod Isolation

YAML
# Default deny all ingress cat <
24

Limit Service Account Token Access

YAML
# Role that only allows SA to read its own token cat <

Module 6: AWS EKS IAM Integration

Module 6: IAM Roles for Service Accounts (IRSA)

Connect Kubernetes service accounts to AWS IAM roles.

45-60 minutes4 steps
25

Understand IRSA

AWS
IAM Roles for Service Accounts (IRSA): Problem: Pods need AWS permissions (S3, DynamoDB, etc.) Old solution: Node IAM role (too broad), hardcoded keys (insecure) IRSA solution: Map K8s ServiceAccount to IAM Role How it works: 1. EKS cluster has OIDC provider 2. IAM role trusts the OIDC provider 3. ServiceAccount annotated with IAM role ARN 4. Pod gets temporary AWS credentials via projected token 5. AWS SDK automatically uses these credentials Benefits: - Least privilege per pod - No long-lived credentials - Automatic credential rotation - Audit trail via CloudTrail
26

Create IAM Role for Service Account

Terminal
# Get OIDC provider URL CLUSTER_NAME="my-eks-cluster" OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME \ --query "cluster.identity.oidc.issuer" --output text | sed 's|https://||') ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) # Create trust policy cat > trust-policy.json << EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "${OIDC_PROVIDER}:sub": "system:serviceaccount:app-team:app-backend", "${OIDC_PROVIDER}:aud": "sts.amazonaws.com" } } } ] } EOF # Create IAM role aws iam create-role \ --role-name eks-app-backend-role \ --assume-role-policy-document file://trust-policy.json # Attach permissions policy aws iam attach-role-policy \ --role-name eks-app-backend-role \ --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
27

Annotate Service Account

YAML
# Update ServiceAccount with IAM role annotation cat <
28

Test AWS Access from Pod

kubectl
# Create pod with the service account cat <

Module 7: GKE Workload Identity

Module 7: GKE Workload Identity Federation

Connect Kubernetes service accounts to GCP service accounts.

45-60 minutes4 steps
29

Enable Workload Identity

Terminal
# Enable Workload Identity on existing cluster gcloud container clusters update CLUSTER_NAME \ --workload-pool=PROJECT_ID.svc.id.goog # Enable on node pool gcloud container node-pools update NODE_POOL \ --cluster=CLUSTER_NAME \ --workload-metadata=GKE_METADATA # Or create new cluster with Workload Identity gcloud container clusters create my-cluster \ --workload-pool=PROJECT_ID.svc.id.goog
30

Create GCP Service Account

Terminal
PROJECT_ID=$(gcloud config get-value project) # Create GCP service account gcloud iam service-accounts create gke-app-backend \ --display-name="GKE App Backend" GCP_SA="gke-app-backend@${PROJECT_ID}.iam.gserviceaccount.com" # Grant permissions to GCP SA gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$GCP_SA" \ --role="roles/storage.objectViewer"
31

Bind K8s SA to GCP SA

Terminal
# Allow K8s SA to impersonate GCP SA gcloud iam service-accounts add-iam-policy-binding $GCP_SA \ --role="roles/iam.workloadIdentityUser" \ --member="serviceAccount:${PROJECT_ID}.svc.id.goog[app-team/app-backend]" # Annotate K8s ServiceAccount kubectl annotate serviceaccount app-backend \ --namespace app-team \ iam.gke.io/gcp-service-account=$GCP_SA
32

Test GCP Access from Pod

kubectl
# Create test pod cat <

Module 8: Audit Logging & Security

Module 8: Monitor and Audit RBAC

Implement audit logging and security monitoring for Kubernetes.

30-45 minutes4 steps
33

Enable Audit Logging

YAML
# Audit policy (kube-apiserver configuration) apiVersion: audit.k8s.io/v1 kind: Policy rules: # Log all requests at Metadata level - level: Metadata # Log pod creation/deletion at RequestResponse level - level: RequestResponse resources: - group: "" resources: ["pods"] verbs: ["create", "delete"] # Log secrets access at Metadata level (don't log content!) - level: Metadata resources: - group: "" resources: ["secrets"] # Log RBAC changes - level: RequestResponse resources: - group: "rbac.authorization.k8s.io" resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"] # Levels: None, Metadata, Request, RequestResponse
34

Query RBAC Permissions

kubectl
# Check all permissions for a user kubectl auth can-i --list --as=developer # Check all permissions for a service account kubectl auth can-i --list \ --as=system:serviceaccount:app-team:app-backend # Check specific permission kubectl auth can-i create pods --as=developer -n app-team # Who can perform an action? (requires RBAC investigation) # Use kubectl-who-can plugin: # kubectl who-can create pods -n app-team
35

RBAC Security Scan

Terminal
# Find risky RBAC configurations # List all ClusterRoleBindings to cluster-admin kubectl get clusterrolebindings -o json | \ jq '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name' # Find service accounts with secrets access kubectl get rolebindings,clusterrolebindings -A -o json | \ jq '.items[] | select(.roleRef.name | test("secret"))' # Find pods running as root kubectl get pods -A -o json | \ jq '.items[] | select(.spec.securityContext.runAsUser==0 or .spec.containers[].securityContext.runAsUser==0) | .metadata.name' # Tools for RBAC analysis: # - kubescape # - rbac-lookup # - kubectl-who-can # - rakkess
36

Implement Least Privilege Checklist

kubectl
Kubernetes RBAC Least Privilege Checklist: Disable automountServiceAccountToken where not needed Use namespace-scoped Roles over ClusterRoles Bind to groups, not individual users Use resourceNames to limit to specific resources Avoid wildcard (*) in verbs and resources Limit cluster-admin bindings Review built-in role bindings Implement Pod Security Standards Use Network Policies for pod isolation Enable audit logging Regular RBAC reviews Use IRSA/Workload Identity for cloud access

Security Best Practices

CRITICAL

Cluster-Admin Everywhere

Too many users/SAs with cluster-admin role.

Mitigation
  • Audit all cluster-admin bindings
  • Use namespace-scoped admin instead
  • Implement JIT elevation for cluster-admin
CRITICAL

Secrets Access Too Broad

Roles with wildcard secrets access.

Mitigation
  • Use resourceNames to limit secret access
  • Use external secrets management (Vault, ESO)
  • Audit who can read secrets
HIGH

Privileged Pods

Pods running as root or with privileged security context.

Mitigation
  • Enforce Pod Security Standards
  • Use runAsNonRoot: true
  • Drop all capabilities
HIGH

Mounted Service Account Tokens

All pods have API access by default.

Mitigation
  • Set automountServiceAccountToken: false
  • Use bound, time-limited tokens
  • Apply least privilege RBAC

Kubernetes RBAC Best Practices

  • Use namespace-scoped Roles over ClusterRoles when possible
  • Bind to Groups, not individual Users
  • Avoid wildcard (*) permissions in roles
  • Disable service account token auto-mount when not needed
  • Use IRSA/Workload Identity for cloud provider access
  • Implement Pod Security Standards (restricted)
  • Enable audit logging for security events
  • Use Network Policies to isolate pods
  • Regularly audit RBAC with tools (kubescape, rbac-lookup)
  • Integrate with external IdP via OIDC for user authentication