Back to Blog

Kubernetes Security: Pod Security Standards, OPA Gatekeeper, and Network Policies

Harden Kubernetes clusters — Pod Security Standards enforcement, OPA Gatekeeper admission policies, NetworkPolicy for pod isolation, secrets rotation with Exter

Viprasol Tech Team
May 15, 2026
13 min read

Kubernetes Security: Pod Security Standards, OPA Gatekeeper, and Network Policies

A default Kubernetes cluster is not secure. Pods run as root, containers can access host resources, any pod can talk to any other pod, and secrets are stored as base64-encoded (not encrypted) values in etcd. Left unconfigured, a compromised container can escalate to node-level access within minutes.

This guide covers the controls that turn a default cluster into a hardened production environment.


The Attack Surface

VectorDefault KubernetesHardened
Container privilegesRoot by defaultNon-root, read-only filesystem
Pod-to-pod trafficOpen (any pod → any pod)NetworkPolicy whitelist
Host accessPossible (hostPID, hostNetwork)Blocked by Pod Security Standards
Secrets at restBase64 in etcd (not encrypted)Envelope encryption + External Secrets
RBACWide permissionsLeast-privilege per namespace
Container imagesAny public imageSigned images, private registry
AdmissionNo policy enforcementOPA Gatekeeper policies

Pod Security Standards

Kubernetes 1.25+ replaced PodSecurityPolicy (deprecated) with Pod Security Standards (PSS). Three levels:

LevelApplies ToWhat It Restricts
privilegedNothingNo restrictions
baselineMost workloadsBlocks most privilege escalation paths
restrictedSecurity-sensitiveStrictest; requires non-root, drops capabilities

Enforce at the namespace level via labels:

# Enforce restricted PSS for production namespace
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # Enforce: reject pods that violate restricted policy
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # Audit: log violations without blocking (for gradual migration)
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    # Warn: show warnings in kubectl output
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

Compliant deployment for restricted PSS:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: production
spec:
  template:
    spec:
      # Run as non-root user
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault  # Required by restricted PSS

      containers:
        - name: api
          image: ghcr.io/yourorg/api:v1.2.3
          securityContext:
            allowPrivilegeEscalation: false  # Required
            readOnlyRootFilesystem: true      # Best practice
            capabilities:
              drop:
                - ALL  # Drop all Linux capabilities
              # add: [NET_BIND_SERVICE]  # Only if needed for port < 1024

          # Writeable volumes for temp files if needed
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: var-run
              mountPath: /var/run

      volumes:
        - name: tmp
          emptyDir: {}
        - name: var-run
          emptyDir: {}

☁️ Is Your Cloud Costing Too Much?

Most teams overspend 30–40% on cloud — wrong instance types, no reserved pricing, bloated storage. We audit, right-size, and automate your infrastructure.

  • AWS, GCP, Azure certified engineers
  • Infrastructure as Code (Terraform, CDK)
  • Docker, Kubernetes, GitHub Actions CI/CD
  • Typical audit recovers $500–$3,000/month in savings

OPA Gatekeeper: Custom Admission Policies

Pod Security Standards cover container privileges. OPA Gatekeeper enforces custom business policies via admission webhooks — image registry restrictions, required labels, resource limits, etc.

# Install Gatekeeper
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml

Policy: Require resource limits on all containers

# ConstraintTemplate: defines the policy logic in Rego
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: requireresourcelimits
spec:
  crd:
    spec:
      names:
        kind: RequireResourceLimits
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package requireresourcelimits

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.cpu
          msg := sprintf("Container '%v' must have CPU limits set", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.memory
          msg := sprintf("Container '%v' must have memory limits set", [container.name])
        }

---
# Constraint: apply the policy to production namespace
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequireResourceLimits
metadata:
  name: require-resource-limits
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment", "StatefulSet", "DaemonSet"]
    namespaces: ["production", "staging"]
  enforcementAction: deny  # Reject non-compliant deployments

