Vulnerability

Exposed Kubernetes API Server (TCP 6443 / 8443 / 10250)
detect, harden, and verify closure

A kube-apiserver reachable from the public internet with --anonymous-auth=true and the system:anonymous or system:unauthenticated user bound to a permissive ClusterRole, a kubelet on TCP 10250 accepting anonymous requests with --authorization-mode=AlwaysAllow, an etcd peer or client port on 2379 or 2380 reachable without TLS client certificates, or a managed cluster with the public API endpoint left open after a debug session, all collapse to the same outcome: cluster takeover, lateral movement to every workload, and a credentialled pivot into every cloud account the cluster is federated against. Learn how to detect the exposure, lock it down, and keep the closure verifiable.

No credit card required. Free plan available forever.

Severity

Critical

CWE ID

CWE-306

OWASP Top 10

A01:2021 - Broken Access Control; A05:2021 - Security Misconfiguration

CVSS 3.1 Score

10.0

What is an exposed Kubernetes API server?

An exposed Kubernetes API server is the cluster of weaknesses that turn the cluster control plane into a credentialled foothold for any caller that can reach it. The most common shape is the kube-apiserver bound to a public IP on TCP 6443 (the default secure port) or TCP 8443 (a common alternative), reachable from 0.0.0.0/0, with --anonymous-auth=true and a ClusterRoleBinding that grants the system:anonymous or system:unauthenticated user list, watch, or create permissions on cluster-scoped resources. The second common shape is the kubelet on every node bound to TCP 10250 with --authorization-mode=AlwaysAllow, --anonymous-auth=true, or the deprecated read-only port on TCP 10255, accepting unauthenticated requests for /pods, /exec, and /run. The third shape is an etcd peer or client port (TCP 2379, TCP 2380) reachable without TLS client certificates, giving direct read and write access to every cluster object including every Secret in plaintext.

Either listener collapses to the same outcome. A caller with access to a permissive kube-apiserver can enumerate every Namespace, Pod, Service, ConfigMap, Secret, ServiceAccount, ClusterRole, and ClusterRoleBinding; create a privileged Pod that mounts the host root filesystem and the cloud metadata service; exec into running containers across every Namespace; read every Service Account token from a node and pivot to peer clusters federated against the same identity provider; or post a Job to the kube-system Namespace that persists across reboots. A caller with access to a permissive kubelet bypasses the apiserver entirely and reaches container exec, image pull, and node-level command execution directly. A caller with access to etcd reads every cluster object in plaintext, including every Secret, and writes new objects that the apiserver will faithfully reconcile. Sister exposures land on the same hosts: an open Docker socket or container runtime socket on a node is the same root-equivalence pattern at the node layer, and a permissive cloud security group that opens 6443 to 0.0.0.0/0 frequently opens MongoDB or Elasticsearch on neighbouring ports at the same time.

