惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
aimingoo的专栏
aimingoo的专栏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
J
Java Code Geeks
博客园_首页
Google Online Security Blog
Google Online Security Blog
Hugging Face - Blog
Hugging Face - Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
Intezer
P
Palo Alto Networks Blog
V
Vulnerabilities – Threatpost
雷峰网
雷峰网
O
OpenAI News
SecWiki News
SecWiki News
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
N
News | PayPal Newsroom
Project Zero
Project Zero
Forbes - Security
Forbes - Security
IT之家
IT之家
A
Arctic Wolf
WordPress大学
WordPress大学
Jina AI
Jina AI
T
Tor Project blog
博客园 - 三生石上(FineUI控件)
S
Secure Thoughts
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
博客园 - 聂微东
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
Cloudbric
Cloudbric
G
GRAHAM CLULEY
博客园 - 叶小钗
H
Hacker News: Front Page
腾讯CDC
量子位
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
C
Cyber Attacks, Cyber Crime and Cyber Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
宝玉的分享
宝玉的分享
爱范儿
爱范儿
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recorded Future
Recorded Future
C
CERT Recently Published Vulnerability Notes

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Kubernetes Security Best Practices 2026: Complete Hardening Guide
devtocash · 2026-06-26 · via DEV Community

Kubernetes Security Best Practices 2026: The Complete Hardening Guide

Introduction

A single misconfigured Kubernetes cluster can expose your entire infrastructure in minutes. In 2025 alone, over 60% of organizations reported at least one Kubernetes security incident — and the majority traced back to preventable misconfigurations, not zero-day exploits.

Kubernetes ships with minimal security defaults. Everything is open. Pods can talk to each other freely. Service accounts carry cluster-admin privileges by default. Secrets sit in etcd unencrypted. If you deploy a vanilla cluster and walk away, you are effectively running with the doors unlocked.

This guide walks through 4 critical security layers you must implement in any production Kubernetes cluster. Each section includes real, copy-paste-ready YAML manifests and CLI commands. Whether you run EKS, GKE, AKS, or bare-metal kubeadm clusters, these practices apply universally.

Who this is for: DevOps engineers, SREs, platform engineers, and anyone responsible for production Kubernetes clusters. You should be comfortable with kubectl and basic YAML. No prior security specialization required.

What you will implement by the end of this guide:

  • Fine-grained RBAC with least-privilege principles
  • Pod Security Admission replacing deprecated PSPs
  • Zero-trust network policies with default-deny
  • Image vulnerability scanning with Trivy in CI/CD

Let's lock it down.

1. RBAC: Least Privilege from Day One

Role-Based Access Control (RBAC) is your first and most important line of defense. The principle is simple: every user, service account, and application should have exactly the permissions it needs — nothing more.

The Problem with Default RBAC

Out of the box, Kubernetes grants the system:masters group (used by kubeadm init) cluster-admin. Every default service account in the kube-system namespace gets elevated privileges. The default service account in every namespace exists automatically and — unless you explicitly bind it — has no permissions. But many teams accidentally grant it broad access during development and forget to revoke it.

Here is the most common anti-pattern we see in production audits:

# BAD: cluster-admin bound to default service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dangerous-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: default
  namespace: default

This gives every pod in the default namespace unrestricted control over the entire cluster. If any pod gets compromised, the attacker owns everything.

RBAC Best Practices

1. Use namespace-scoped Roles, not ClusterRoles, whenever possible.

A Role is bound to a single namespace. A ClusterRole is cluster-wide. Most applications only need access to resources in their own namespace.

# GOOD: namespace-scoped Role for a web application
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: webapp-role
  namespace: production
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "secrets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "update"]

2. Create a dedicated ServiceAccount for every application.

Never use the default service account. Always create a named ServiceAccount and bind it to a specific Role.

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

Then reference it in your Deployment:

spec:
  serviceAccountName: webapp-sa
  automountServiceAccountToken: true

3. Avoid wildcard verbs and resources.

Every * in your RBAC rules is a potential escalation path. Be specific:

# BAD
verbs: ["*"]
resources: ["*"]

# GOOD: explicit
verbs: ["get", "list", "watch"]
resources: ["pods", "services"]

4. Use kubectl auth can-i to verify permissions.

Before deploying, test what a service account can actually do:

kubectl auth can-i create deployments \
  --as=system:serviceaccount:production:webapp-sa \
  --namespace=production

5. Separate human users from machine accounts.

Use OIDC (OpenID Connect) for human authentication. Service accounts are for pods and CI/CD pipelines. Map OIDC groups to Kubernetes roles — never give individual users cluster-admin. Tools like Dex, Keycloak, or cloud-provider IAM (aws-iam-authenticator for EKS, GCP IAM for GKE) handle this cleanly.

