Every meaningful action in a Kubernetes cluster passes through the API server. kubectl apply, a controller reconciling state, a kubelet reporting node status, an admission webhook, an attacker with a stolen token — all of it hits the same endpoint. That makes the API server the single most important thing to secure, because a weakness there bypasses every other control. You can run distroless images, enforce restricted Pod Security Standards, and segment the network perfectly, and none of it matters if an attacker can reach an API server that accepts anonymous requests or hands out cluster-admin through a sloppy RBAC binding. This guide covers the controls that matter most, in roughly the order an attacker would probe them.
Close anonymous and unauthenticated access
The API server processes requests through three stages: authentication, authorization, then admission. The first thing to verify is that unauthenticated requests get nothing. Historically the API server allowed anonymous requests mapped to the system:anonymous user, and misconfigured RBAC occasionally granted that user real permissions — a direct path to takeover.
# Anonymous requests should be rejected, not answered
kubectl get --raw / --as=system:anonymous
# Expect: Error from server (Forbidden)
On the API server, ensure --anonymous-auth=false unless a component genuinely needs the unauthenticated health endpoints, and never bind any role to system:anonymous or the system:unauthenticated group.
Scope RBAC — the flaw attackers actually use
Most real-world cluster compromises escalate through over-broad RBAC, not through a kernel exploit. A ServiceAccount token that can create pods can often schedule a privileged pod and pivot to the node; one that can read Secrets cluster-wide already holds your credentials. Audit for the dangerous grants:
# Who has cluster-admin?
kubectl get clusterrolebindings -o json \
| jq -r '.items[] | select(.roleRef.name=="cluster-admin")
| .metadata.name + " -> " +
([.subjects[]? | .kind + "/" + .name] | join(", "))'
# Any binding to the anonymous or all-authenticated groups is a red flag
kubectl get clusterrolebindings,rolebindings -A -o json \
| jq -r '.items[] | select(.subjects[]?.name
| test("system:unauthenticated|system:anonymous"))
| .metadata.name'
Apply the principle relentlessly: no wildcards in verbs or resources, no cluster-admin on ServiceAccounts, and use aggregated Roles scoped to namespaces. Disable auto-mounting of the default ServiceAccount token on workloads that never call the API:
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-access
automountServiceAccountToken: false
Turn on audit logging before you need it
When an incident happens, the first question is "what did the compromised identity do?" Without an audit policy, you cannot answer it. The API server supports a configurable audit policy that records who did what, when, and to which resource. At minimum, log metadata for all requests and full request/response bodies for sensitive resources:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Capture reads/writes of Secrets and RBAC at request level
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
- group: "rbac.authorization.k8s.io"
resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
# Everything else: metadata only, to keep volume manageable
- level: Metadata
Ship these logs off the control-plane node to a store an attacker cannot reach or tamper with. Logs that only exist on the machine you are investigating are logs you cannot trust.
Restrict the cloud metadata endpoint
On managed clusters, the node's cloud metadata service (the 169.254.169.254 link-local address) can hand out node IAM credentials. A pod that reaches it may assume the node's cloud role and escape the cluster boundary entirely. Block pod access to it with a NetworkPolicy and prefer per-workload cloud identity (IRSA on EKS, Workload Identity on GKE) over node-level roles:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata
namespace: apps
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # block cloud metadata
Lock the admission chain
Admission controllers run after authorization and can be a weakness or a strength. Ensure dangerous built-ins are not disabled and that mutating/validating webhooks fail closed. A webhook configured with failurePolicy: Ignore means that if the webhook is down, requests sail through unchecked — exactly when you least want it. Prefer failurePolicy: Fail for security-critical webhooks and give them tight timeouts.
Hardening checklist
| Control | Setting / check |
|---|---|
| Anonymous auth | --anonymous-auth=false; no anonymous RBAC |
| RBAC | No wildcards, no cluster-admin on ServiceAccounts |
| Token automount | automountServiceAccountToken: false where unused |
| Audit logging | Policy enabled; logs shipped off-node |
| TLS | Strong ciphers; rotate API server and etcd certs |
| Metadata endpoint | Blocked via NetworkPolicy; per-pod cloud identity |
| Admission webhooks | failurePolicy: Fail for security controls |
| API reachability | Private endpoint or restricted CIDR, not public |
How Safeguard helps
The API server's security is largely a configuration and RBAC problem, and configuration drifts. Safeguard's IaC scanning evaluates the RBAC, ServiceAccount, and admission manifests you commit — flagging wildcard permissions, cluster-admin bindings on ServiceAccounts, auto-mounted tokens on workloads that never use them, and webhooks that fail open — so the misconfiguration is caught in review rather than discovered during an incident. Griffin AI correlates these posture findings with reachability and known exploits, so an over-permissioned ServiceAccount attached to an internet-facing, vulnerable workload is escalated over a harmless one. Teams comparing cloud security platforms that assess control-plane posture can see the differences in Safeguard vs Wiz, and review plans on the pricing page.
The front door deserves the strongest lock. Create a free Safeguard account or read the documentation to audit your cluster's control-plane posture as code.