Kubernetes Security Audit
Perform comprehensive security audits of Kubernetes clusters, identifying vulnerabilities, misconfigurations, and compliance gaps.
Prerequisites
- kubectl configured with administrative access (kubeconfig in
~/.kube/config or specified via KUBECONFIG)
- Cluster admin or equivalent read permissions across all namespaces
- Python 3.8+ with
pyyaml, tabulate dependencies (optional, for report generation)
Audit Workflow Overview
- Cluster Access Validation - Verify connectivity and permissions
- Cluster Information Gathering - Version, nodes, namespaces inventory
- RBAC Analysis - Roles, bindings, privilege escalation paths
- Pod Security Assessment - Container privileges, security contexts
- Network Policy Review - Ingress/egress rules, default deny policies
- Secrets Management Audit - Encryption, exposure risks
- Workload Configuration Review - Resource limits, probes, images
- CIS Benchmark Checks - Alignment with Kubernetes security benchmarks
- Report Generation - Findings with severity and remediation
Step 1: Cluster Access Validation
Verify kubectl connectivity and permissions before proceeding:
# Test cluster connectivity
kubectl cluster-info
# Verify admin access
kubectl auth can-i '*' '*' --all-namespaces
# Check current context
kubectl config current-context
# List available contexts
kubectl config get-contexts
If kubectl auth can-i '' '' returns no, document limited permissions and adjust audit scope accordingly.
Step 2: Cluster Information Gathering
Collect cluster metadata for context:
# Kubernetes version (check for EOL or vulnerable versions)
kubectl version --output=yaml
# Node inventory with roles
kubectl get nodes -o wide
# Namespace inventory
kubectl get namespaces
# API resources available
kubectl api-resources --verbs=list -o name
# Cluster events (last 100, look for security-relevant)
kubectl get events -A --sort-by='.lastTimestamp' | tail -100
Version Security Check
Compare against [references/k8scveversions.md](references/k8scveversions.md) for known vulnerabilities in the running version.
Step 3: RBAC Analysis
See [references/rbacanalysis.md](references/rbacanalysis.md) for detailed RBAC security patterns.
Extract RBAC Configuration
# ClusterRoles (focus on high-privilege)
kubectl get clusterroles -o yaml > /tmp/clusterroles.yaml
# ClusterRoleBindings
kubectl get clusterrolebindings -o yaml > /tmp/clusterrolebindings.yaml
# Roles per namespace
kubectl get roles -A -o yaml > /tmp/roles.yaml
# RoleBindings per namespace
kubectl get rolebindings -A -o yaml > /tmp/rolebindings.yaml
# ServiceAccounts
kubectl get serviceaccounts -A -o yaml > /tmp/serviceaccounts.yaml
High-Risk RBAC Patterns to Flag
| Risk |
Pattern |
Detection |
| Critical |
Wildcard permissions (* on resources/verbs) |
`grep -E "resources:.\\ |
verbs:.\"` |
| Critical |
Secrets access cluster-wide |
ClusterRole with get/list secrets |
| High |
Pod exec permissions |
Verb create on pods/exec |
| High |
ServiceAccount token mounting |
automountServiceAccountToken: true |
| High |
Impersonation rights |
Verb on users, groups, serviceaccounts impersonate |
| Medium |
Excessive namespace admin |
RoleBinding to admin or edit for broad groups |
Quick RBAC Risk Detection
# Find wildcard cluster permissions
kubectl get clusterroles -o json | jq -r '.items[] | select(.rules[]?.resources[]? == "*" or .rules[]?.verbs[]? == "*") | .metadata.name'
# Find who can exec into pods
kubectl auth can-i create pods/exec --all-namespaces --list
# Find who can read secrets cluster-wide
kubectl auth can-i get secrets --all-namespaces --list
# ServiceAccounts with cluster-admin binding
kubectl get clusterrolebindings -o json | jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | select(.kind == "ServiceAccount") | "\(.namespace)/\(.name)"'
Step 4: Pod Security Assessment
Gather Pod Security Contexts
# All pods with security context details
kubectl get pods -A -o json | jq -r '
.items[] |
"\(.metadata.namespace)/\(.metadata.name): privileged=\(.spec.containers[].securityContext.privileged // false), runAsRoot=\(.spec.containers[].securityContext.runAsNonRoot // "unset"), hostNetwork=\(.spec.hostNetwork // false), hostPID=\(.spec.hostPID // false)"
'
Critical Security Flags
| Flag |
Risk |
Query |
privileged: true |
Critical - Full host access |
`kubectl get pods -A -o json \ |
jq '.items[] \ |
select(.spec.containers[].securityContext.privileged == true)'` |
hostNetwork: true |
High - Network namespace escape |
`kubectl get pods -A -o json \ |
jq '.items[] \ |
select(.spec.hostNetwork == true)'` |
hostPID: true |
High - Process namespace escape |
`kubectl get pods -A -o json \ |
jq '.items[] \ |
select(.spec.hostPID == true)'` |
hostPath volumes |
High - Host filesystem access |
`kubectl get pods -A -o json \ |
jq '.items[] \ |
select(.spec.volumes[]?.hostPath != null)'` |
runAsUser: 0 |
Medium - Container runs as root |
Check securityContext.runAsUser |
Missing readOnlyRootFilesystem |
Low - Writable container filesystem |
Default is writable |
Pod Security Standards Compliance
Check namespace labels for Pod Security Standards enforcement:
# Check PSS labels on namespaces
kubectl get namespaces -o json | jq -r '.items[] | "\(.metadata.name): enforce=\(.metadata.labels["pod-security.kubernetes.io/enforce"] // "none"), audit=\(.metadata.labels["pod-security.kubernetes.io/audit"] // "none")"'
Expected: Production namespaces should have restricted or baseline enforcement.
Step 5: Network Policy Review
See [references/networkpolicyanalysis.md](references/networkpolicyanalysis.md) for network security patterns.
Inventory Network Policies
# All NetworkPolicies
kubectl get networkpolicies -A -o yaml > /tmp/netpols.yaml
# Namespaces without any NetworkPolicy
kubectl get namespaces -o name | while read ns; do
ns_name=${ns#namespace/}
count=$(kubectl get networkpolicies -n "$ns_name" --no-headers 2>/dev/null | wc -l)
if [ "$count" -eq 0 ]; then
echo "No NetworkPolicy: $ns_name"
fi
done
Network Security Checklist
Default Deny Policy Template
Namespaces lacking this pattern are exposed:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Step 6: Secrets Management Audit
Secrets Inventory
# Count secrets by namespace and type
kubectl get secrets -A -o json | jq -r '.items | group_by(.metadata.namespace) | .[] | "\(.[0].metadata.namespace): \(length) secrets"'
# Identify secret types
kubectl get secrets -A -o json | jq -r '.items | group_by(.type) | .[] | "\(.[0].type): \(length)"'
# Find secrets mounted in pods
kubectl get pods -A -o json | jq -r '.items[] | .spec.volumes[]? | select(.secret != null) | .secret.secretName' | sort -u
Secrets Security Checklist
Check for Secrets in ConfigMaps (Anti-pattern)
# Look for base64-encoded or password-like values in ConfigMaps
kubectl get configmaps -A -o json | jq -r '.items[] | select(.data != null) | select(.data | to_entries[] | .key | test("password|secret|key|token|credential"; "i")) | "\(.metadata.namespace)/\(.metadata.name)"'
Step 7: Workload Configuration Review
Image Security
# List all container images
kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -u
# Find images without explicit tags (using :latest or no tag)
kubectl get pods -A -o json | jq -r '.items[].spec.containers[] | select(.image | test(":latest$") or (test(":") | not)) | .image' | sort -u
# Find images from untrusted registries
kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | grep -vE "^(gcr.io|docker.io|quay.io|registry.k8s.io|your-registry.com)" | sort -u
Resource Limits Check
# Pods without resource limits
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | .resources.limits == null) | "\(.metadata.namespace)/\(.metadata.name)"'
# Pods without resource requests
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | .resources.requests == null) | "\(.metadata.namespace)/\(.metadata.name)"'
Liveness/Readiness Probes
# Pods without health probes
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | (.livenessProbe == null) and (.readinessProbe == null)) | "\(.metadata.namespace)/\(.metadata.name)"'
Step 8: CIS Benchmark Quick Checks
See [references/cisbenchmarks.md](references/cisbenchmarks.md) for complete CIS Kubernetes Benchmark mapping.
Control Plane Security (if accessible)
# Check API server flags (if running as pod)
kubectl get pods -n kube-system -l component=kube-apiserver -o yaml | grep -E "anonymous-auth|enable-admission-plugins|audit-log"
# Check etcd encryption
kubectl get pods -n kube-system -l component=etcd -o yaml | grep -E "client-cert-auth|peer-client-cert-auth"
Worker Node Security
# Kubelet configuration (requires node access or kubelet API)
# Check for --anonymous-auth=false, --authorization-mode not AlwaysAllow
kubectl get nodes -o json | jq -r '.items[].status.nodeInfo'
Step 9: Generate Audit Report
Use [scripts/generatereport.py](scripts/generatereport.py) to compile findings:
python scripts/generate_report.py --kubeconfig ~/.kube/config --output audit_report.md
Report Structure
# Kubernetes Security Audit Report
**Cluster**: {context_name}
**Date**: {timestamp}
**Auditor**: Claude AI
## Executive Summary
| Category | Critical | High | Medium | Low |
|----------|----------|------|--------|-----|
| RBAC | X | X | X | X |
| Pod Security | X | X | X | X |
| Network | X | X | X | X |
| Secrets | X | X | X | X |
| Workloads | X | X | X | X |
**Overall Risk Level**: {Critical/High/Medium/Low}
## Cluster Information
- Kubernetes Version: {version}
- Nodes: {count}
- Namespaces: {count}
## Critical Findings
{findings with remediation}
## High-Risk Findings
{findings with remediation}
## Detailed Findings by Category
### RBAC Analysis
{detailed findings}
### Pod Security
{detailed findings}
### Network Policies
{detailed findings}
### Secrets Management
{detailed findings}
### Workload Configuration
{detailed findings}
## Remediation Priority Matrix
{ordered list by risk and effort}
## Appendix
- Raw data references
- Commands used
Quick Audit Commands
Run this script for a rapid security snapshot:
#!/bin/bash
echo "=== K8s Security Quick Audit ==="
echo -e "\n[1] Privileged Pods:"
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].securityContext.privileged == true) | "\(.metadata.namespace)/\(.metadata.name)"'
echo -e "\n[2] Pods with hostNetwork/hostPID:"
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.hostNetwork == true or .spec.hostPID == true) | "\(.metadata.namespace)/\(.metadata.name)"'
echo -e "\n[3] Cluster-admin ServiceAccounts:"
kubectl get clusterrolebindings -o json | jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | "\(.kind): \(.namespace // "cluster")/\(.name)"'
echo -e "\n[4] Namespaces without NetworkPolicies:"
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
[ $(kubectl get netpol -n $ns --no-headers 2>/dev/null | wc -l) -eq 0 ] && echo "$ns"
done
echo -e "\n[5] Pods with latest/untagged images:"
kubectl get pods -A -o json | jq -r '.items[].spec.containers[] | select(.image | test(":latest$") or (test(":") | not)) | .image' | sort -u
echo -e "\n[6] Pods without resource limits:"
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | .resources.limits == null) | "\(.metadata.namespace)/\(.metadata.name)"' | head -20
Reference Files
- [references/rbacanalysis.md](references/rbacanalysis.md): Deep-dive RBAC security patterns and privilege escalation paths
- [references/networkpolicyanalysis.md](references/networkpolicyanalysis.md): Network policy security patterns and templates
- [references/cisbenchmarks.md](references/cisbenchmarks.md): CIS Kubernetes Benchmark controls mapping
- [references/k8scveversions.md](references/k8scveversions.md): Known CVEs by Kubernetes version
Report Template
Use [assets/auditreporttemplate.md](assets/auditreporttemplate.md) for consistent report formatting.
Common Remediation Patterns
| Finding |
Remediation |
| Privileged pods |
Add securityContext.privileged: false, use capabilities instead |
| Missing NetworkPolicies |
Apply default-deny, then allow required traffic |
| Overly permissive RBAC |
Follow least-privilege, use namespaced Roles |
| Secrets in env vars |
Mount as volumes with defaultMode: 0400 |
| Latest image tags |
Pin to specific digests or semantic versions |
| Missing resource limits |
Define CPU/memory limits and requests |
| No Pod Security Standards |
Add pod-security.kubernetes.io/* labels to namespaces |