Safeguard
Containers

How to Write a Kubernetes Security Policy That Holds Up

A Kubernetes security policy is the set of enforced rules that decide what workloads may run and how. This guide covers Pod Security Standards, admission control, and turning intent into rules the cluster actually enforces.

Karan Patel
Platform Engineer
5 min read

A Kubernetes security policy is the enforced set of rules that governs what workloads are allowed to run and under what constraints, and the only version that matters is the one the cluster actually enforces at admission time, not the one written in a wiki. A policy document nobody enforces is a suggestion. This guide is about closing the gap between intent and enforcement, using the built-in Pod Security Standards and, where you need more, a policy engine at the admission layer.

Policy is only real when it is enforced

The trap teams fall into is writing a thoughtful security standard, sharing it in a doc, and assuming compliance. Developers under deadline pressure will not read it, and even those who do will make mistakes. The fix is to move the policy into the admission path, where the API server checks every incoming object against the rules and rejects what violates them. If your policy cannot answer "what happens when someone submits a pod that breaks this rule," it is not yet a Kubernetes security policy; it is a wish.

Start with Pod Security Standards

Since PodSecurityPolicy was removed in Kubernetes 1.25, the built-in successor is Pod Security Admission, which enforces the three Pod Security Standards levels: privileged, baseline, and restricted. You apply them per namespace with labels, and you can run each level in enforce, audit, or warn mode independently.

apiVersion: v1
kind: Namespace
metadata:
  name: payments
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/warn: restricted

The restricted profile disallows privilege escalation, requires running as non-root, drops all capabilities, and blocks host namespaces. For most application workloads it is the right default. Roll it out in warn mode first so developers see what would be rejected, then flip to enforce once the noise is gone.

When built-in standards are not enough

Pod Security Standards cover pod-level security context, but they cannot express rules like "images must come from our registry," "every pod must carry a cost-center label," or "no LoadBalancer services in staging." For those you need a general policy engine at the admission webhook: OPA Gatekeeper or Kyverno are the two common choices.

Kyverno policies are written in YAML and tend to be easier for teams already fluent in Kubernetes manifests. Here is a policy that blocks images from any registry except your own:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: allowed-registries
spec:
  validationFailureAction: Enforce
  rules:
    - name: only-internal-registry
      match:
        any:
          - resources:
              kinds: ["Pod"]
      validate:
        message: "Images must come from registry.internal.example.com"
        pattern:
          spec:
            containers:
              - image: "registry.internal.example.com/*"

OPA Gatekeeper uses Rego, which is more expressive and steeper to learn. Pick based on the complexity of the rules you need and your team's appetite for a new query language.

Cover the whole surface, not just pods

A complete Kubernetes security policy addresses more than pod security context. RBAC belongs in scope: define who can create workloads in which namespaces, and forbid wildcard roles. Network policy belongs in scope: default-deny traffic and require explicit allows. Image provenance belongs in scope: require signed images and verify signatures at admission. Resource limits belong in scope too, because an unbounded workload is an availability risk. Treat these as the sections of one policy, each with a matching enforcement mechanism.

For the specific misconfigurations these rules are meant to prevent, our rundown of Kubernetes security issues is a useful companion.

Write policy as code and version it

Because enforcement lives in the cluster as objects, your policy should live in Git as code and deploy through the same pipeline as everything else. This gives you review, history, and rollback. When an auditor asks "prove the restricted profile was enforced on the payments namespace in March," the answer is a Git commit and the applied manifest, not a screenshot. Storing policy as code also lets you test it: run policies against a library of known-good and known-bad manifests in CI so a policy change cannot silently start rejecting legitimate workloads.

Handle exceptions deliberately

Every real environment needs exceptions: a monitoring agent that legitimately needs hostPath, a legacy workload that cannot yet run non-root. Do not handle these by weakening the whole policy. Both Kyverno and Gatekeeper support scoped exclusions, and Pod Security Admission lets you set a laxer profile on a specific namespace. Record each exception with an owner and a review date so it is a tracked decision, not permanent erosion. Scanning findings and policy exceptions should feed the same review cadence; a software composition analysis workflow that tracks accepted risks with expiry dates gives you the same discipline for exceptions.

FAQ

What replaced PodSecurityPolicy in Kubernetes?

Pod Security Admission, which enforces the three Pod Security Standards (privileged, baseline, restricted) through namespace labels. PodSecurityPolicy was deprecated in 1.21 and removed in 1.25.

Should I use Kyverno or OPA Gatekeeper?

Kyverno uses YAML and is friendlier for teams already comfortable with Kubernetes manifests. Gatekeeper uses Rego, which is more powerful but has a steeper learning curve. For most teams Kyverno is the faster path to a working policy; choose Gatekeeper if you need Rego's expressiveness.

Can Pod Security Standards enforce image registry rules?

No. Pod Security Standards only cover pod-level security context. To restrict registries, require labels, or verify image signatures, you need a policy engine like Kyverno or Gatekeeper at the admission layer.

How do I roll out a strict policy without breaking existing workloads?

Start in audit or warn mode so violations are logged but not blocked, fix the flagged workloads, then switch to enforce. Rolling out namespace by namespace, beginning with lower-risk environments, keeps the blast radius small.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.