Policy: Restrict to approved image registries

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: allowedregistries
spec:
  crd:
    spec:
      names:
        kind: AllowedRegistries
      validation:
        openAPIV3Schema:
          type: object
          properties:
            allowedRegistries:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package allowedregistries

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          image := container.image
          not any_allowed(image, input.parameters.allowedRegistries)
          msg := sprintf("Image '%v' is from an unauthorized registry", [image])
        }

        any_allowed(image, registries) {
          registry := registries[_]
          startswith(image, registry)
        }

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AllowedRegistries
metadata:
  name: allowed-registries-prod
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
    namespaces: ["production"]
  parameters:
    allowedRegistries:
      - "ghcr.io/yourorg/"
      - "123456789.dkr.ecr.us-east-1.amazonaws.com/"
      # Reject Docker Hub, public images in production

NetworkPolicy: Pod Isolation

By default, all pods can communicate with all other pods. NetworkPolicy implements a deny-all + explicit-allow model:

# Step 1: Default deny all ingress and egress in production
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}  # Applies to all pods
  policyTypes:
    - Ingress
    - Egress

---
# Step 2: Allow API pods to receive traffic from the load balancer
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-ingress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        # Only from ALB controller pods or ingress namespace
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 3000

---
# Step 3: Allow API to talk to PostgreSQL (and nothing else)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-postgres
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432
    # Also allow DNS resolution
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53

⚙️ DevOps Done Right — Zero Downtime, Full Automation

Ship faster without breaking things. We build CI/CD pipelines, monitoring stacks, and auto-scaling infrastructure that your team can actually maintain.

  • Staging + production environments with feature flags
  • Automated security scanning in the pipeline
  • Uptime monitoring + alerting + runbook automation
  • On-call support handover docs included

RBAC: Least Privilege

# Role: allow api-service to read secrets in its namespace only
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: api-role
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["api-secrets", "db-creds"]  # Named secrets only
    verbs: ["get"]
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["api-config"]
    verbs: ["get", "watch", "list"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: api-role-binding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: api-service-account
    namespace: production
roleRef:
  kind: Role
  name: api-role
  apiGroup: rbac.authorization.k8s.io

---
# ServiceAccount for the API deployment
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-service-account
  namespace: production
  annotations:
    # AWS IRSA: give this SA IAM role access (no static credentials needed)
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/api-production-role
automountServiceAccountToken: false  # Don't auto-mount unless explicitly needed

Secrets Rotation with External Secrets Operator

Rotate secrets automatically without redeployment:

# ESO SecretStore pointing to AWS SSM
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-ssm
spec:
  provider:
    aws:
      service: ParameterStore
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-sa
            namespace: external-secrets

---
# ExternalSecret: auto-rotate DB credentials every hour
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-creds
  namespace: production
spec:
  refreshInterval: 1h    # Check for rotation every hour
  secretStoreRef:
    name: aws-ssm
    kind: ClusterSecretStore
  target:
    name: db-creds
    creationPolicy: Owner
    template:
      # Force pod restart when secret changes
      annotations:
        secret-rotation-timestamp: "{{ .rotatedAt }}"
  data:
    - secretKey: password
      remoteRef:
        key: /production/api/db-password
        version: AWSCURRENT  # Always fetch current version

Security Checklist

Before going to production:

  • Pod Security Standards restricted enforced on production namespace
  • All containers run as non-root (runAsNonRoot: true)
  • All containers have CPU and memory limits
  • allowPrivilegeEscalation: false on all containers
  • NetworkPolicy default-deny with explicit allow rules
  • RBAC: no ClusterAdmin roles for application service accounts
  • No hostNetwork, hostPID, hostIPC in production workloads
  • Image registry restricted to internal registry
  • Secrets from External Secrets Operator (not hardcoded in manifests)
  • etcd encryption at rest enabled
  • Audit logging enabled for API server
  • OPA Gatekeeper policies for organizational standards

Working With Viprasol

We harden Kubernetes clusters for production — Pod Security Standards enforcement, OPA Gatekeeper policies, NetworkPolicy implementation, RBAC audit and remediation, and secrets rotation automation. Security posture improves significantly with these controls in place.

Talk to our security team about Kubernetes hardening.


See Also

Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Need DevOps & Cloud Expertise?

Scale your infrastructure with confidence. AWS, GCP, Azure certified team.

Free consultation • No commitment • Response within 24 hours

Viprasol · Big Data & Analytics

Making sense of your data at scale?

Viprasol builds end-to-end big data analytics solutions — ETL pipelines, data warehouses on Snowflake or BigQuery, and self-service BI dashboards. One reliable source of truth for your entire organisation.