6. Audit your RBAC regularly.

Run this one-liner to find overly permissive bindings:

kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'

You will be surprised how many cluster-admin bindings accumulate over time. RBAC auditing tools like kubescape, kube-bench, or popeye can automate this check.

RBAC for CI/CD Pipelines

CI/CD systems (GitHub Actions, GitLab CI, ArgoCD) need API access to deploy. The pattern: create a ServiceAccount with the minimum permissions required for deployments, extract its token, and inject it into your pipeline secrets.

kubectl create serviceaccount cicd-deployer -n production
kubectl create rolebinding cicd-deployer-binding \
  --role=webapp-role \
  --serviceaccount=production:cicd-deployer \
  -n production

# For Kubernetes 1.24+, create a long-lived token:
kubectl create token cicd-deployer -n production --duration=8760h

Store that token in your CI secrets manager — never in source code.

2. Pod Security Standards & Pod Security Admission

Pod Security Policies (PSPs) were deprecated in Kubernetes 1.21 and removed in 1.25. The replacement is Pod Security Admission (PSA) — a built-in admission controller that enforces Pod Security Standards at the namespace level.

The Three Pod Security Standards

Standard Description Key Restrictions
Privileged Unrestricted. Equivalent to no policy. None. Use only for system namespaces.
Baseline Prevents known privilege escalations. Minimum for production. No hostNetwork, hostPID, hostIPC, hostPorts, privileged containers, or hostPath volumes.
Restricted Hardened following Pod hardening best practices. Everything Baseline restricts, plus: must run as non-root, seccomp profile required, capabilities dropped to NET_BIND_SERVICE only, read-only root filesystem.

Enforcing PSA with Namespace Labels

PSA uses namespace labels — no separate CRD needed. Apply a label to any namespace:

# Enforce the restricted policy on a namespace
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted

# Also set audit and warn modes for visibility
kubectl label namespace production \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

Three enforcement modes exist for each label:

  • enforce: reject pods that violate the policy
  • audit: allow but log violations to the audit log
  • warn: allow but show a warning to the user

Gradual rollout strategy: Start with warn and audit modes for a week. Fix all warnings. Then switch to enforce. Never jump straight to enforce=restricted on existing production namespaces — you will break running workloads.

Writing a Restricted-Compliant Pod

Here is a pod that passes the restricted Pod Security Standard:

apiVersion: v1
kind: Pod
metadata:
  name: secure-nginx
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: nginx
    image: nginx:1.25-alpine
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
        add: ["NET_BIND_SERVICE"]
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: nginx-cache
      mountPath: /var/cache/nginx
  volumes:
  - name: tmp
    emptyDir: {}
  - name: nginx-cache
    emptyDir: {}

Key points:

  • runAsNonRoot: true — the container must not run as UID 0
  • seccompProfile: RuntimeDefault — blocks dangerous syscalls by default
  • capabilities.drop: ["ALL"] — strip all Linux capabilities
  • readOnlyRootFilesystem: true — attackers cannot write to the filesystem
  • allowPrivilegeEscalation: false — no setuid binaries

Exemptions (When You Need Them)

Some system workloads genuinely need privileged access — CNI plugins, CSI drivers, monitoring agents. Use namespace exemptions:

# In kube-apiserver.yaml
apiVersion: v1
kind: Pod
spec:
  containers:
  - command:
    - kube-apiserver
    - --admission-control-config-file=/etc/kubernetes/admission.yaml

And in the admission configuration:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "restricted"
    exemptions:
      namespaces: ["kube-system", "cert-manager", "ingress-nginx"]
      usernames: ["system:serviceaccount:kube-system:calico-node"]

3. Network Policies: Zero-Trust Inside the Cluster

Kubernetes networking is flat by default. Every pod can reach every other pod in the cluster, across all namespaces — with zero built-in filtering. If one pod gets compromised, it can scan your entire internal network, hit internal APIs, and pivot to databases.

Network Policies are your internal firewall. They are Kubernetes-native resources that control traffic flow at Layers 3 and 4 (IP and port level). Think of them as security group rules for pods.

Default-Deny: Lock Everything First

Start by denying all ingress and egress traffic. Then selectively open only what each application needs:

# Default deny all ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}          # selects all pods
  policyTypes:
  - Ingress

# Default deny all egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress

With these two policies in place, no pod can receive or initiate traffic. Then layer on allow rules for specific flows:

# Allow frontend → backend on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

