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
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
| Vector | Default Kubernetes | Hardened |
|---|---|---|
| Container privileges | Root by default | Non-root, read-only filesystem |
| Pod-to-pod traffic | Open (any pod → any pod) | NetworkPolicy whitelist |
| Host access | Possible (hostPID, hostNetwork) | Blocked by Pod Security Standards |
| Secrets at rest | Base64 in etcd (not encrypted) | Envelope encryption + External Secrets |
| RBAC | Wide permissions | Least-privilege per namespace |
| Container images | Any public image | Signed images, private registry |
| Admission | No policy enforcement | OPA Gatekeeper policies |
Pod Security Standards
Kubernetes 1.25+ replaced PodSecurityPolicy (deprecated) with Pod Security Standards (PSS). Three levels:
| Level | Applies To | What It Restricts |
|---|---|---|
privileged | Nothing | No restrictions |
baseline | Most workloads | Blocks most privilege escalation paths |
restricted | Security-sensitive | Strictest; 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
restrictedenforced on production namespace - All containers run as non-root (
runAsNonRoot: true) - All containers have CPU and memory limits
-
allowPrivilegeEscalation: falseon all containers - NetworkPolicy default-deny with explicit allow rules
- RBAC: no ClusterAdmin roles for application service accounts
- No
hostNetwork,hostPID,hostIPCin 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
- GitOps Workflows — secure GitOps delivery with ArgoCD/Flux
- Zero Trust Security — network-level zero trust to complement cluster security
- Infrastructure as Code — Terraform for cluster provisioning
- DevOps Best Practices — security in the CI/CD pipeline
- Cloud Solutions — Kubernetes security and platform engineering
About the Author
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.
Need DevOps & Cloud Expertise?
Scale your infrastructure with confidence. AWS, GCP, Azure certified team.
Free consultation • No commitment • Response within 24 hours
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.