Cloud Tech by Victor
SecurityAdvanced

Kubernetes Security

How RBAC's default-deny, additive model, Pod Security Standards, and NetworkPolicy's default-allow-until-selected behavior fit together, and why each one surprises people coming from a simpler permissions model.

Updated 2026-07-243 min read

Overview

Kubernetes security rests on three independent mechanisms that each surprise people used to a simpler model: RBAC is default-deny and purely additive, permissions can only be granted, never explicitly denied, so restricting access means removing a binding, not layering a deny rule on top. Pod Security Standards define three cumulative levels, Privileged, Baseline, Restricted, each adding real hardening (non-root, no privilege escalation, dropped capabilities) on top of the last. NetworkPolicy inverts the surprise the other way: pods are wide open by default with no policy at all, and only become isolated the moment any policy selects them, which is why the very first NetworkPolicy applied to a namespace is the one most likely to unexpectedly break something.

Quick Reference

MechanismDefault without any policyWhat applying one does
RBACDeny everythingGrants are additive; there's no explicit deny
Pod Security StandardsNo pod-level restrictionBaseline blocks known escalations; Restricted adds real hardening
NetworkPolicyPods are non-isolated, all traffic allowedSelected pods become isolated; only explicitly allowed traffic gets through

Syntax

yaml
# RBAC - grants are additive; to restrict access, remove the
# binding, there's no "deny" rule to add on top of it.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
  - kind: ServiceAccount
    name: ci-deploy
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Examples

yaml
# NetworkPolicy - the moment this selects role=db pods, they
# become isolated for ingress; only traffic matching this rule
# is allowed in, everything else that used to reach them is cut off.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-api-only
spec:
  podSelector:
    matchLabels: { role: db }
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - podSelector:
            matchLabels: { role: api }

The first NetworkPolicy in a namespace is the riskiest one

Pods are non-isolated until some policy selects them. Applying the first NetworkPolicy to a namespace can silently cut off traffic nobody ever had to declare before, because it was simply allowed by default. Test in staging before rolling NetworkPolicy into a previously policy-free namespace.

Visual Diagram

Common Mistakes

  • Trying to write an RBAC "deny" rule; RBAC has no such concept, restricting access means removing the grant, not layering a denial on top of it.
  • Applying the Restricted Pod Security Standard cluster-wide without testing, breaking workloads that legitimately needed a capability Baseline still allowed.
  • Assuming pods are isolated by default and skipping NetworkPolicy entirely, when the actual default is fully open pod-to-pod traffic.
  • Applying a single NetworkPolicy to a namespace and being surprised when unrelated traffic breaks, not realizing that policy selected pods well beyond its intended scope.

Performance

  • RBAC authorization checks happen on the API request path but are lightweight lookups against already-loaded bindings; the real cost is the audit and design effort of keeping bindings correct, not runtime latency.
  • NetworkPolicy enforcement is implemented by the cluster's CNI plugin, not the API server itself, and its performance characteristics (rule-matching overhead per packet) vary by CNI implementation and policy count.

Best Practices

  • Scope RBAC bindings as narrowly as possible (a Role in one namespace) and reserve ClusterRole/ClusterRoleBinding for genuinely cluster-wide needs.
  • Apply the Restricted Pod Security Standard to namespaces running security-critical or lower-trust workloads, and Baseline as the sane default everywhere else.
  • Introduce NetworkPolicy deliberately and incrementally, starting with a namespace's own traffic map, rather than applying a broad policy and reacting to what breaks.
  • Default every namespace to at least a basic deny-by-default NetworkPolicy plus explicit allows, rather than leaving pod-to-pod traffic fully open indefinitely.

Interview questions

How does Kubernetes RBAC decide whether a request is allowed, and can you write an explicit deny rule?

RBAC is default-deny and purely additive: a request is denied unless some Role/RoleBinding or ClusterRole/ClusterRoleBinding explicitly grants it, and there is no such thing as an explicit deny rule. Every applicable binding's permissions are unioned together, so restricting access means removing a grant (or removing the subject from a binding), not adding a deny statement on top of an existing grant, an approach that works cleanly for grant-based access but has no mechanism for "allow everything except X" within RBAC itself.

What is the difference between the Baseline and Restricted Pod Security Standards levels, and why are they cumulative?

Baseline blocks the most well-known container privilege-escalation paths, privileged containers, host namespaces, hostPath volumes, dangerous Linux capabilities, while still allowing a fairly permissive pod spec otherwise. Restricted inherits every Baseline rule and adds real hardening on top: it requires running as non-root, forbids privilege escalation outright, requires a restricted seccomp profile, and requires dropping all Linux capabilities except NET_BIND_SERVICE. A read-only root filesystem is not part of either standard, it's a separate hardening measure some organizations layer on as their own policy, on top of, not as part of, Restricted. They're cumulative by design, Restricted is Baseline plus more, so a workload that passes Restricted automatically satisfies Baseline too, and a cluster can apply different levels per namespace based on how much a given workload can be trusted.

If no NetworkPolicy exists in a namespace, what traffic is allowed between pods, and what changes the moment one NetworkPolicy is applied?

With no NetworkPolicy at all, pods are non-isolated: every pod can send and receive traffic from any other pod, with no restriction in either direction. The moment any NetworkPolicy selects a pod for a given direction (ingress or egress), that pod becomes isolated for that direction specifically, and only the traffic explicitly allowed by an applicable policy's rules gets through from then on; unrelated pods elsewhere in the cluster that no policy selects remain fully open. This is why introducing NetworkPolicy incrementally, rather than all at once, tends to break things: the first policy applied to a namespace can silently cut off traffic nobody had previously needed to declare.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement