Cloud & DevOps · K8s Security

Kubernetes Security Basics: RBAC, Network Policies, and Images

The highest-impact controls for safer clusters.

Reading time: ~8–12 min
Level: All levels
Updated:

Kubernetes security can feel endless, but most clusters get dramatically safer with three controls: RBAC (who can do what), NetworkPolicies (who can talk to what), and image hygiene (what code you run). This guide focuses on the highest-impact basics you can apply today—without turning your platform into a maze of YAML.


Quickstart

If you only have 30–60 minutes, do these in order. You’ll reduce your blast radius fast and create a foundation for deeper hardening later.

Fast wins (do first)

  • Stop accidental API access: set automountServiceAccountToken: false by default, and enable it only where needed.
  • Remove “wide-open” RBAC: hunt for cluster-admin bindings and wildcard rules (* verbs/resources).
  • Default-deny network traffic for one namespace (dev first), then allow only what’s required (DNS, ingress, dependencies).
  • Pin and scan images: avoid mutable tags like :latest; prefer immutable versions and scan in CI.

Guardrails that prevent regressions

  • Protect production changes: require review + CI checks for manifests.
  • Use least-privilege ServiceAccounts: one per app (or per workload class), not one shared “do-everything” token.
  • Document allowed traffic: keep an “egress map” (who needs to call what) so NetworkPolicies don’t become guesswork.
  • Harden pod runtime: run as non-root, drop Linux capabilities, read-only root filesystem where possible.

A quick audit loop to run weekly

This is not a full security assessment. It’s a “keep us honest” routine that catches the common ways clusters drift into danger.

# 1) Find ClusterRoleBindings that grant cluster-admin (highest risk)
kubectl get clusterrolebindings -o json \
  | jq -r '.items[]
    | select(.roleRef.name=="cluster-admin")
    | .metadata.name'

# 2) Spot RBAC rules using wildcards (*), often a smell unless justified
kubectl get clusterroles -o json \
  | jq -r '.items[]
    | select(.rules != null)
    | select(any(.rules[]; (.verbs|index("*")) or (.resources|index("*"))))
    | .metadata.name'

# 3) Check which namespaces have zero NetworkPolicies (default-allow networking)
kubectl get ns -o json \
  | jq -r '.items[].metadata.name' \
  | while read -r ns; do
      count=$(kubectl get netpol -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d " ")
      if [ "$count" = "0" ]; then echo "NO NetworkPolicies: $ns"; fi
    done

# 4) Quick privilege check: can a given identity do something dangerous?
# Example: can the "default" ServiceAccount in namespace "demo" list secrets?
kubectl auth can-i list secrets --as=system:serviceaccount:demo:default -n demo
Why these checks matter

Most real-world Kubernetes incidents come from over-permission (RBAC), unrestricted lateral movement (network), or unsafe supply chain choices (images). These quick checks keep those risks visible.

Overview

“Kubernetes security” isn’t one setting—it’s a set of tradeoffs about identity, traffic, and software supply chain. The good news: you can get a big risk reduction without buying a dozen tools.

What you’ll learn in this post

  • RBAC basics: Roles, ClusterRoles, bindings, and least privilege you can actually maintain.
  • NetworkPolicies: how “default allow” works, how to introduce “default deny” safely, and how to avoid breaking DNS.
  • Image hygiene: pinning, scanning, and runtime hardening so “what you run” is intentional.
  • Common mistakes: the patterns that repeatedly create security incidents in real clusters.
Control Stops Typical symptom when missing Low-friction starting point
RBAC Over-privileged API actions Service accounts can read secrets / create pods anywhere Namespace Roles + dedicated ServiceAccounts
NetworkPolicies Lateral movement between workloads Compromised pod can scan/call everything Default deny + allow DNS + allow required ingress
Image hygiene Running unknown / vulnerable code “latest” changes unexpectedly; unscanned CVEs ship to prod Pin versions/digests + scan in CI + non-root runtime
A practical definition of “secure enough”

You don’t need perfection to be safer. You need to reduce blast radius (least privilege), reduce movement (network segmentation), and reduce unknowns (image control). The rest becomes incremental improvement.

Core concepts

Before you write YAML, it helps to have a clear mental model. These concepts will keep your security configuration readable and maintainable.