The track record is unambiguous. Kinsing, TeamTNT, Hildegard, and Siloscape have each industrialised exposed-Kubernetes-API discovery into commodity cryptojacking, lateral movement, and cloud-account pivots. The pattern is the same across every variant: scan IPv4 for TCP 6443 answering with the Kubernetes API HTTP banner on /version, /api, or /healthz; probe whether system:anonymous can list pods (curl -sk https://host:6443/api/v1/pods returning a populated PodList is proof); post a privileged Pod manifest to the kube-system Namespace that binds the host root filesystem and runs a miner or a reverse shell; pull every Service Account token from /var/run/secrets/kubernetes.io/serviceaccount on a compromised node; reach the cloud metadata service through the Pod network; and federate access to every cloud-account permission the cluster identity is granted. Shodan and Censys continuously index tens of thousands of unauthenticated Kubernetes API servers and kubelets on public IPv4 at any moment, and the operators of those scans publish public dashboards that script attackers consume daily.

Exposed Kubernetes API is also a recurring audit, cyber-insurance, and cloud-account isolation finding. The CIS Kubernetes Benchmark controls 1.2.1 (--anonymous-auth=false on the apiserver), 1.2.2 (--authorization-mode does not include AlwaysAllow), 1.2.5 (kubelet certificate authority is set), 2.1 (etcd uses TLS client certificates), 4.2.1 (kubelet --anonymous-auth=false), and 4.2.2 (kubelet --authorization-mode is not AlwaysAllow) cover the listener-side hardening; the NSA and CISA Kubernetes Hardening Guidance names network policy, authentication, and authorisation as baseline expectations; PCI DSS Requirement 1.3, 2.2, 7.1, and 7.2 cover boundary protection and least-privilege access; ISO 27001 Annex A.5.15, A.8.2, A.8.5, A.8.20, A.8.21, and A.8.24 cover privileged access, network controls, and cryptographic use; NIST SP 800-53 SC-7 (boundary protection), AC-3 (access enforcement), AC-6 (least privilege), and CM-7 (least functionality) all read the same exposure; NIST SP 800-207 (Zero Trust Architecture) covers the principle that no listener should accept anonymous requests; the CIS Critical Security Controls v8.1 Control 4 (secure configuration), Control 5 (account management), and Control 12 (network infrastructure management) cover the broader posture; and the CISA Secure by Design guidance names default-deny networking and default-enabled authentication as baseline expectations for software shipping with a network listener. Closing or hardening the exposure is almost always inexpensive compared to the cost of a successful cluster takeover, which is why this class of finding tends to move quickly through remediation tracking once it is surfaced on a controlled record.

How attackers exploit it

1

Discover open 6443, 8443, or 10250

Attackers continuously scan IPv4 (Shodan, Censys, Masscan, ZMap, custom Go and Python tooling) for hosts answering on TCP 6443, 8443, 10250, 2379, and 2380. A simple GET to /version on 6443 returns the apiserver version, the build date, the Go version, and the platform, before any authentication is attempted. A GET to /pods on a permissive kubelet at 10250 returns the running pod inventory. A GET to /metrics on the same port returns node-level telemetry. Shodan dorks for "kubernetes" or "Server: nginx" with port 6443 yield thousands of candidate hosts continuously.

2

Probe anonymous-auth and RBAC state

Where the apiserver answers /api/v1/pods or /api/v1/namespaces without an Authorization header and returns a populated JSON list rather than a 401 or 403, --anonymous-auth=true and the system:anonymous or system:unauthenticated user is bound to a permissive role. Where the kubelet answers /pods without an Authorization header, --authorization-mode=AlwaysAllow and --anonymous-auth=true are set. Where etcd answers v2 keys or v3 range requests without a client certificate, the peer or client port is exposed without TLS client-certificate enforcement.

3

Create a privileged workload or read every Secret

On the apiserver path, the attacker posts a Pod manifest to the kube-system Namespace that sets privileged: true, hostNetwork: true, hostPID: true, hostIPC: true, and a hostPath volume mount of /. The Pod now reads and writes every file on the node. On the kubelet path, the attacker POSTs to /run/{namespace}/{pod}/{container} to issue a command inside a running container without ever passing through the apiserver. On the etcd path, the attacker reads every key under /registry/secrets/ and decodes every Service Account token, kubeconfig, image pull credential, and platform secret in plaintext.

4

Persist, pivot, and monetise

The attacker schedules a CronJob in kube-system that runs every hour on every node, deploys a DaemonSet that watches for new nodes and re-infects them, mints a new ServiceAccount in kube-system and binds cluster-admin to it, replaces an admission webhook to grant ongoing access regardless of policy state, or pulls a mining image (Kinsing, TeamTNT XMRig variants) onto every node. Cluster-attached identity lets the attacker pivot to the cloud metadata service, IAM-federated cloud roles, peer clusters, the container registry, and any orchestrator or secret store the cluster is granted access to.

Common causes

Public API endpoint on a managed cluster

An EKS, AKS, or GKE cluster is created with the public endpoint enabled and the source IP allowlist set to 0.0.0.0/0 during a console click-through, a CDK or Terraform default, a debugging session, or a vendor handoff. The flag persists across cluster upgrades, autoscaler events, and account migrations. The control plane itself is hardened by the hyperscaler, but the listener is still reachable from the public internet and accepts kubectl from anywhere on earth. RBAC misconfiguration on the same cluster turns a reachable listener into a cluster takeover.

Self-managed control plane on cloud VMs

A kops, kubeadm, k3s, RKE, or vanilla kube-apiserver deployment on EC2, Compute Engine, Azure VM, or DigitalOcean droplets binds the apiserver to 0.0.0.0:6443 with --anonymous-auth=true and accepts public traffic. The deployment also exposes etcd on 2379 for cluster-internal traffic without TLS client-certificate enforcement, and the kubelet on 10250 with --authorization-mode=AlwaysAllow because the operator copied a quick-start guide. The plaintext bind survives across image rebuilds, autoscaling events, and node refreshes long after the original convenience reason is forgotten.

Over-permissive RBAC for anonymous users

A ClusterRoleBinding grants the system:anonymous or system:unauthenticated user a role that lets it list pods, list nodes, list namespaces, or read secrets. The binding is often left behind by an early debugging session, a stale tutorial, a Helm chart that ships an anonymous read for service discovery, an admission webhook that needs an anonymous probe path, or a monitoring agent that wanted unauthenticated cluster reads during prototyping. A reachable apiserver plus a permissive anonymous binding equals the same outcome as a fully unauthenticated apiserver.

Kubelet anonymous-auth and AlwaysAllow

The kubelet on every node ships with --anonymous-auth=true and --authorization-mode=AlwaysAllow in legacy distributions, in self-managed deployments, in development clusters that were never retuned, in k3s installs whose flag defaults differ from upstream, and in IoT and edge deployments that prioritise zero-touch onboarding. The kubelet exposes /pods, /containerLogs, /exec, /run, /portForward, and /metrics on 10250 without an authentication or authorisation check. A reachable kubelet is a node takeover before the apiserver even enters the discussion.

Cloud security group rule too broad

An AWS security group, Azure NSG, GCP firewall rule, or DigitalOcean rule opens TCP 6443, 8443, 10250, 2379, or 2380 to 0.0.0.0/0 during a Terraform run, a debug session, a vendor handoff, or a recovery exercise. The rule is never narrowed, and a public Kubernetes control plane persists across image rebuilds, autoscaling events, and account migrations. Public-cloud attacker tooling continuously sweeps cloud netblocks for the pattern.

etcd reachable without client certificates

A self-managed etcd cluster on TCP 2379 (client) and 2380 (peer) is configured without --client-cert-auth=true and --peer-client-cert-auth=true, or with the TLS material missing entirely. The peer port that nodes use for replication is reachable from outside the cluster network, and the client port that the apiserver uses is reachable from the same broad range. A caller with network access reads every cluster object including every Secret in plaintext and writes new objects that the apiserver will reconcile into cluster state.

How to detect it

Automated detection

  • SecPortal's external scanner port-scan module probes TCP 6443, 8443, 10250, 2379, and 2380 across every verified domain and host in scope. Port 6443 is registered in the scanner port catalogue as kubernetes-api, and the tech-fingerprint module identifies kubernetes through banner and port signals on 6443 and 10250. Every responding listener that is reachable from the public internet raises a critical-severity finding under the rule that the Kubernetes control plane should never be addressable from 0.0.0.0/0.
  • Continuous monitoring re-runs the external scan on a schedule, so a security-group edit, a Terraform apply, a managed-cluster public-endpoint flag flip, an autoscaler event, or a recovered host that reopens 6443 or 10250 reopens the finding rather than leaving the regression invisible until the next audit cycle.
  • Bulk import accepts Nessus, Burp, and CSV output from network-tier scanners and from Shodan and Censys exports that already list exposed Kubernetes API servers and kubelets, so the catalogue stays centralised even when a third-party tool is the original discovery source.

Manual verification

  • Use nmap -p 6443,8443,10250,10255,2379,2380 with service detection (-sV) and the http-headers and ssl-cert scripts against the external range to confirm the listener, the TLS certificate subject, and whether the response advertises Kubernetes on the HTTP banner.
  • Issue curl -sk https://<host>:6443/version against each reachable apiserver. A populated JSON response with major, minor, gitVersion, gitCommit, buildDate, goVersion, and platform is proof of a reachable Kubernetes control plane. Issue curl -sk https://<host>:6443/api/v1/pods to test whether system:anonymous can list pods. A populated PodList rather than a 401 or 403 is proof of an unauthenticated or weakly authenticated apiserver.
  • Issue curl -sk https://<host>:10250/pods against each reachable kubelet. A populated PodList without an Authorization header is proof of --anonymous-auth=true and --authorization-mode=AlwaysAllow. The same probe at /metrics returns node-level telemetry on a permissive kubelet.
  • For inside-the-cluster verification, run kubectl auth can-i --list --as=system:anonymous and kubectl auth can-i --list --as=system:unauthenticated to enumerate every permission the anonymous and unauthenticated users hold across the cluster. Audit ClusterRoleBindings whose subjects include system:anonymous, system:unauthenticated, system:authenticated, or empty subject lists. Cross-reference Shodan and Censys for the organisation's netblocks and known hostnames to see whether external observatories already list exposed control planes or kubelets.

How to fix it

Make the control plane unreachable from the public internet

For managed clusters, set the public endpoint flag to private and configure the source IP allowlist to a managed bastion service, a zero-trust access broker, a private VPC peering route, or a managed identity-aware proxy. For EKS, set endpointPublicAccess to false (or restrict publicAccessCidrs). For AKS, disable the public endpoint and use private cluster mode. For GKE, set private-cluster with master authorized networks scoped to a small allowlist. For self-managed clusters, block TCP 6443, 8443, 10250, 2379, and 2380 inbound at the perimeter firewall and the cloud security group for every host. There is no production scenario where the Kubernetes control plane should be reachable from 0.0.0.0/0.

Set --anonymous-auth=false on the apiserver and kubelet

Audit the apiserver flags (--anonymous-auth, --authorization-mode, --client-ca-file, --service-account-key-file) and the kubelet flags (--anonymous-auth, --authorization-mode, --client-ca-file) on every node and every cluster. The CIS Kubernetes Benchmark controls 1.2.1, 1.2.2, 4.2.1, and 4.2.2 cover the exact flag values. Where the cluster runs through a managed control plane that hides these flags, verify the equivalent posture through the hyperscaler audit log and the managed-cluster security configuration.

Remove anonymous bindings from RBAC

Audit ClusterRoleBindings and RoleBindings whose subjects include system:anonymous, system:unauthenticated, or system:authenticated. The system:authenticated group is especially dangerous because it includes every Service Account token that the apiserver issues, so a misconfigured Service Account turns a permissive system:authenticated binding into the same exposure as system:anonymous. Delete the binding, narrow the binding to a named ServiceAccount, or move the binding to a more restrictive Role scoped to a single Namespace.

Lock kubelet authentication and authorization

Set --anonymous-auth=false, --authorization-mode=Webhook, --client-ca-file pointing at the cluster CA, and --read-only-port=0 on every kubelet. Where a managed distribution sets these by default (kubeadm, EKS-managed nodes, GKE-managed nodes, AKS-managed nodes), confirm the values in the kubelet config rather than trusting the documentation. The kubelet on every node is its own listener and its own admission boundary; one mis-tuned node leaks every workload running on it.

Enforce TLS client certificates on etcd

Set --client-cert-auth=true on the etcd client endpoint (2379) and --peer-client-cert-auth=true on the peer endpoint (2380). Set --cert-file, --key-file, --trusted-ca-file, --peer-cert-file, --peer-key-file, and --peer-trusted-ca-file with TLS material issued from an internal certificate authority. Block 2379 and 2380 at the perimeter firewall, the cloud security group, and the node-level iptables so the listener is reachable only from the apiserver nodes that need it.

Apply Pod Security Admission and admission policies

Set a PodSecurity admission baseline of restricted on every Namespace that does not need privileged workloads, baseline on every Namespace that does need them under documented exception, and use audit or warn mode on transition Namespaces. Deploy Kyverno or OPA Gatekeeper policies that forbid privileged: true, hostPath volumes pointing at /, hostNetwork, hostPID, hostIPC, and ClusterRoleBindings whose subjects include system:anonymous or system:unauthenticated. Pair the policy with a Kyverno PolicyReport export or Gatekeeper audit endpoint so the violations land on the finding record rather than only in cluster state.

Restrict ServiceAccount token automounting and bind durations

Set automountServiceAccountToken: false on Pods and ServiceAccounts that do not need to call the apiserver. For Pods that do, use BoundServiceAccountTokenVolume so the token is bound to a Pod lifetime and an audience rather than a long-lived secret. Rotate Service Account signing keys on a defined cadence and revoke compromised tokens immediately. Audit every ClusterRoleBinding whose subject is a ServiceAccount to confirm the principle of least privilege.

Enable audit logging and runtime detection

Turn on the kube-apiserver audit policy with a Metadata level for read requests and a RequestResponse level for write requests against kube-system, system:masters, and ClusterRoleBindings. Forward the audit stream to the SIEM or detection pipeline, and pair the listener with detection rules for Pod creation that requests privileged: true, hostPath volumes pointing at /, RBAC mutations that grant cluster-admin to a new subject, and exec into Pods in kube-system. Runtime detection (Falco, Tetragon, Defender for Containers, Sysdig Secure, Wiz Runtime, Aqua) on the node tier is the second line that catches a successful control-plane compromise that the network controls missed.

Operationalising the fix on the workspace

Closing an exposed Kubernetes API server and rebuilding a managed-cluster endpoint allowlist is rarely the hard part for an experienced platform or cloud security engineer. The operational hard part is keeping the closure intact across cluster rebuilds, autoscaling events, mergers and acquisitions, new cloud accounts, new self-managed control planes, new edge clusters, and the long tail of legacy clusters that nobody currently lists in a primary asset inventory. The workspace pattern below is what AppSec, cloud security, platform engineering, vulnerability management, and security engineering teams use to keep an exposed-Kubernetes-API finding from coming back six months later on a new cluster or a recovered backup image.

Surface every reachable apiserver and kubelet on one finding record

External scanning and bulk import land every exposed-Kubernetes-API detection on the findings management record with CVSS, the discovered host, the captured /version response, the apiserver build, the platform, the scan timestamp, and the named owner so the catalogue is a single source of truth rather than a Shodan tab, a spreadsheet, and a Slack thread.

Inspect cluster posture through authenticated scans

Where the team has authorisation and a kubeconfig with read-only access, the authenticated scanning path pairs external-listener evidence with the per-cluster hardening record. Credentials for authenticated runs live in encrypted credential storage scoped to a verified domain so the same kubeconfig is not copied across spreadsheets, chat threads, and pull-request bodies.

Run remediation as a tracked workflow

Each finding flows through the remediation tracking workflow with severity, target close date, named owner, and the agreed compensating control (for example, an inbound source allowlist while a private bastion is provisioned, a temporary firewall rule while the master authorized networks list is narrowed, or a Kyverno enforce-mode rollout while audit-mode coverage is validated). Permanent exceptions enter the eight-field exception register with a named approver, a rationale, an effective period, and a renewal cadence.

Verify the fix with retest

Once the firewall rule, the security-group edit, the apiserver flag change, the kubelet config update, the managed-cluster endpoint flip, or the etcd TLS rollout completes, the retesting workflow re-runs the external scan, confirms the listener is gone or that the responding service now refuses anonymous requests on a private interface, and stamps the closure with timestamped evidence rather than a verbal sign-off in a stand-up.

Catch regressions through continuous monitoring

Scheduled continuous monitoring re-scans of verified domains reopen the finding if a Kubernetes control plane returns to the public internet, if a new self-managed cluster spins up with the apiserver bound to 0.0.0.0:6443, or if a new cloud account joins the estate with an open kubelet port. A Terraform apply, a managed-cluster public-endpoint flag flip, an autoscaler event, or a recovered backup image does not silently reintroduce the exposure.

Pair the apiserver finding with the node-level exposure

A permissive kube-apiserver, a permissive kubelet, an unauthenticated etcd, and a mounted Docker socket or runtime socket on a node are the same root-equivalence pattern at different layers. Treating them as one finding family on the workspace record, with the cluster as the affected asset and the listener type or RBAC binding as the evidence, keeps the queue ordered against real residual risk rather than reading three unrelated queues. The same record flows through container image vulnerability remediation and Kubernetes security finding remediation where appropriate.

Carry the audit trail through closure

The activity log records who opened, scoped, remediated, and verified each exposed-Kubernetes-API finding, so the next ISO 27001, SOC 2, PCI DSS, NIST CSF 2.0, or cyber-insurance review reads the operating evidence from the record rather than from reconstructed memory, archived ticket exports, or screenshots taken weeks after the fact. The compliance tracking crosswalk maps each finding to the relevant control catalogue so audit fieldwork reads from the same record.

Communicate posture in leadership language

When cluster exposure sits inside a broader programme review, the Claude-drafted AI report composes a leadership-ready narrative from the open and closed findings, the remediation cadence, and the linked compliance evidence. The same record drives security leadership reporting without rebuilding the slide deck by hand each quarter.

Where the exposure usually lands

Exposed Kubernetes API is not a single shape across the enterprise estate. The conversation a cloud security team has with a platform team about a managed EKS, AKS, or GKE cluster with a public endpoint differs from the conversation an AppSec team has with a developer about a k3s install on an edge device, or the conversation a vulnerability management team has with a platform owner about a kops or kubeadm self-managed control plane left behind by a prior team. The four contexts below are the most common landing places, and each one needs a slightly different remediation playbook surfaced on the same finding record.

Managed clusters with public endpoints

EKS, AKS, GKE, DOKS, Linode Kubernetes Engine, and other managed control planes are the most common modern exposure. The hyperscaler hardens the control-plane binaries, but the public-endpoint flag and the source IP allowlist remain operator-controlled. Set the endpoint to private, use a managed bastion service or zero-trust access broker for human operators, and scope master authorized networks to a small allowlist tracked as configuration evidence on the finding record.

Self-managed control planes on cloud VMs

kops, kubeadm, k3s, RKE, RKE2, Talos, and home-grown apiserver deployments on EC2, Compute Engine, Azure VM, and DigitalOcean droplet stacks make up the bulk of historic exposure events. Replace standing 0.0.0.0/0 security-group rules with private subnets, security-group source restrictions, and managed bastion access for human operators. Move long-lived production workloads onto a managed orchestrator where the network boundary is built in and the per-node listener is not directly addressable.

Kubelet and read-only-port exposure on every node

A reachable kubelet on TCP 10250 with --anonymous-auth=true or --authorization-mode=AlwaysAllow bypasses the apiserver entirely and exposes /pods, /containerLogs, /exec, /run, and /metrics on every node. A reachable read-only port on TCP 10255 (deprecated since Kubernetes 1.10 but still set on long-lived nodes) exposes the same telemetry without any authentication. Set --anonymous-auth=false, --authorization-mode=Webhook, and --read-only-port=0 on every kubelet, and verify the values per node rather than per cluster.

Edge, IoT, and legacy clusters

Long-lived virtual machines, vendor-distributed appliances, customer-managed instances at acquired companies, k3s installs on edge devices and IoT gateways, and home-grown installs run a Kubernetes release several versions behind the upstream support window and frequently inherit the legacy plaintext-bind, anonymous-auth defaults. Track these clusters on the finding record with the kube-apiserver version, the kubelet version, the maintainer contact, and the upgrade path. Where the cluster cannot be upgraded immediately, isolate it behind a managed network access boundary and document the residual risk decision in the exception register.

Compliance impact

Where Kubernetes API posture lands inside the team

Exposed Kubernetes API sits across several teams at once. The internal security team owns the policy, the audit response, and the breach-notification posture. The cloud security team owns the cloud-account perimeter, the security-group baseline, the master authorized networks allowlist, and the private-endpoint architecture. The platform engineering team owns the cluster lifecycle, the kubelet baseline, the admission policy rollout, and the kubeadm or managed-cluster template. The vulnerability management team owns the backlog, the prioritisation, and the SLA. The security engineering team owns the hardening tooling, the Kyverno or OPA Gatekeeper rollout, the runtime detection coverage, and the Pod Security Admission baseline. SecPortal keeps the same finding visible to all of them on the workspace record so the handoffs do not lose context as the work moves between owners.

Find every exposed Kubernetes API and unauthenticated kubelet before an attacker does

SecPortal's external scanner probes TCP 6443, 8443, 10250, 2379, and 2380 across verified domains, identifies responding control-plane and kubelet listeners through port and banner signals, raises a critical-severity finding with port, service, and detected-software evidence, tracks remediation through closure, verifies the fix through retest, and re-runs continuously so a Terraform apply, a managed-cluster public-endpoint flag, a security-group widening, or a new self-managed control plane does not silently re-expose the cluster. Start free.

No credit card required. Free plan available forever.