Real-World Zero-Trust Policy Patterns

Pattern 1: Allow only from the ingress controller

ingress:
- from:
  - namespaceSelector:
      matchLabels:
        name: ingress-nginx
  - podSelector:
      matchLabels:
        app.kubernetes.io/name: ingress-nginx

Pattern 2: Allow DNS egress only (block everything else)

egress:
- to:
  - namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: kube-system
  - podSelector:
      matchLabels:
        k8s-app: kube-dns
  ports:
  - protocol: UDP
    port: 53

Pattern 3: Allow egress only to specific external IPs

egress:
- to:
  - ipBlock:
      cidr: 10.0.0.0/8    # internal VPC only
      except:
      - 10.0.0.0/28        # except management subnet

Cilium: Beyond Basic Network Policies

If your CNI is Cilium (increasingly common in 2026 for eBPF-based networking), you get Layer 7 policies — filtering by HTTP method, path, or DNS name:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: l7-policy
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/api/v1/.*"

Layer 7 policies let you declare: only GET requests to /api/v1/ endpoints are allowed — no POST, no DELETE, no /admin. A compromised frontend pod cannot abuse backend endpoints it should not access.

Testing Network Policies

Use netshoot (a network debugging container) to verify connectivity:

kubectl run netshoot --rm -it --image nicolaka/netshoot -- /bin/bash

# From inside, test connectivity:
curl backend-service.production.svc.cluster.local:8080

# Test DNS:
nslookup kubernetes.default

Always test both positive (traffic that should flow) and negative (traffic that should be blocked) cases. Network policies are easy to misconfigure — a single missing label selector can leave a hole wide open.

4. Image Security: Scan Every Container with Trivy

Running containers from untrusted or unverified images is the most common entry point for supply-chain attacks. In 2024, a compromised xz-utils backdoor nearly made it into production containers worldwide. In 2025, multiple malicious NPM and PyPI packages were found embedded in popular Docker images.

The rule: every image that enters your cluster must be scanned. Trivy, by Aqua Security, is the de-facto open-source scanner — fast, comprehensive, and CI/CD-friendly. It scans OS packages, language dependencies, and misconfigurations in a single pass.

Scanning Images

trivy image nginx:1.25-alpine

# Filter by severity — only flag HIGH and CRITICAL
trivy image --severity HIGH,CRITICAL nginx:1.25-alpine

# Output as JSON for pipeline integration
trivy image --format json --output trivy-report.json myapp:latest

# Scan filesystem for IaC misconfigurations
trivy config ./kubernetes/

Integrating Trivy into CI/CD

The most effective pattern: scan in your pipeline and block deployments for HIGH or CRITICAL findings.

GitHub Actions — Trivy scan step:

- name: Scan container image with Trivy
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: myapp:${{ github.sha }}
    format: sarif
    output: trivy-results.sarif
    severity: HIGH,CRITICAL
    exit-code: 1

GitLab CI — Trivy scan job:

trivy-scan:
  stage: security
  image: aquasec/trivy:latest
  script:
    - trivy image --severity HIGH,CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  allow_failure: false

Trivy Operator: Continuous Cluster Scanning

For runtime scanning of images already deployed in your cluster, install the Trivy Operator:

helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm install trivy-operator aqua/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --set trivy.ignoreUnfixed=true

Query vulnerability reports across your namespaces:

kubectl get vulnerabilityreports -n production
kubectl describe vulnerabilityreport replicaset-myapp-7d4f8b9c6d-nginx

Image Pinning and Digest-Based References

Never use floating tags like :latest in production. Pin to a content-addressable digest:

# BAD: floating tag, can change underneath you
image: nginx:latest

# GOOD: digest pinning, immutable reference
image: nginx@sha256:aed492c4d72c4a4e2f4d7d5e1f3b6c8a9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4

Extract the digest of any image:

docker inspect nginx:1.25-alpine | jq -r '.[0].RepoDigests[0]'

Combine digest pinning with automated PRs from Renovate or Dependabot to keep digests updated without manual toil.

Conclusion

Kubernetes security is a layered defense. Start with RBAC — if every pod runs as cluster-admin, nothing else matters. Then enforce Pod Security Standards to prevent containers from escaping their sandbox. Lock down the network with default-deny policies so a compromised pod can't pivot. Finally, scan every image with Trivy to catch vulnerabilities before they reach production.

Each of these four layers independently raises the bar. Together, they turn a wide-open cluster into a hardened environment where an attacker needs to defeat multiple defenses to cause real damage. The YAML in this guide is production-ready — copy, adapt, and apply it today.


Originally published at devtocash.com