RBAC: “who can do what” on the Kubernetes API

Kubernetes RBAC controls API access (create deployments, list secrets, patch configmaps, etc.). It does not control network traffic, and it does not stop a compromised app from calling other services—RBAC is about the control plane.

RBAC building blocks

  • Subject: a user, group, or ServiceAccount (identity)
  • Role / ClusterRole: a set of allowed actions (rules)
  • RoleBinding / ClusterRoleBinding: ties a subject to a role
  • Rules: verbs (get, list, watch, create, patch) + resources

Least-privilege rule of thumb

  • Prefer namespace-scoped Roles over cluster-wide permissions.
  • Prefer read-only verbs (get/list/watch) unless a workload truly needs write access.
  • Use dedicated ServiceAccounts per app/workload type; avoid using default.
  • Use small, reviewable rules (avoid * verbs/resources).

NetworkPolicies: “who can talk to what” at the pod level

By default, Kubernetes networking is typically allow all within the cluster network. NetworkPolicies let you define allowed ingress/egress for selected pods. This limits lateral movement and makes your system’s communication paths explicit.

NetworkPolicies require CNI support

NetworkPolicy resources are “just objects” unless your CNI plugin enforces them. Always validate in a dev namespace first: apply a deny policy and confirm traffic is actually blocked.

Images: “what code you run” (supply chain + runtime)

Container images are your application plus its dependencies. Image security has two sides: supply chain (where the image came from, what’s inside) and runtime (how safely it runs in the cluster). Many teams scan images but forget runtime hardening—both matter.

Topic What good looks like Common pitfall
RBAC scope Namespace Roles + minimal verbs ClusterRoleBinding to cluster-admin “temporarily”
Network segmentation Default deny + explicit allows No policies (everything can talk to everything)
Image immutability Pin versions/digests Using mutable tags like latest
Runtime hardening Non-root + drop caps + RO filesystem Privileged pods / writable root filesystem

Step-by-step

The safest way to improve Kubernetes security is to work incrementally: start in one namespace, introduce constraints, observe what breaks, then expand. This section gives you a repeatable approach.

Step 1 — Pick a pilot namespace and establish “security ownership”

Security controls fail when nobody owns them. Choose a namespace (dev or staging), assign an owner, and define what “done” means.

  • Create/choose a namespace with 1–2 services you understand.
  • Document required dependencies (DB, cache, external APIs, DNS needs).
  • Decide who reviews RBAC/NetworkPolicy changes (platform, app team, or both).

Step 2 — Replace the “default” ServiceAccount and minimize token exposure

Many clusters accidentally grant workloads Kubernetes API access they don’t need. A strong default is: don’t mount a token unless the pod must talk to the API.

What to do

  • Set automountServiceAccountToken: false on your workloads by default.
  • Create a dedicated ServiceAccount for each app that needs API calls.
  • Bind only the minimal Role required (next step).

Why it matters

If an attacker compromises a pod, the first thing they try is “what can I do with the service account token?” Minimizing tokens and privileges directly reduces post-compromise options.

Step 3 — Create a least-privilege RBAC Role (and test it)

Treat RBAC like an API contract: write the smallest possible permissions, then confirm the workload still functions. Start with read-only access where possible; add write permissions only after you can justify them.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-reader
  namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-read-config
  namespace: demo
rules:
  # Allow the app to read only what it needs: configmaps + its own pods for self-discovery/health
  - apiGroups: [""]
    resources: ["configmaps", "pods"]
    verbs: ["get", "list", "watch"]
  # Optional: allow reading endpoints/services if the app does service discovery
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-reader-binding
  namespace: demo
subjects:
  - kind: ServiceAccount
    name: app-reader
    namespace: demo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-read-config

Mini checklist: RBAC validation

  • Use kubectl auth can-i to test the exact identity (--as=system:serviceaccount:... ).
  • Avoid granting secrets access unless absolutely required (it’s often the real crown jewel).
  • Prefer namespace Roles; use ClusterRoles only for cluster-wide controllers (and document why).

Step 4 — Introduce NetworkPolicies safely: deny-by-default, allow-by-need

