Back

K06: Broken Authentication Mechanisms - Prevention

Prevention Strategy Overview

Preventing broken authentication is less about a single control and more about making a verified, short-lived identity the only way in:

  1. Reject the unauthenticated—disable anonymous access on every component.
  2. Authenticate every component independently—API server, kubelet, and etcd.
  3. Give humans federated identity (OIDC/SSO with MFA), not shared static credentials.
  4. Give workloads short-lived, audience-bound tokens, not permanent secrets.
  5. Keep every credential short-lived, rotated, and revocable—and never publicly reachable.

Core Principles

1. Disable Anonymous Authentication

Stop unauthenticated requests from being downgraded to an identity. Set this on the API server and confirm the kubelet is not anonymous either.

# kube-apiserver: reject anything that fails all real authenticators
kube-apiserver \
  --anonymous-auth=false

# Verify no binding hands anything to anonymous groups:
$ kubectl get clusterrolebindings -o json \
  | jq '.items[] | select(.subjects[]?
        | .name=="system:anonymous" or .name=="system:unauthenticated")
        | .metadata.name'
# Expect NO results.

Some managed control planes keep a narrow anonymous allowance for health endpoints. That is acceptable when it is scoped to /healthz, /livez, /readyz only. What must never exist is a binding granting system:anonymous or system:unauthenticated access to real resources.

2. Authenticate and Lock Down the Kubelet

The kubelet API on port 10250 is a separate door. Require authentication and delegate authorization to the API server, and remove the always-allow escape hatch.

# kubelet configuration (KubeletConfiguration)
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false           # no anonymous kubelet access
  webhook:
    enabled: true            # authenticate bearer tokens via the API server
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook              # NOT AlwaysAllow; defer authz to the API server

Also restrict network reach: node ports (10250) should only be reachable from the control plane, never from workloads or the internet. Enforce with network policy / security groups and consider the NodeRestriction admission plugin so a kubelet credential can only affect its own node.

3. Protect etcd with Mutual TLS

etcd holds all cluster state and Secrets. It must require client-certificate authentication, use peer TLS, and never be reachable outside the control plane.

# etcd: require client certs and encrypt peer traffic
etcd \
  --cert-file=/etc/kubernetes/pki/etcd/server.crt \
  --key-file=/etc/kubernetes/pki/etcd/server.key \
  --client-cert-auth=true \
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
  --peer-client-cert-auth=true \
  --listen-client-urls=https://127.0.0.1:2379,https://:2379

Pair this with encryption at rest so a datastore or backup leak does not expose Secrets in the clear:

# EncryptionConfiguration referenced by --encryption-provider-config
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources: ["secrets"]
    providers:
      - aescbc:
          keys:
            - name: key1
              secret:    # ideally backed by a KMS provider
      - identity: {}

4. Use Short-Lived, Bound ServiceAccount Tokens

Prefer projected tokens issued by the TokenRequest API: they expire, are bound to a specific Pod and audience, and rotate automatically. Avoid legacy non-expiring token Secrets.

# A projected, audience-bound, short-lived token (auto-rotated by the kubelet)
apiVersion: v1
kind: Pod
metadata:
  name: worker
spec:
  serviceAccountName: worker-sa
  automountServiceAccountToken: false   # opt in explicitly below
  containers:
    - name: app
      image: myapp:1.2.3@sha256:
      volumeMounts:
        - name: api-token
          mountPath: /var/run/secrets/tokens
          readOnly: true
  volumes:
    - name: api-token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              audience: api            # scoped audience, not "any"
              expirationSeconds: 3600  # short-lived, then rotated

Turn off automatic mounting where API access is not needed, so a compromised container has no token to steal:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: no-api-access
automountServiceAccountToken: false   # default-deny at the ServiceAccount level

5. Integrate OIDC / SSO for Humans

Humans should authenticate through your identity provider with MFA, receiving short-lived tokens and group claims that map to RBAC—never a shared kubeconfig.

# kube-apiserver: trust an external OIDC identity provider
kube-apiserver \
  --oidc-issuer-url=https://idp.example.com \
  --oidc-client-id=kubernetes \
  --oidc-username-claim=email \
  --oidc-groups-claim=groups \
  --oidc-username-prefix="oidc:" \
  --oidc-groups-prefix="oidc:"

Benefits over shared credentials: MFA at the IdP, central revocation when someone leaves, short-lived id_tokens, and per-user audit attribution. Map IdP groups to least-privilege RBAC roles (see K03), never straight to system:masters.

6. Remove Static Token and Basic-Auth Files

These legacy mechanisms are static bearer credentials with no expiry. Remove the flags entirely.

# DELETE these from the API server startup if present:
#   --token-auth-file=/etc/kubernetes/tokens.csv
#   --basic-auth-file=/etc/kubernetes/basic-auth.csv
# Migrate those identities to OIDC (humans) or ServiceAccounts (workloads).

7. Manage Certificates: Short-Lived, Scoped, Rotated

Because Kubernetes has no certificate revocation list, control risk with short lifetimes and tight scope.

# Request a short-lived client cert via the CSR API
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: ci-deployer
spec:
  request: 
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 3600        # one hour, then re-issue
  usages: ["client auth"]

8. Do Not Expose the API Server or Dashboards Publicly

9. Strong Cloud-IAM-to-RBAC Mapping

On managed clusters, the cloud identity layer is part of authentication. Keep the mapping least-privilege.

10. Detect Authentication Weaknesses and Abuse

Audit for the conditions and watch for the signatures of abuse. (Detection depth is the subject of K05.)

# Configuration audit with a benchmark scanner:
$ kube-bench run --targets master,node   # flags anonymous-auth, kubelet authz, etc.

# Hunt for legacy long-lived SA token Secrets:
$ kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token

# Alert (via audit logs) on:
#   - successful requests from system:anonymous
#   - authentication from unexpected source IPs for a ServiceAccount
#   - use of a credential after the associated identity was deprovisioned

Prevention Checklist by Component

ComponentControlSetting
API serverNo anonymous access--anonymous-auth=false; no static token/basic-auth files
API serverHuman identityOIDC/SSO with MFA and group claims
KubeletAuthn + authzanonymous off, webhook authn, authorization.mode: Webhook
etcdMutual TLS, private--client-cert-auth=true, control-plane-only reachability, encryption at rest
WorkloadsShort-lived tokensProjected/bound tokens, automountServiceAccountToken: false by default
CertificatesShort-lived, scopedCSR API with expirationSeconds, rotation, no shared admin certs
Cloud IAMLeast-privilege mappingSpecific roles to scoped groups, workload identity federation

Key Takeaways

  1. Deny the anonymous — --anonymous-auth=false and no binding to system:unauthenticated.
  2. Authenticate every door — the kubelet and etcd need their own authentication, independent of the API server.
  3. Federate humans, scope workloads — OIDC/SSO with MFA for people, short-lived projected tokens for Pods.
  4. Short-lived beats revocable — expiry is the revocation Kubernetes certs don’t otherwise have; keep lifetimes small and rotate.
  5. Never expose the control plane — API server, kubelet, etcd, and dashboards stay behind network controls and authentication.

Next Steps