kubectl - Security
Quick navigation tip: use
Ctrl/Cmd + Fto jump to sections. Key terms:rbac,auth,csr,pss,security-context,pdb,quota.
RBAC - Roles and Access Control
Official documentation RBAC ↗
# List all roles in namespace
kubectl get roles
# List all ClusterRoles
kubectl get clusterroles
# List RoleBindings
kubectl get rolebindings
# List ClusterRoleBindings
kubectl get clusterrolebindings
# Describe role
kubectl describe role <role-name>
kubectl describe clusterrole <clusterrole-name>
# Create role (allows get/list pods)
kubectl create role pod-reader --verb=get,list,watch --resource=pods
# Create ClusterRole
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
# Bind role to user
kubectl create rolebinding <binding-name> --role=pod-reader --user=<username>
# Bind role to ServiceAccount
kubectl create rolebinding <binding-name> --role=pod-reader --serviceaccount=<namespace>:<sa-name>
# Bind ClusterRole to user (cluster-wide)
kubectl create clusterrolebinding <binding-name> --clusterrole=pod-reader --user=<username>
# Create ServiceAccount
kubectl create serviceaccount <sa-name>
# List ServiceAccounts
kubectl get serviceaccounts
kubectl get sa
# Describe ServiceAccount
kubectl describe sa <sa-name>
# Check own permissions
kubectl auth can-i --list
# Check specific permission
kubectl auth can-i create pods
kubectl auth can-i delete deployments -n production
# Check permissions for another user
kubectl auth can-i create pods --as=<username>
# Check permissions for ServiceAccount
kubectl auth can-i create pods --as=system:serviceaccount:<namespace>:<sa-name>
# Legacy: get ServiceAccount token from Secret (only for k8s < 1.24 clusters)
kubectl get secret $(kubectl get sa <sa-name> -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 -d
# Preferred: create a short-lived ServiceAccount token (k8s >= 1.24)
kubectl create token <sa-name>
# Create token with custom TTL
kubectl create token <sa-name> --duration=24h
Checking permissions (auth)
Official documentation kubectl auth can-i ↗
# Check if you can perform an action in the current namespace
kubectl auth can-i get pods
kubectl auth can-i create deployments
kubectl auth can-i delete secrets
# Check in a specific namespace
kubectl auth can-i get pods -n kube-system
# Check in all namespaces
kubectl auth can-i get pods --all-namespaces
# List all actions you are allowed to perform in current namespace
kubectl auth can-i --list
kubectl auth can-i --list -n staging
# Impersonate another user to check their permissions
kubectl auth can-i get pods --as dev-user
kubectl auth can-i get pods --as system:serviceaccount:default:mysa
# Impersonate a group
kubectl auth can-i get pods --as-group system:masters --as fake-user
# Check if a ServiceAccount can do something (useful for debugging workloads)
kubectl auth can-i list pods \
--as system:serviceaccount:<namespace>:<serviceaccount-name>
# Show current identity (user, groups, extra)
kubectl auth whoami
# Reconcile RBAC objects from a file (applies missing rules, non-destructive)
kubectl auth reconcile -f rbac-manifest.yaml
# Dry-run reconcile to preview changes
kubectl auth reconcile -f rbac-manifest.yaml --dry-run=client
Certificate Signing Requests (CSR)
Official documentation Certificate Signing Requests ↗
# List all CSRs in cluster
kubectl get csr
kubectl get certificatesigningrequests
# Show CSR with status and signer
kubectl get csr -o custom-columns=NAME:.metadata.name,AGE:.metadata.creationTimestamp,SIGNERNAME:.spec.signerName,REQUESTOR:.spec.username,CONDITION:.status.conditions[0].type
# Describe CSR (shows subject, usages, events)
kubectl describe csr <csr-name>
# Approve a CSR
kubectl certificate approve <csr-name>
# Deny a CSR
kubectl certificate deny <csr-name>
# Delete a CSR
kubectl delete csr <csr-name>
# Create a CSR object from a PEM file (k8s >= 1.18)
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-user
spec:
request: $(cat my-user.csr | base64 | tr -d '\n')
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400 # 1 day
usages:
- client auth
EOF
# Retrieve the signed certificate after approval
kubectl get csr <csr-name> -o jsonpath='{.status.certificate}' | base64 -d > my-user.crt
# Full workflow: generate key + CSR, submit, approve, fetch cert
# 1. Generate private key and CSR with openssl
openssl genrsa -out my-user.key 2048
openssl req -new -key my-user.key -out my-user.csr -subj "/CN=my-user/O=my-group"
# 2. Submit CSR to Kubernetes (see above)
# 3. Approve
kubectl certificate approve my-user
# 4. Fetch signed cert
kubectl get csr my-user -o jsonpath='{.status.certificate}' | base64 -d > my-user.crt
# 5. Add user to kubeconfig
kubectl config set-credentials my-user --client-key=my-user.key --client-certificate=my-user.crt --embed-certs=true
kubectl config set-context my-user-context --cluster=<cluster-name> --user=my-user
Pod Security Standards (PSS)
Official documentation Pod Security Standards ↗
# Check current Pod Security labels on a namespace
kubectl get ns <namespace> --show-labels
kubectl get ns <namespace> -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}'
# Enable enforce=baseline mode
kubectl label ns <namespace> pod-security.kubernetes.io/enforce=baseline --overwrite
# Enable enforce=restricted mode (stricter)
kubectl label ns <namespace> pod-security.kubernetes.io/enforce=restricted --overwrite
# Add warn/audit modes for soft validation
kubectl label ns <namespace> pod-security.kubernetes.io/warn=restricted --overwrite
kubectl label ns <namespace> pod-security.kubernetes.io/audit=restricted --overwrite
# Pin policy version to your cluster minor version
kubectl label ns <namespace> pod-security.kubernetes.io/enforce-version=v<cluster-minor> --overwrite
kubectl label ns <namespace> pod-security.kubernetes.io/warn-version=v<cluster-minor> --overwrite
kubectl label ns <namespace> pod-security.kubernetes.io/audit-version=v<cluster-minor> --overwrite
# Remove PSS labels from namespace
kubectl label ns <namespace> pod-security.kubernetes.io/enforce-
kubectl label ns <namespace> pod-security.kubernetes.io/warn-
kubectl label ns <namespace> pod-security.kubernetes.io/audit-
# Check warnings on apply (if policy is violated)
kubectl apply -f pod.yaml -n <namespace>
# Inspect pod securityContext quickly
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A 40 -E 'securityContext|runAsNonRoot|privileged|allowPrivilegeEscalation|capabilities'
Security Context
Official documentation Security Context ↗
# View securityContext of a running pod
kubectl get pod <pod-name> -o yaml | grep -A 20 securityContext
# Check if pods run as root across all namespaces
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{" runAsUser:"}{.spec.securityContext.runAsUser}{"\n"}{end}'
# Show uid / non-root flag per pod
kubectl get pods -n <namespace> -o custom-columns=NAME:.metadata.name,UID:.spec.securityContext.runAsUser,NON_ROOT:.spec.securityContext.runAsNonRoot
# Find privileged containers in cluster
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged == true) | "\(.metadata.namespace)/\(.metadata.name)"'
# Check hostNetwork / hostPID usage
kubectl get pods -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,HOST_NET:.spec.hostNetwork,HOST_PID:.spec.hostPID | grep -v '<none>'
# Pod-level securityContext example (applied to all containers):
# spec:
# securityContext:
# runAsNonRoot: true
# runAsUser: 1000
# runAsGroup: 3000
# fsGroup: 2000
# seccompProfile:
# type: RuntimeDefault
# Container-level securityContext example (most restrictive):
# spec:
# containers:
# - name: app
# securityContext:
# allowPrivilegeEscalation: false
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# capabilities:
# drop:
# - ALL
# add:
# - NET_BIND_SERVICE # only if port < 1024 needed
# Verify readOnlyRootFilesystem is set (write attempt will fail):
kubectl exec <pod-name> -- touch /test-write
PodDisruptionBudget (PDB)
Official documentation Pod disruptions and PDB ↗ PDB API reference ↗
# List all PDBs
kubectl get poddisruptionbudget
kubectl get pdb
# List in all namespaces
kubectl get pdb -A
# Describe PDB
kubectl describe pdb <pdb-name>
# View PDB as YAML
kubectl get pdb <pdb-name> -o yaml
# Create PDB from file
kubectl apply -f pdb.yaml
# Delete PDB
kubectl delete pdb <pdb-name>
# Show PDB with disruption allowed status
kubectl get pdb -o custom-columns=NAME:.metadata.name,MIN-AVAILABLE:.spec.minAvailable,MAX-UNAVAILABLE:.spec.maxUnavailable,ALLOWED:.status.disruptionsAllowed
# Example PDB YAML (at least 2 pods must be available):
# apiVersion: policy/v1
# kind: PodDisruptionBudget
# metadata:
# name: my-pdb
# spec:
# minAvailable: 2
# selector:
# matchLabels:
# app: my-app
# Example PDB YAML (max 1 pod unavailable at a time):
# spec:
# maxUnavailable: 1
# selector:
# matchLabels:
# app: my-app
ResourceQuota and LimitRange
Official documentation Resource Quotas ↗ LimitRange ↗
# List ResourceQuotas in namespace
kubectl get resourcequota
kubectl get quota
# List in all namespaces
kubectl get quota -A
# Describe ResourceQuota (shows used vs limit)
kubectl describe quota <quota-name>
# Create ResourceQuota from file
kubectl apply -f quota.yaml
# Delete ResourceQuota
kubectl delete quota <quota-name>
# List LimitRanges
kubectl get limitrange
kubectl get limits
# Describe LimitRange
kubectl describe limits <limitrange-name>
# Create LimitRange from file
kubectl apply -f limitrange.yaml
# Example ResourceQuota YAML:
# apiVersion: v1
# kind: ResourceQuota
# metadata:
# name: namespace-quota
# spec:
# hard:
# requests.cpu: "4"
# requests.memory: 8Gi
# limits.cpu: "8"
# limits.memory: 16Gi
# pods: "20"
# services: "10"
# persistentvolumeclaims: "5"
# Example LimitRange YAML (default limits for containers):
# apiVersion: v1
# kind: LimitRange
# metadata:
# name: container-limits
# spec:
# limits:
# - type: Container
# default:
# cpu: 500m
# memory: 256Mi
# defaultRequest:
# cpu: 100m
# memory: 128Mi
# max:
# cpu: "2"
# memory: 2Gi