Skip to main content

Client Certificate Authentication Should Not Be Used For Users

More Info:

Kubernetes provides the option to use client certificates for user authentication. However as there is no way to revoke these certificates when a user leaves an organization or loses their credential, they are not suitable for this purpose. It is not possible to fully disable client certificate use within a cluster as it is used for component to component authentication.

Risk Level

Low

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify all client-certificate-based user configs on admin machines (evidence gathering)

    • On any machine with kubectl access, search typical kubeconfig locations for client-certificate auth (non-service-account):
      grep -RInE "client-certificate:|client-certificate-data:" \
      $HOME/.kube /etc/kubernetes/admin.conf /etc/kubernetes/controller-manager.conf \
      /etc/kubernetes/scheduler.conf 2>/dev/null
    • For each kubeconfig found, list users and auth methods:
      KUBECONFIG=$HOME/.kube/config kubectl config view --minify=false --raw
    • Decision: treat any context where user has client-certificate/client-certificate-data and is used by a human admin/script (not a core control-plane component) as non-compliant.
  2. Confirm which client-cert identities have cluster privileges

    • On any machine with kubectl access, list ClusterRoleBindings/RoleBindings that reference User subjects (these may be backed by client certs):
      kubectl get clusterrolebindings -o yaml | grep -B5 -A5 "kind: User"
      kubectl get rolebindings --all-namespaces -o yaml | grep -B5 -A5 "kind: User"
    • For each User subject, note whether that identity corresponds to a client certificate user from kubeconfig.
    • Decision: if such users are humans or automation that could instead use OIDC or another revocable mechanism, mark them for migration.
  3. Design and enable an alternative auth mechanism (e.g., OIDC) on every control plane node

    • On every control plane node, review the current API server flags to see if OIDC (or another centralized identity provider) is already configured:
      ps -eo args | grep kube-apiserver | grep -v grep
    • If OIDC is not enabled, follow your organization’s identity design to add the appropriate --oidc-* flags (issuer-url, client-id, etc.) to the kube-apiserver manifest, usually at:
      /etc/kubernetes/manifests/kube-apiserver.yaml
      • Editing this static pod manifest will restart the API server on that node.
    • Decision: only proceed once a revocable and centrally managed authentication method is configured and tested.
  4. Migrate human and automation users from client certs to OIDC (or chosen mechanism)

    • On any machine with kubectl access, for each non-compliant user:
      1. Create or identify the corresponding OIDC identity (subject/claim) in your IdP.
      2. Create/update Kubernetes RoleBinding/ClusterRoleBinding to grant the same roles to the new User/Group as identified by OIDC claims. Example (adapt to your IdP and roles):
        kubectl apply -f - <<'EOF'
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
        name: oidc-admins
        subjects:
        - kind: Group
        name: your-oidc-admin-group
        roleRef:
        kind: ClusterRole
        name: cluster-admin
        apiGroup: rbac.authorization.k8s.io
        EOF
    • Update kubeconfig used by each human/automation to use OIDC auth (e.g., via an exec plugin or cloud CLI) instead of client certificate fields.
  5. Remove or de-privilege client-certificate users once migration is confirmed

    • On any machine with kubectl access, after verifying affected users can authenticate and operate via OIDC:
      • Remove or adjust RBAC bindings that reference the old client-cert User identities:
        # Example: delete a specific ClusterRoleBinding
        kubectl delete clusterrolebinding <old-client-cert-crb-name>
      • Optionally, revoke or delete the issuing CA or intermediate used solely for those user client certs in your PKI system (outside the cluster), ensuring it does not impact component-to-component certs.
    • On admin machines, clean up outdated kubeconfigs that still contain client-certificate for humans or automation:
      sed -i.bak '/client-certificate-data:/d;/client-key-data:/d;/client-certificate:/d;/client-key:/d' $HOME/.kube/config
  6. Verify that client certificate authentication is no longer used for users (only components)

    • On any machine with kubectl access, re-check kubeconfigs:
      grep -RInE "client-certificate:|client-certificate-data:" \
      $HOME/.kube /etc/kubernetes/admin.conf /etc/kubernetes/controller-manager.conf \
      /etc/kubernetes/scheduler.conf 2>/dev/null
      Ensure only control-plane/component configs (e.g., kube-apiserver, kube-controller-manager, kube-scheduler, kubelet) use client certs.
    • Confirm RBAC no longer grants roles to user identities that are only accessible via client certs by re-running:
      kubectl get clusterrolebindings -o yaml | grep -B5 -A5 "kind: User"
      kubectl get rolebindings --all-namespaces -o yaml | grep -B5 -A5 "kind: User"
      and ensuring any remaining User subjects correspond only to component identities or identities authenticated via your new mechanism.
Using kubectl

