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

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.
