5 tips to improve the security of your Kubernetes infrastructure
In the light of the Hugging Face hack, AI agents have gotten all the limelight and attention — but the attack was easily preventable if the basic Kubernetes hardenings had been in place. The uncomfortable truth about most cluster compromises is that they don't hinge on exotic zero-days; they hinge on defaults that were never tightened.
Here are five hardening steps we consider table stakes. Each one closes a door that attackers reach for early, and every one of them is applied by default on ModStack-managed clusters. This post kicks off our Hardening Kubernetes series with the overview — each part that follows digs into one of these tips in production-level depth.
1. Disable automounting of service account tokens
By default, every pod gets a service account token mounted at a well-known path. If an attacker lands in any container — through a dependency exploit, a leaked credential, a compromised model file — that token is their first pivot: it's a ready-made credential for talking to the Kubernetes API. Most workloads never need it.
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
automountServiceAccountToken: false
Set it on the service account (or per pod spec) and grant the token back explicitly only to the rare workload that genuinely talks to the API server.
2. Use an admission controller — we recommend Kyverno
Everything above relies on people remembering to write the right YAML. An admission controller makes the cluster enforce it instead: every object is validated against policy before it's admitted, so an insecure manifest can't even be created. We recommend Kyverno — policies are plain Kubernetes resources, no new language to learn.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-privileged-containers
spec:
validationFailureAction: Enforce
rules:
- name: no-privileged
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Privileged containers are not allowed."
pattern:
spec:
containers:
- securityContext:
privileged: "false"
Start with the Pod Security Standards policies, then layer on rules that encode your own conventions — required labels, banned registries, mandatory resource limits.
3. Configure system call profiles
Containers share the host kernel, and the kernel's syscall
surface is where container escapes live. A
seccomp profile restricts which syscalls a
container may make. We restrict every workload to
RuntimeDefault — the container runtime's curated
profile, which blocks the obscure syscalls that most kernel
exploits depend on while remaining compatible with virtually
all application workloads.
securityContext:
seccompProfile:
type: RuntimeDefault
allowPrivilegeEscalation: false
Pair it with your admission controller from tip 2 so a pod without a seccomp profile simply never schedules.
4. Use a network provider like Cilium
Flat pod networks are an attacker's best friend: land anywhere, reach everything — including the Kubernetes API server. A CNI like Cilium gives you identity-aware network policies, which lets you restrict which workloads can reach the K8s API server at all, on top of standard pod-to-pod segmentation.
# Only pods labelled api-client may talk to the API server
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: restrict-kube-apiserver
spec:
endpointSelector: {}
egressDeny:
- toEntities: [kube-apiserver]
enableDefaultDeny:
egress: false
Combined with tip 1, this defuses the classic escalation chain: even a pod that somehow holds a token can't open a connection to spend it.
5. Scan for vulnerabilities continuously
The four tips above shrink the blast radius of a compromise; scanning shrinks the odds of one happening. Every image you run carries a dependency tree you didn't write, and new CVEs land in it weekly. We use AWS's vulnerability scanning (Amazon Inspector, wired into ECR) so every pushed image is scanned automatically and re-evaluated as new CVEs are published — findings surface per service, worst-severity first, before and after the image ships.
Scanning only pays off if someone looks at the results. Put the worst finding on the same dashboard people already use daily — a CVE report nobody opens is indistinguishable from no scanner at all.
The takeaway
None of these five require heroics — they're configuration, not architecture. That's precisely the point of the Hugging Face lesson: the gap between "compromised" and "contained" was a handful of YAML defaults.
If you'd rather not maintain that YAML yourself: ModStack
applies every one of these hardenings — token automount off,
Kyverno policies, RuntimeDefault seccomp, Cilium
networking, and continuous image scanning — to every
environment it provisions, out of the box.
Hardened by default
Every ModStack environment ships with these five protections already on. Spend your time on your product, not your YAML.
Get Started