Exposed Kubernetes API Servers Are an RBAC Time Bomb
You don’t need a zero-day to take over a Kubernetes cluster. You need an
API server that answers on the public internet and an anonymous-auth
flag nobody turned off. Aqua Nautilus researchers spent three months
scanning for exactly that combination and found it everywhere: hundreds of
organizations with clusters already under active attack, most of it
opportunistic cryptomining riding in through anonymous access that had
somehow been granted real privileges (CSO Online, Aqua Nautilus). This isn’t a rare edge case — Shadowserver’s internet-wide scans have
repeatedly found north of 380,000 Kubernetes API servers responding
publicly, the large majority in the US and Western Europe (Shadowserver, The Register). Exposure alone doesn’t mean compromise, but it’s the precondition every
campaign in this space depends on, and the misconfigurations that turn
“reachable” into “owned” are boringly common.
Root cause
Kubernetes has two places where “no credentials” quietly becomes “some
credentials.” The first is the API server and kubelet’s anonymous-auth
setting, which historically defaulted to enabled and maps unauthenticated
requests to the system:anonymous user (or the system:unauthenticated
group). By itself that’s supposed to be harmless — anonymous requests
should have no RBAC permissions bound to them. In practice, Aqua’s kubelet
research found operators binding that anonymous identity to real roles,
sometimes admin-level ones, usually as a shortcut to get some internal
tool or health check working without wiring up proper auth (Aqua Security). Once that binding exists, anyone who can reach the API server or the
kubelet’s HTTPS port (10250) has exactly the access that role grants — no
login required. Aqua’s kube-hunter tool has a dedicated check for this
pattern precisely because it recurs so often across audited clusters
(Aqua Security / kube-hunter).
The second gap is the default service account. Every pod gets one mounted
automatically unless a manifest explicitly sets
automountServiceAccountToken: false, and that token grants whatever RBAC
role is bound to the account — which in a lot of real clusters is broader
than anyone intended, because default service accounts accumulate
permissions over time as people bind roles the fast way instead of the
correct way. Layer that onto an internet-reachable control plane —
sometimes because of a misconfigured cloud load balancer, sometimes
because someone ran kubectl proxy --address=0.0.0.0 --accept-hosts='.*'
on a bastion and forgot about it, a specific misconfiguration Aqua called
out by name in its research — and a single unauthenticated HTTP request is
enough to start probing.
Blast radius
The API server isn’t just another service — it’s the thing that can
schedule code onto every node in the cluster. Anonymous or default-account
access that includes pod-create permission lets an attacker launch a pod
with a hostPath mount or privileged: true, which is a direct path off
the container and onto the underlying node. From the node, the attacker
can read the kubelet’s credentials and every service account token
scheduled there, pivoting sideways into other namespaces the original
access point never touched. In cloud-hosted clusters, pods often carry
workload-identity credentials (IRSA on EKS, Workload Identity on GKE) or
can reach the instance metadata service directly, so cluster compromise
routinely becomes cloud-account compromise. This is also the exact class
of risk that made CVE-2018-1002105 so severe a few years back — a bug that
let unauthenticated requests reach the API server’s proxy layer and come
out the other side with cluster-admin, a reminder that “you’re talking to
the API server” and “you have privileged access” are one and the same
threat model even without a misconfiguration involved (Tenable). In practice, most of what Aqua and others observe on these exposed
clusters is quieter than that: TeamTNT- and Kinsing-style campaigns that
drop XMRig miners across every node they can reach, plus at least one
documented “RBAC buster” campaign that used the access specifically to
plant a persistent backdoor role rather than just mine coins (Tigera, CSO Online).
Remediation
- Disable anonymous auth. Run kubelets with
--anonymous-auth=falseand route real authentication through--client-ca-fileor a token webhook; never bindsystem:anonymousorsystem:unauthenticatedto any ClusterRole, including “harmless-looking” ones. - Stop exposing the control plane. Put the API server behind a private endpoint and security-group/authorized-network restrictions (EKS private endpoint access, GKE authorized networks, or equivalent) instead of a public load balancer with no source restriction.
- Audit RBAC for least privilege, especially bindings on
defaultservice accounts — Kubernetes’ own docs have a good-practices guide for this and it’s worth running against every namespace, not just the obviously sensitive ones (Kubernetes docs). - Set
automountServiceAccountToken: falseon any pod that doesn’t actually call the Kubernetes API, so a compromised pod doesn’t automatically hand over a credential. - Lock down the kubelet port (10250) with network policies and firewall rules — it shouldn’t be reachable from outside the cluster network, full stop.
- Turn on audit logging and alert on anonymous or unexpected-identity requests hitting the API server; that signal is cheap to collect and catches this exact pattern early.
The bigger lesson
None of this requires a novel exploit — every campaign cited here rode in on defaults nobody explicitly locked down. Kubernetes was built to be flexible for cluster operators first and secure-by-default second, and that tradeoff means the burden of hardening anonymous access, RBAC bindings, and network exposure sits entirely on whoever stood the cluster up. Attackers aren’t finding your cluster through cleverness; they’re running the same internet-wide scans Shadowserver publishes for free and checking whether you did the hardening step or skipped it. Assume they’ve already scanned you, and go verify which answer they got.
Join the discussion
Comments for this post live on social — reply to the thread.
Related posts
Cloud roundup: macOS Screen Sharing bug now under attack
A patched macOS Screen Sharing flaw is being exploited to plant crypto miners, a Windows Defender bypass has no fix yet, and EC2 gets built-in app health checks.
Cloud roundup: S3 finally names the policy that denied you
AWS S3 access-denied errors now name the exact policy ARN, Client VPN gets a scriptable CLI, and OpenAI ships authorized offensive-security models on Bedrock.
The Capital One breach: an SSRF bug into 100M records
A misconfigured WAF and an SSRF bug let an attacker reach the AWS metadata service and steal role credentials — exposing 100M+ Capital One records.