The safest NetworkPolicy rollout starts with a controlled blast radius: apply policies to a single namespace and focus on three traffic categories: DNS, ingress, and dependencies.

Order of operations

  1. Apply default deny (ingress + egress) to the namespace.
  2. Allow egress to DNS (or your cluster DNS namespace/labels).
  3. Allow ingress from your ingress controller namespace/labels.
  4. Allow app-to-app or app-to-DB traffic explicitly.

What breaks first (usually)

  • DNS resolution (no egress allowed yet).
  • Metrics/log shipping (egress to monitoring stack blocked).
  • Ingress routing (ingress controller can’t reach pods).
  • Service dependencies (DB/cache) missing explicit allow.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: demo
spec:
  podSelector: {} # selects all pods in the namespace
  policyTypes: ["Ingress", "Egress"]
---
# Allow DNS egress (adjust namespaceSelector/labels for your cluster DNS)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: demo
spec:
  podSelector: {}
  policyTypes: ["Egress"]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
---
# Allow ingress from an ingress controller namespace (adjust labels to match yours)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-from-ingress-controller
  namespace: demo
spec:
  podSelector: {}
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 80
        - protocol: TCP
          port: 443
Make policies readable by naming intent

Prefer names like allow-dns-egress over netpol-1. Security configs age well when a teammate can understand intent during an incident.

Step 5 — Image hygiene and runtime hardening (make “what you run” intentional)

The goal is to reduce two risks: unexpected changes (mutable tags) and unsafe runtime (privileged containers, writable root). Even without advanced admission policies, you can enforce good defaults in your Deployments.

What “good” looks like

  • Pin images: prefer immutable versions (and digests when you can).
  • Scan in CI: block known critical/high vulnerabilities based on your risk tolerance.
  • Run as non-root: set runAsNonRoot and a non-zero UID.
  • Drop Linux capabilities: keep only what’s required (often none).
  • Read-only root filesystem: write to mounted volumes (e.g., /tmp) when needed.
Scanning isn’t a shield if runtime is wide open

A scanned image can still be exploited if it runs as root, is privileged, or can write to the filesystem freely. Supply chain controls and runtime hardening work best together.

Common mistakes

Most Kubernetes security problems aren’t exotic—they’re repeats of the same handful of pitfalls. Here’s what to watch for and how to fix it without slowing delivery to a crawl.

Mistake 1 — “Just give it cluster-admin”

Cluster-admin is convenient and catastrophic. A compromised workload with cluster-admin can own your entire cluster.

  • Fix: start with a namespace Role and the smallest set of verbs/resources.
  • Fix: if a controller needs cluster scope, document why and constrain rules carefully.

Mistake 2 — Using the default ServiceAccount everywhere

Default service accounts often end up over-permissioned “by accident” over time.

  • Fix: create dedicated ServiceAccounts per workload class/app.
  • Fix: set automountServiceAccountToken: false unless API access is needed.

Mistake 3 — Assuming NetworkPolicies “just work”

Policies require enforcement by your CNI and correct selectors. Otherwise, they give a false sense of safety.

  • Fix: test with a deny policy in a dev namespace and verify traffic is actually blocked.
  • Fix: keep policy intent explicit and validate DNS/ingress after changes.

Mistake 4 — Default-deny without a rollout plan

Default-deny is powerful. Rolled out abruptly, it breaks apps and trains teams to fear security changes.

  • Fix: start with one namespace and add allows incrementally.
  • Fix: document dependencies and add policies per dependency.

Mistake 5 — Relying on mutable image tags

:latest and reused tags make incidents harder: you can’t reliably reproduce what ran yesterday.

  • Fix: pin versions and treat images as immutable artifacts.
  • Fix: prefer a promotion flow (dev → staging → prod) that doesn’t rebuild “the same tag”.

Mistake 6 — “Scanned” images, but privileged runtime

A privileged container can escape or cause major damage even if the image has no known CVEs.

  • Fix: run as non-root, drop capabilities, use read-only root filesystem when possible.
  • Fix: reserve privileged mode for rare infrastructure components only.
A simple “blast radius” test

Assume one pod is compromised. Ask: Can it read secrets? Can it talk to everything? Can it change the cluster? If the answer is “yes” to any, tighten RBAC and network policies before adding more tools.

FAQ

