> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Certificate Authentication Should Not Be Used For Users

### More Info:

Client certificates are hard to revoke and manage, making them weak for user authentication. Use OIDC instead.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify how users currently authenticate**
           * On any machine with access to kubeconfig files (e.g. admin workstation, bastion):
             ```sh theme={null}
             grep -RniE 'client-certificate|client-key|auth-provider: oidc|idp-issuer-url|id-token' ~/.kube /etc/kubernetes 2>/dev/null
             ```
           * Review the output to determine which user contexts use `client-certificate`/`client-key` versus OIDC (`auth-provider: oidc` or `exec` plugins that obtain OIDC tokens).

        2. **Inspect API server authentication configuration**
           * On every control plane node:
             ```sh theme={null}
             grep -E -- '-(--client-ca-file|--requestheader-client-ca-file|--oidc-issuer-url|--oidc-client-id|--oidc-username-claim|--oidc-groups-claim)' \
               /etc/kubernetes/manifests/kube-apiserver.yaml
             ```
           * Confirm whether `--client-ca-file` is configured (enables client cert auth) and whether OIDC flags (`--oidc-issuer-url`, `--oidc-client-id`, etc.) are already present.

        3. **Determine which client certificate subjects correspond to human users**
           * On any machine that has user client certificates (often where kubeconfigs are stored):
             ```sh theme={null}
             find ~/.kube -type f \( -name '*.crt' -o -name '*.pem' \) -print -exec openssl x509 -in {} -noout -subject -issuer -enddate \;
             ```
           * For each certificate found in kubeconfigs used by people (not components like kubelets/controllers), record the Subject (CN/O) and map them to real users and groups in your identity system.

        4. **Plan and, if appropriate, implement migration of human users to OIDC**
           * Work with your identity provider team to either confirm an existing OIDC integration or define: issuer URL, client ID, redirect URI, groups/username claims.
           * Update user kubeconfigs (on user machines) to remove `client-certificate`/`client-key` and configure OIDC instead, per your IdP’s guidance (e.g. `auth-provider: oidc` or an `exec` plugin).
           * Keep client certificate access in place during a transition window so users can switch without lockout.

        5. **Restrict or disable client certificate authentication for users (post‑migration)**
           * On every control plane node, before changing anything, back up the manifest:
             ```sh theme={null}
             cp -p /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak
             ```
           * Edit `/etc/kubernetes/manifests/kube-apiserver.yaml` and, following your migration plan:
             * Ensure any `--client-ca-file` referenced is only used for non‑user components (e.g. kubelet, front-proxy) and that certificates issued for human users are no longer trusted by that CA; **or**
             * Point `--client-ca-file` to a CA that signs only component certs, not human user certs; **or**, if you have an alternative authentication mechanism for all access, remove `--client-ca-file`.
           * Be aware: saving this file will cause the kube-apiserver static pod to be restarted on that control plane node.

        6. **Verify that human user access no longer depends on client certificates**
           * On any machine with user kubeconfigs, for a migrated user:
             ```sh theme={null}
             kubectl --context <OIDC-user-context> auth can-i get pods -A
             ```
           * Attempt the same with a kubeconfig that previously used a client certificate for that user but no longer has OIDC configured; it should now fail.
           * On every control plane node, confirm the running apiserver has the intended flags:
             ```sh theme={null}
             crictl ps | grep kube-apiserver
             crictl inspect $(crictl ps -q --name kube-apiserver) | grep -E '"--client-ca-file=|--oidc-issuer-url=|--oidc-client-id='
             ```
           * Confirm that only non‑user components (e.g. kubelets, front-proxy) use client certificate authentication and that human users authenticate via OIDC.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to change client certificate authentication for users because this configuration lives in the kube-apiserver static pod manifest on each control plane node at `/etc/kubernetes/manifests/kube-apiserver.yaml`. To review and adjust this setting, follow the guidance in the Manual Steps section on the control plane nodes directly.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Purpose: Report use of client-certificate auth for *users* so it can be
        # reviewed and migrated to OIDC or other mechanisms.
        #
        # Requirements:
        #   - Run on any machine with kubectl access and cluster-admin privileges.
        #   - kubectl must be configured to talk to the target cluster.

        set -euo pipefail

        echo "=== Cluster-wide authentication configuration review ==="
        echo

        ###############################################################################
        # 1) Inspect kube-apiserver authentication flags (from static pod manifest)
        ###############################################################################
        echo "[1] kube-apiserver authentication flags"

        # Try all namespaces in case control-plane is not in kube-system
        for ns in kube-system kube-public default; do
          if kubectl get pod -n "$ns" 2>/dev/null | grep -q 'kube-apiserver'; then
            APISERVER_NS="$ns"
            break
          fi
        done

        if [ -z "${APISERVER_NS:-}" ]; then
          echo "  !! No kube-apiserver pod found via kubectl (managed control-plane or restricted access)."
          echo "     Review /etc/kubernetes/manifests/kube-apiserver.yaml directly on every control plane node."
        else
          echo "  Using namespace: $APISERVER_NS"
          echo

          # Extract command-line flags from kube-apiserver container spec
          kubectl get pod -n "$APISERVER_NS" -l component=kube-apiserver -o jsonpath='{range .items[*]}Name: {.metadata.name}{"\n"}Command: {.spec.containers[0].command}{"\n\n"}{end}' 2>/dev/null \
            || kubectl get pod -n "$APISERVER_NS" -o jsonpath='{range .items[*]}Name: {.metadata.name}{"\n"}Command: {.spec.containers[0].command}{"\n\n"}{end}'

          echo
          echo ">>> Review for these flags indicating client certificate auth is enabled:"
          echo "    --client-ca-file=..."
          echo "    --requestheader-client-ca-file=..."
          echo
          echo "    Presence of these flags means the API server accepts client certificates."
          echo "    You must determine whether they are used for *human users* vs nodes/services."
        fi

        ###############################################################################
        # 2) List client-certificate usage in kubeconfigs (users & contexts)
        ###############################################################################
        echo
        echo "[2] kubeconfig objects in the cluster that reference client certificates"
        echo

        # Get all kubeconfig-like Secrets and ConfigMaps in kube-system and default
        # (common locations), and show entries that use client-certificate-data/authority
        for ns in kube-system default; do
          echo "  Namespace: $ns"
          echo "  - Secrets with potential kubeconfigs using client certificates:"
          kubectl get secrets -n "$ns" -o jsonpath='{range .items[?(@.type=="Opaque")]}{.metadata.name}{"\n"}{end}' 2>/dev/null | while read -r s; do
            [ -z "$s" ] && continue
            DATA_KEYS=$(kubectl get secret "$s" -n "$ns" -o jsonpath='{range .data}{@}{" "}{end}' 2>/dev/null | base64 -d 2>/dev/null || true)
            if echo "$DATA_KEYS" | grep -q "client-certificate-data"; then
              echo "    * $s (contains client-certificate-data)"
            fi
          done

          echo "  - ConfigMaps that may store kubeconfigs with client certificates:"
          kubectl get configmaps -n "$ns" -o name 2>/dev/null | while read -r cm; do
            CONTENT=$(kubectl get "$cm" -n "$ns" -o yaml 2>/dev/null)
            if printf '%s\n' "$CONTENT" | grep -q "client-certificate-data"; then
              echo "    * ${cm#configmap/} (contains client-certificate-data)"
            fi
          done
          echo
        done

        echo ">>> Any kubeconfig used by *human users* that contains client-certificate-data"
        echo "    indicates client certificate authentication is in use for users and should"
        echo "    be considered for migration to OIDC or another revocable mechanism."

        ###############################################################################
        # 3) Enumerate cluster RoleBindings/ClusterRoleBindings tied to cert-based users
        ###############################################################################
        echo
        echo "[3] RoleBindings/ClusterRoleBindings granted to likely certificate-based users"
        echo

        echo "  ClusterRoleBindings:"
        kubectl get clusterrolebindings -o json | \
          jq -r '.items[] | select(.subjects!=null) |
                 .metadata.name as $rbname |
                 .subjects[] |
                 select(.kind=="User") |
                 "\($rbname) -> User: \(.name)"' 2>/dev/null || echo "    (jq not available; skipping detailed subject parsing)"

        echo
        echo "  RoleBindings (all namespaces):"
        kubectl get rolebindings --all-namespaces -o json | \
          jq -r '.items[] | select(.subjects!=null) |
                 .metadata.namespace as $ns |
                 .metadata.name as $rbname |
                 .subjects[] |
                 select(.kind=="User") |
                 "\($ns)/\($rbname) -> User: \(.name)"' 2>/dev/null || echo "    (jq not available; skipping detailed subject parsing)"

        echo
        echo ">>> Users listed above are authenticated somehow (certs, OIDC, etc.)."
        echo "    Cross-reference these users with your known OIDC identities."
        echo "    Any user principals that are *not* OIDC identities likely rely on"
        echo "    client certificates or other non-OIDC mechanisms."

        ###############################################################################
        # 4) Check for OIDC configuration on kube-apiserver
        ###############################################################################
        echo
        echo "[4] OIDC configuration on kube-apiserver (from pod spec)"
        echo

        if [ -n "${APISERVER_NS:-}" ]; then
          kubectl get pod -n "$APISERVER_NS" -l component=kube-apiserver -o jsonpath='{range .items[*]}Name: {.metadata.name}{"\n"}Args: {.spec.containers[0].command}{"\n\n"}{end}' 2>/dev/null \
            || kubectl get pod -n "$APISERVER_NS" -o jsonpath='{range .items[*]}Name: {.metadata.name}{"\n"}Args: {.spec.containers[0].command}{"\n\n"}{end}'

          echo
          echo ">>> Look for these OIDC flags:"
          echo "    --oidc-issuer-url"
          echo "    --oidc-client-id"
          echo "    --oidc-username-claim"
          echo "    --oidc-groups-claim"
          echo
          echo "    Absence of OIDC flags, combined with presence of client-ca-file and"
          echo "    user kubeconfigs using client certificates, indicates a problematic"
          echo "    reliance on client certificates for user authentication."
        else
          echo "  (kube-apiserver not visible via kubectl; verify OIDC flags in"
          echo "   /etc/kubernetes/manifests/kube-apiserver.yaml on every control plane node.)"
        fi

        echo
        echo "=== Interpretation summary ==="
        echo "- Problematic state for this check:"
        echo "  * kube-apiserver has --client-ca-file configured AND"
        echo "  * Human user kubeconfigs (outside node/kubelet/system accounts) contain"
        echo "    client-certificate-data AND"
        echo "  * OIDC flags are absent or not used for those users."
        echo
        echo "- Acceptable direction for this check:"
        echo "  * Human users authenticate via OIDC (or another centrally revocable"
        echo "    mechanism), with no remaining human user kubeconfigs relying on"
        echo "    client certificates."
        ```

        **What output indicates a problem**

        * `--client-ca-file` is present in the kube-apiserver command line **and**
        * One or more kubeconfigs used by human users (for example, developer/admin configs stored in Secrets/ConfigMaps or known off-cluster files) contain `client-certificate-data` **and**
        * The kube-apiserver command line does **not** show OIDC flags (`--oidc-issuer-url`, `--oidc-client-id`, etc.), or you know the listed users in RoleBindings/ClusterRoleBindings are authenticating via client certificates instead of OIDC.

        This script only reports state; migration away from client certificates to OIDC must be planned and executed manually.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
