Kubernetes

Kubernetes Security 101: RBAC, Network Policies, and Secrets with kubectl

August 7, 2026 Kubezilla Team 4 min read
Kubernetes security architecture diagram showing RBAC, API server, etcd secrets, NetworkPolicy between frontend and backend pods, and Pod Security Admission enforcement

Kubernetes ships with three layers of built-in security controls: RBAC for “who can do what,” NetworkPolicy for “who can talk to whom,” and Secrets plus Pod Security Admission for “what workloads are allowed to run.” This tutorial wires all three together using nothing but kubectl and plain YAML.

Architecture Overview

Kubernetes security architecture diagram showing RBAC, API server, etcd secrets, NetworkPolicy between frontend and backend pods, and Pod Security Admission enforcement

Prerequisites

# A running cluster and kubectl configured against it
kubectl version --client
kubectl cluster-info

# You'll need cluster-admin (or similar) to create RBAC objects
kubectl auth can-i create rolebindings --all-namespaces

Step 1: Create a Namespace and a Restricted ServiceAccount

kubectl create namespace production

kubectl create serviceaccount ci-deployer -n production

Step 2: Define a Role and RoleBinding (RBAC)

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: deployment-manager
rules:
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
EOF

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: ci-deployer
  namespace: production
roleRef:
  kind: Role
  name: deployment-manager
  apiGroup: rbac.authorization.k8s.io
EOF
# Verify the permissions actually took effect
kubectl auth can-i create deployments \
  --namespace production \
  --as=system:serviceaccount:production:ci-deployer

kubectl auth can-i delete secrets \
  --namespace production \
  --as=system:serviceaccount:production:ci-deployer

Step 3: Restrict Pod-to-Pod Traffic with NetworkPolicy

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-allow-from-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
EOF
# Default-deny all other ingress traffic in the namespace
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
EOF

kubectl get networkpolicy -n production

Step 4: Store Sensitive Data with Secrets

# Create a Secret from literals (never commit these to git)
kubectl create secret generic db-credentials \
  --namespace production \
  --from-literal=username=app_user \
  --from-literal=password='S3cure-Pass!'

kubectl get secret db-credentials -n production -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: production
  labels:
    app: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      serviceAccountName: ci-deployer
      containers:
      - name: backend
        image: registry.example.com/backend:1.4.0
        envFrom:
        - secretRef:
            name: db-credentials
        securityContext:
          runAsNonRoot: true
          readOnlyRootFilesystem: true
          allowPrivilegeEscalation: false

Step 5: Enforce Pod Security Admission

# Label the namespace to enforce the "restricted" policy
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

# A Pod that violates the policy is rejected at admission time
kubectl run bad-pod --image=nginx --privileged -n production
# Error from server (Forbidden): pods "bad-pod" is forbidden:
# violates PodSecurity "restricted:latest"

kubectl Security Cheat Sheet

kubectl auth can-i <verb> <resource>              # check your own permissions
kubectl auth can-i <verb> <resource> --as=<user>  # impersonate and check
kubectl get roles,rolebindings -n <namespace>      # list RBAC in a namespace
kubectl get clusterroles,clusterrolebindings        # list cluster-wide RBAC
kubectl get networkpolicy -A                        # audit NetworkPolicies
kubectl get secrets -n <namespace>                  # list Secrets (not values)
kubectl describe secret <name> -n <namespace>       # inspect metadata only
kubectl get ns --show-labels | grep pod-security     # check PSA enforcement

FAQ

Do I need a service mesh for NetworkPolicy to work? No. NetworkPolicy is enforced by your CNI plugin (such as Calico or Cilium). A service mesh adds mTLS and L7 policy on top, but is not required for basic L3/L4 network segmentation.

Are Kubernetes Secrets encrypted by default? They are base64-encoded, not encrypted, unless you enable encryption at rest for etcd. Base64 is an encoding, not encryption, so treat Secrets as sensitive regardless.

What replaced PodSecurityPolicy? Pod Security Admission, the built-in namespace-label-based controller shown in Step 5, replaced the deprecated PodSecurityPolicy API.

Summary

In this tutorial you scoped access with a Role and RoleBinding, segmented traffic with two NetworkPolicy objects, stored credentials in a Secret, and locked down the namespace with Pod Security Admission, all driven from kubectl and version-controlled YAML.

Leave a comment