Image description

How to disable anonymous auth globally but keep /livez, /readyz, and /healthz accessible

Recently, a security-savvy colleague posed an interesting question: "Is it possible to disable anonymous access to the Kubernetes API server entirely, but still allow the /livez, /readyz, and /healthz endpoints to work?" 🤔

Honestly, I didn't know the answer offhand, so I dove into the Kubernetes source code and relevant KEPs (Kubernetes Enhancement Proposals). Here's what I found and how you can implement it.

The Default Scenario: Anonymous Access Enabled

As you probably know, by default, kube-apiserver allows anonymous authentication:

--anonymous-auth     Default: true

Enables anonymous requests to the secure port of the API server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.

When you set up a cluster using kubeadm with default settings, anonymous requests to health endpoints (and potentially others like /version, depending on default bindings) succeed:

# curl -k https://:6443/livez ; echo
ok

Looking at the audit logs, we can confirm the request is handled by system:anonymous:

{
  "kind": "Event",
  "apiVersion": "audit.k8s.io/v1",
  "level": "Metadata",
  "auditID": "2caca66d-ccec-4b1f-8116-86741193cdce", // Example ID
  "stage": "ResponseComplete",
  "requestURI": "/livez",
  "verb": "get",
  "user": {
    "username": "system:anonymous", // <--- Anonymous user
    "groups": [
      "system:unauthenticated"
    ]
  },
  "sourceIPs": [ "" ],
  "userAgent": "curl/7.88.1",
  "responseStatus": { "code": 200 },
  "requestReceivedTimestamp": "2025-04-13T14:41:33.630618Z", // Example timestamp
  "stageTimestamp": "2025-04-13T14:41:33.630908Z", // Example timestamp
  "annotations": {
    "authorization.k8s.io/decision": "allow",
    "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"system:public-info-viewer\"..."
  }
}

However, anonymous users can't access other API paths like /apis:

{
  // ... audit metadata ...
  "requestURI": "/apis",
  "verb": "get",
  "user": {
    "username": "system:anonymous",
    "groups": [ "system:unauthenticated" ]
  },
  "responseStatus": {
    "status": "Failure",
    "message": "forbidden: User \"system:anonymous\" cannot get path \"/apis\"",
    "reason": "Forbidden",
    "code": 403 // <--- Forbidden
  },
  // ... timestamps and annotations ...
  "annotations": {
    "authorization.k8s.io/decision": "forbid",
    "authorization.k8s.io/reason": ""
  }
}

While this default setup restricts access, leaving anonymous authentication enabled at all, even for specific paths, can be a security concern. Misconfigured RBAC or potential vulnerabilities could expose more than intended. 😟

The Solution: KEP-4633 and AuthenticationConfiguration

Digging into the Kubernetes source code led me to KEP-4633: Make anonymous authentication configuration endpoints configurable. This KEP addresses the exact concern of wanting to disable anonymous access globally while still allowing essential health checks (which don't necessarily need full TCP checks to be useful).

The solution, documented here, involves using an AuthenticationConfiguration object.

How to Implement It:

  1. Disable Global Anonymous Auth:
    Modify your kube-apiserver manifest (usually a static pod in /etc/kubernetes/manifests/) to include these flags:

    spec:
      containers:
      - command:
        - kube-apiserver
        # ... other flags ...
        - --anonymous-auth=false # <--- Disable global anonymous auth
        - --authentication-config=/etc/kubernetes/auth-config.yaml # <--- Point to the config file
        volumeMounts:
        # ... other volume mounts ...
        - mountPath: /etc/kubernetes/auth-config.yaml
          name: auth-config-volume
          readOnly: true
      volumes:
      # ... other volumes ...
      - name: auth-config-volume
        hostPath:
          path: /etc/kubernetes/auth-config.yaml
          type: File
    
  2. Create the Authentication Configuration File:
    Create the following file on your control plane node(s) at /etc/kubernetes/auth-config.yaml:

    # /etc/kubernetes/auth-config.yaml
    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    anonymous:
      # Enable anonymous access ONLY for the paths listed below
      enabled: true
      conditions:
      - path: /livez
      - path: /readyz
      - path: /healthz
    

    Ensure the kube-apiserver pod restarts to pick up the changes.

The Result: Granular Anonymous Access

With this configuration:

✅ Requests to /livez, /readyz, and /healthz are still allowed for system:anonymous. The audit log looks similar to the default case for these paths.

{
  // ... audit metadata ...
  "requestURI": "/livez",
  "verb": "get",
  "user": {
    "username": "system:anonymous", // <--- Still anonymous
    "groups": [ "system:unauthenticated" ]
  },
  "responseStatus": { "code": 200 }, // <--- Still OK
  // ... timestamps and annotations ...
}

❌ Requests to any other path (like /apis) without valid credentials will now receive a 401 Unauthorized instead of a 403 Forbidden, because the request isn't even treated as anonymous anymore.

# curl -k https://:6443/apis ; echo
Unauthorized

The audit log confirms this:

{
  // ... audit metadata ...
  "requestURI": "/apis",
  "verb": "get",
  "user": {}, // <--- No user identified (not even anonymous)
  "responseStatus": {
    "status": "Failure",
    "message": "Unauthorized",
    "reason": "Unauthorized",
    "code": 401 // <--- Unauthorized
  },
  // ... timestamps ...
}

Feature Availability

According to the KEP issue tracker, this feature graduated to Alpha in Kubernetes v1.31 and Beta in v1.32.

This approach provides a more secure posture by default, explicitly allowing anonymous access only where strictly necessary for health checks, without relying solely on RBAC for protection against anonymous requests to other endpoints. Now you can tighten security without breaking essential cluster monitoring! 👍


Connect with me on LinkedIn!