kubectl cannot modify client certificate authentication for users because this is controlled by host-level configuration and API server flags on every control plane node. Make the necessary changes directly on those nodes as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Purpose:
# Enumerate where client-certificate authentication is used for *users*
# (as opposed to internal components), so it can be reviewed and phased
# out in favor of OIDC or other revocable mechanisms.
#
# Requirements:
# - Run on any machine with kubectl and (optionally) openssl/jq
# - KUBECONFIG configured with access to the cluster

set -euo pipefail

echo "=== 1) kubectl user identities (from current kubeconfig) ==="
# Machine: any machine with kubectl access
kubectl config view --flatten --output='jsonpath={.users[*].name}' | tr ' ' '\n' | sort -u | sed '/^$/d' || true

echo
echo "For each user above, inspect its auth mechanism:"
echo

# Show full users section so you can see client-certificate-data / client-key-data use
kubectl config view --flatten -o json | jq '.users' 2>/dev/null || kubectl config view --flatten

cat <<'EOF'

Interpretation:
- Any user entry that has fields like:
"client-certificate": "path/to/cert"
"client-key": "path/to/key"
or:
"client-certificate-data": "BASE64..."
"client-key-data": "BASE64..."
is using client certificate authentication.
- These are *suspect* for this control if they represent human users rather than components
(e.g. "alice", "devops-admin", "ci-bot" vs "kubernetes-admin", "system:admin", or node identities).

Action:
- Flag human/long‑lived users that rely on client certs and plan migration to OIDC or another
revocable method.

EOF

echo "=== 2) Detect client‑certificate auth in kubeconfig used by common automation accounts ==="
echo "Checking for client-certificate usage in typical locations (~/.kube/config, KUBECONFIG)..."
echo

for cfg in "$HOME/.kube/config" ${KUBECONFIG:-}; do
[ -f "$cfg" ] || continue
echo "---- $cfg ----"
if grep -E 'client-certificate|client-certificate-data' "$cfg" >/dev/null 2>&1; then
echo "POTENTIAL PROBLEM: client-certificate auth is in use in this kubeconfig."
grep -nE 'name:|user:|client-certificate|client-certificate-data' "$cfg" | sed 's/^/ /'
else
echo "OK: no client-certificate fields found in this kubeconfig (may be using tokens/OIDC)."
fi
echo
done

cat <<'EOF'

Interpretation:
- Lines under a user that reference client-certificate / client-certificate-data indicate use
of client cert auth. If those users are humans or long-lived service accounts, they are
non-compliant with this control.
- Kubeconfigs that use:
- exec: plugins (e.g. aws-iam-authenticator, gcloud, az) or
- id-token / access-token / auth-provider: oidc
are generally OK for this control.

EOF

echo "=== 3) Identify client‑certificate usage from apiserver audit logs (if available) ==="
echo "NOTE: This is optional and depends on audit logging configuration and log access."
echo " Run the following on every control plane node that has API server audit logs:"
cat <<'EOF'

# On every control plane node (adjust paths to your environment):

AUDIT_DIR="/var/log/kubernetes/audit" # or /var/log/kube-apiserver, /var/log/apiserver, etc.
LOG_FILE_PATTERN="*.log"

# Example grep to show client certificate subjects seen by the apiserver:
sudo find "$AUDIT_DIR" -name "$LOG_FILE_PATTERN" -type f -print0 2>/dev/null | \
xargs -0 sudo grep -Eo '"user":{"username":"[^"]+","groups":\[[^]]*\],"extra":\{[^}]*"authentication.kubernetes.io/x509name":"[^"]+"' | \
sed 's/.*"username":"\([^"]\+\)".*"authentication.kubernetes.io\/x509name":"\([^"]\+\)".*/USERNAME=\1 X509_SUBJECT=\2/' | \
sort -u

EOF

cat <<'EOF'

Interpretation:
- Any USERNAME/X509_SUBJECT pairs you see there are identities that authenticated
with an X.509 client certificate.
- If those usernames correspond to human or long‑lived users (as opposed to
system: nodes, controllers, or internal components), they are in scope for this finding.

EOF

echo "=== 4) Summary: what indicates a problem? ==="
cat <<'EOF'
You have a *problem* for this control when:
- A human user or long‑lived non‑system identity (e.g. "alice", "team-admin",
"jenkins-prod") in kubeconfig or in audit logs is authenticated via a client
certificate (client-certificate / client-certificate-data).
You are *aligned* with the control when:
- Humans and long‑lived identities authenticate using OIDC or another revocable
mechanism (tokens tied to IdP or short‑lived cloud IAM credentials), and
- Client certificate authentication is only used for internal Kubernetes
components where it cannot be disabled.

This script does not and cannot automatically fix the issue; it only surfaces
where client certificate auth is being used so you can plan migration to OIDC
or another revocable mechanism per the benchmark guidance.
EOF