Do NetworkPolicies work on every Kubernetes cluster?

Not automatically. The NetworkPolicy API exists in Kubernetes, but enforcement depends on your CNI plugin. Always validate in a safe namespace by applying a deny policy and testing connectivity.

How do I test whether an RBAC role is correct?

Use kubectl auth can-i for the exact identity (user or ServiceAccount) and test the specific verbs/resources you expect. Treat RBAC rules like unit tests: start minimal, then add permissions only when you can explain why they’re required.

Should I allow workloads to read Secrets?

Only when absolutely necessary. Secrets access often becomes the “keys to the kingdom.” Prefer injecting secrets via controlled mechanisms (external secrets operators, mounted volumes, or environment variables) and avoid broad list/watch on secrets.

Is image scanning enough to secure containers?

No. Scanning reduces supply chain risk, but runtime settings (non-root, capabilities, filesystem permissions) often decide how damaging an exploit is. Combine scanning with runtime hardening to reduce both likelihood and impact.

Should I set automountServiceAccountToken: false everywhere?

It’s a strong default for application workloads that don’t need the Kubernetes API. For controllers and operators that do need API access, enable token mounting explicitly and keep RBAC minimal and well-reviewed.

What else should I consider beyond RBAC, NetworkPolicies, and images?

These three give high impact quickly, but mature clusters add admission controls, policy-as-code, and runtime monitoring. A common next step is enforcing baseline pod security standards and adding CI checks for manifests so risky settings don’t reappear.

Cheatsheet

Keep this as your quick reference when reviewing PRs, onboarding a new service, or debugging “why is traffic blocked?”

RBAC checklist (least privilege)

  • Use a dedicated ServiceAccount (avoid default).
  • Prefer namespace Role + RoleBinding.
  • Avoid * verbs/resources unless justified and reviewed.
  • Avoid secrets access; never grant list/watch on secrets casually.
  • Validate with kubectl auth can-i for the service account identity.

NetworkPolicy checklist (safe rollout)

  • Confirm your CNI enforces policies (test a deny).
  • Start in one namespace (dev/staging), not cluster-wide.
  • Default deny first, then add explicit allows.
  • Always allow DNS egress (or you will break name resolution).
  • Document dependencies and update allows with changes.

Image hygiene checklist

  • Pin versions (avoid :latest and reused tags).
  • Scan images in CI and define what severity blocks a merge.
  • Prefer smaller images (reduced attack surface).
  • Use private registry controls (allowlist registries/images where possible).
  • Plan for patching: rebuilding images should be routine, not an emergency.

Runtime hardening checklist

  • runAsNonRoot: true and a non-zero UID.
  • allowPrivilegeEscalation: false.
  • Drop all Linux capabilities (add back only if needed).
  • readOnlyRootFilesystem: true when possible.
  • Request/limit resources to reduce noisy-neighbor and DoS risks.
If you want one “north star” rule

Make the safe path the easy path: secure defaults, small exceptions, and reviews for anything that increases privileges or connectivity.

Wrap-up

Kubernetes security gets tractable when you focus on fundamentals: RBAC limits what identities can do, NetworkPolicies limit where compromised pods can go, and image hygiene ensures you run intentional, controlled software. These controls won’t eliminate every risk, but they massively reduce your blast radius and make incidents easier to contain.

Next actions (pick one and do it this week)

  • Lock down one namespace with a default-deny NetworkPolicy and explicit allows (DNS + ingress + dependencies).
  • Replace default ServiceAccounts with dedicated identities and minimal RBAC Roles.
  • Ban mutable tags in production and add image scanning to CI with clear pass/fail thresholds.
  • Introduce runtime hardening defaults (non-root, no privilege escalation, dropped capabilities).

To go deeper, explore the related posts below for Kubernetes fundamentals, GitOps workflows, CI/CD practices, and policy guardrails—these topics combine naturally with the security basics you implemented here.

Quiz

Quick self-check (demo). This quiz is auto-generated for cloud / devops / k8s.

1) What problem does Kubernetes RBAC primarily solve?
2) In most clusters, what is the default network posture without NetworkPolicies?
3) What is the safest first step when rolling out NetworkPolicies?
4) Which image practice most improves reproducibility and rollback confidence?