Kubernetes Security: RBAC, Network Policies, and Pod Security
Kubernetes security is a shared responsibility. The cluster administrator secures the infrastructure, while application teams secure their workloads. This guide covers the essential security controls you need to protect your Kubernetes environment.
📅 Published: May 2026
⏱️ Estimated Reading Time: 18 minutes
🏷️ Tags: Kubernetes Security, RBAC, Network Policies, Pod Security, DevSecOps
Introduction: The Kubernetes Security Challenge
Kubernetes is powerful, but power comes with risk. A compromised container can potentially affect other containers, access sensitive data, or even take over nodes. Unlike traditional virtual machines with strong isolation boundaries, Kubernetes relies on proper configuration and security practices.
Kubernetes security has three critical layers:
Layer 1: Authentication and Authorization (RBAC) – Who can do what. This is your front door security.
Layer 2: Network Security (Network Policies) – What traffic is allowed where. This is your internal firewall.
Layer 3: Workload Security (Pod Security) – How Pods can run. This protects your applications at runtime.
This guide covers each layer in depth, providing practical configurations and real-world examples.
Part 1: RBAC (Role-Based Access Control)
What is RBAC?
RBAC controls who has access to the Kubernetes API and what actions they can perform. It is the primary mechanism for securing the Kubernetes control plane.
Think of RBAC as a security badge system for your cluster. Each user gets a badge (authentication). The badge has certain permissions (authorization). A developer can read and create Pods. An auditor can only read resources. A cluster admin can do anything.
The Four RBAC Objects
| Object | Purpose |
|---|---|
| Role | Defines permissions within a specific Namespace |
| ClusterRole | Defines permissions cluster-wide or across all Namespaces |
| RoleBinding | Attaches a Role to a user or group within a Namespace |
| ClusterRoleBinding | Attaches a ClusterRole to a user or group cluster-wide |
RBAC Hierarchy
ClusterRoleBinding → ClusterRole (cluster-wide)
↓
RoleBinding → Role (namespace-specific)
↓
User/Group/ServiceAccountRole Example (Namespace-Specific)
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-reader namespace: development rules: - apiGroups: [""] # Core API group resources: ["pods"] # Resource type verbs: ["get", "list", "watch"] # Allowed actions - apiGroups: [""] resources: ["pods/log"] verbs: ["get", "list"]
ClusterRole Example (Cluster-Wide)
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: secret-reader rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list"]
RoleBinding Example
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: development subjects: - kind: User name: alice@example.com # User email apiGroup: rbac.authorization.k8s.io - kind: Group name: developers apiGroup: rbac.authorization.k8s.io - kind: ServiceAccount name: my-sa # Service account in this namespace roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding Example
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: read-secrets-global subjects: - kind: User name: auditor@example.com apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io
Useful Verbs
| Verb | Description |
|---|---|
| get | Retrieve a specific resource |
| list | List all resources of a type |
| watch | Watch for changes |
| create | Create new resources |
| update | Update existing resources |
| patch | Partially update resources |
| delete | Delete resources |
| deletecollection | Delete collections of resources |
Common RBAC Patterns
1. Read-only access for a namespace
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: read-only namespace: development rules: - apiGroups: ["*"] resources: ["*"] verbs: ["get", "list", "watch"]
2. Full access to a specific namespace
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: namespace-admin namespace: development rules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"]
3. Pod exec access (for debugging)
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-exec namespace: development rules: - apiGroups: [""] resources: ["pods/exec"] verbs: ["create"]
Service Account Permissions
A ServiceAccount is an identity for Pods, not humans.
apiVersion: v1 kind: ServiceAccount metadata: name: my-app-sa namespace: production --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: my-app-binding namespace: production subjects: - kind: ServiceAccount name: my-app-sa namespace: production roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
RBAC Commands
# Check if RBAC is enabled kubectl api-versions | grep rbac # List Roles kubectl get roles --all-namespaces kubectl get clusterroles # List RoleBindings kubectl get rolebindings --all-namespaces kubectl get clusterrolebindings # Check permissions for current user kubectl auth can-i create pods kubectl auth can-i delete secrets --namespace=production # Check permissions for specific user kubectl auth can-i list pods --as=alice@example.com # Describe a Role kubectl describe role pod-reader -n development
Part 2: Network Policies
What are Network Policies?
Network Policies control traffic flow between Pods and external endpoints. By default, all Pods can communicate with all other Pods. Network Policies restrict this.
Think of Network Policies as firewall rules for your Pods. You define which Pods can talk to which other Pods, and on which ports.
Network Policy Example
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: backend-policy namespace: production spec: podSelector: matchLabels: app: backend policyTypes: - Ingress - Egress ingress: - from: - podSelector: matchLabels: app: frontend - namespaceSelector: matchLabels: name: monitoring ports: - protocol: TCP port: 8080 egress: - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432 - to: - ipBlock: cidr: 10.0.0.0/8 ports: - protocol: TCP port: 443
Deny All Traffic (Default Deny)
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: production spec: podSelector: {} policyTypes: - Ingress - Egress
Allow All Traffic (Default Allow)
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all namespace: production spec: podSelector: {} ingress: - {} egress: - {} policyTypes: - Ingress - Egress
Allow Only Within Namespace
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-same-namespace spec: podSelector: {} ingress: - from: - podSelector: {} policyTypes: - Ingress
Allow from Specific Namespace
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-from-monitoring spec: podSelector: matchLabels: app: backend ingress: - from: - namespaceSelector: matchLabels: name: monitoring ports: - protocol: TCP port: 9090
Allow from Specific Pod
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-from-frontend spec: podSelector: matchLabels: app: database ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 5432
Allow to External IP (Egress)
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-to-external-api spec: podSelector: matchLabels: app: myapp egress: - to: - ipBlock: cidr: 192.168.1.0/24 ports: - protocol: TCP port: 443 policyTypes: - Egress
Network Policy Commands
# List Network Policies kubectl get networkpolicies kubectl get netpol # Describe a policy kubectl describe networkpolicy backend-policy # Delete a policy kubectl delete networkpolicy deny-all
Network Policy Requirements
Network Policies are implemented by a CNI plugin that supports them. Popular options include:
| CNI Plugin | Network Policy Support |
|---|---|
| Calico | Full support |
| Cilium | Full support |
| Weave | Full support |
| Flannel | No support |
| Amazon VPC CNI | Limited (requires Calico) |
Part 3: Pod Security
What is Pod Security?
Pod Security controls how Pods can run. It restricts privileged operations, host access, and resource usage. This is your last line of defense before a container can compromise the node.
Pod Security Standards
Kubernetes defines three Pod Security Standards:
| Standard | Description | Use Case |
|---|---|---|
| Privileged | No restrictions | System components, CI/CD runners |
| Baseline | Minimal restrictions, prevents known privilege escalations | General purpose applications |
| Restricted | Heavily restricted, follows hardening best practices | Highly sensitive workloads |
Pod Security Admission (PSA)
Pod Security Admission (PSA) is the built-in Kubernetes feature for enforcing Pod Security Standards.
apiVersion: v1 kind: Namespace metadata: name: production labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/enforce-version: latest pod-security.kubernetes.io/warn: baseline pod-security.kubernetes.io/audit: restricted
Labels explained:
| Label | Purpose |
|---|---|
| enforce | Violations block Pod creation |
| warn | Violations emit warnings (non-blocking) |
| audit | Violations are recorded in audit logs |
What Restricted Mode Enforces
# Restricted mode prevents: - privileged: true - hostPID: true - hostIPC: true - hostNetwork: true - allowPrivilegeEscalation: true - readOnlyRootFilesystem: false - seccompProfile: Unconfined
Example: Pod That Complies with Restricted Mode
apiVersion: v1 kind: Pod metadata: name: secure-pod namespace: production spec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: nginx securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL
Pod SecurityContext
SecurityContext applies to a Pod or individual container.
apiVersion: v1 kind: Pod metadata: name: secure-pod spec: securityContext: # Pod-level (applies to all containers) runAsNonRoot: true runAsUser: 1000 fsGroup: 2000 seccompProfile: type: RuntimeDefault containers: - name: app image: myapp securityContext: # Container-level (overrides Pod-level) allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] add: ["NET_BIND_SERVICE"]
Pod SecurityContext Fields
| Field | Purpose |
|---|---|
| privileged | Run as privileged container (dangerous) |
| runAsUser | User ID to run processes |
| runAsGroup | Group ID to run processes |
| runAsNonRoot | Prevent running as root |
| fsGroup | Filesystem group for volumes |
| allowPrivilegeEscalation | Prevent gaining more privileges |
| readOnlyRootFilesystem | Make root filesystem read-only |
| capabilities | Linux capabilities (drop/add) |
| seccompProfile | Seccomp profile for system calls |
Linux Capabilities
securityContext: capabilities: drop: # Remove dangerous capabilities - ALL add: # Add only needed capabilities - NET_BIND_SERVICE # Bind to ports below 1024 - CHOWN # Change file ownership
Pod Security Admission (PSA) Commands
# Check what a namespace enforces kubectl get namespace production -o yaml | grep pod-security # Test if a Pod would be allowed kubectl apply --dry-run=server -f pod.yaml # Evaluate namespace against standard kubectl label namespace production pod-security.kubernetes.gov/check-strict=restricted
Real-World Security Scenarios
Scenario 1: Multi-Tenant Cluster
A cluster serves three teams: frontend, backend, and data. Each team needs isolation.
# Create namespaces per team kubectl create namespace frontend kubectl create namespace backend kubectl create namespace data # Apply RBAC per namespace apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: team-admin namespace: frontend rules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: frontend-binding namespace: frontend subjects: - kind: Group name: frontend-team roleRef: kind: Role name: team-admin apiGroup: rbac.authorization.k8s.io # Apply Network Policies to isolate namespaces apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-cross-namespace namespace: frontend spec: podSelector: {} ingress: - from: - podSelector: {} policyTypes: - Ingress
Scenario 2: Secure Database Access
A database Pod should only accept traffic from the application Pod.
# Database Pod with restrictive Pod Security apiVersion: v1 kind: Pod metadata: name: postgres labels: app: database spec: securityContext: runAsNonRoot: true runAsUser: 999 seccompProfile: type: RuntimeDefault containers: - name: postgres image: postgres:15 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] --- # Network Policy: Only allow app Pods apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: database-policy spec: podSelector: matchLabels: app: database ingress: - from: - podSelector: matchLabels: app: webapp ports: - protocol: TCP port: 5432 policyTypes: - Ingress --- # RBAC: Only service account can access apiVersion: v1 kind: ServiceAccount metadata: name: database-sa --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: database-binding subjects: - kind: ServiceAccount name: webapp-sa roleRef: kind: Role name: database-access
Scenario 3: Pod Security Enforcement
Production namespace requires Restricted security standard.
# Namespace with enforce=restricted apiVersion: v1 kind: Namespace metadata: name: production labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/warn: baseline pod-security.kubernetes.io/audit: restricted # This Pod will be BLOCKED in production apiVersion: v1 kind: Pod metadata: name: unsafe-pod namespace: production spec: securityContext: privileged: true # ← Violation! runAsUser: 0 # ← Violation! containers: - name: app image: nginx
Scenario 4: Service Account for CI/CD
A CI/CD pipeline needs to deploy applications.
# Create service account apiVersion: v1 kind: ServiceAccount metadata: name: ci-cd-sa namespace: development # Grant deployment permissions apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: ci-cd-role namespace: development rules: - apiGroups: ["apps"] resources: ["deployments", "statefulsets"] verbs: ["get", "list", "create", "update", "delete"] - apiGroups: [""] resources: ["services", "configmaps", "secrets"] verbs: ["get", "list", "create", "update"] # Bind to service account apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: ci-cd-binding namespace: development subjects: - kind: ServiceAccount name: ci-cd-sa roleRef: kind: Role name: ci-cd-role apiGroup: rbac.authorization.k8s.io
Summary
| Security Layer | Purpose | Key Features |
|---|---|---|
| RBAC | Who can do what | Roles, ClusterRoles, RoleBindings, ClusterRoleBindings |
| Network Policies | What traffic is allowed | Ingress rules, Egress rules, Pod selectors, Namespace selectors |
| Pod Security | How Pods can run | SecurityContext, RunAsNonRoot, Capabilities, Seccomp, Pod Security Admission |
Security Checklist
RBAC
Default service account has minimal permissions
Users assigned to groups, not individual permissions
Service accounts for Pods, not default
Regular audit of RBAC permissions
Network Policies
Default deny all Network Policy applied
Ingress policies defined for each service
Egress policies for external communication
Cross-namespace communication restricted
Pod Security
Run as non-root user
Read-only root filesystem
Drop all capabilities, add only needed
Seccomp profile set to RuntimeDefault
Pod Security Admission enforced on namespaces
Practice Questions
What is the difference between a Role and a ClusterRole?
How does a Network Policy differ from a firewall rule in a load balancer?
Why should you drop all capabilities and add only needed ones?
What happens when you set runAsNonRoot: true but the container image runs as root?
How would you prevent Pods in the development namespace from accessing Pods in the production namespace?
Learn More
Practice Kubernetes security with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/
Comments
Post a Comment