> ## 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:

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 GKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify how users currently authenticate (any machine with kubectl access)**
           * List configured clusters and contexts and see which client certs are in use:
             ```bash theme={null}
             kubectl config view --raw
             kubectl config get-contexts
             ```
           * For each context that points at this cluster, inspect the user entry and note any `client-certificate` / `client-key` fields:
             ```bash theme={null}
             kubectl config view --raw -o jsonpath='{range .users[*]}{.name}{"\n"}{.user.client-certificate}{"\n"}{.user.client-key}{"\n\n"}{end}'
             ```
           * Treat any non-empty `client-certificate`/`client-key` paired with a human user as in-scope for this finding.

        2. **Confirm whether these certificates are human-user identities vs components (any machine with kubectl access)**
           * Decode each certificate and review its subject/issuer for human names, email, or non-system groups:
             ```bash theme={null}
             # Replace with the actual client-certificate path found above
             openssl x509 -in /absolute/path/to/client.crt -noout -text | sed -n '1,40p'
             ```
           * Certificates with subjects like `CN=kube-apiserver`, `system:node:*`, or other system/service accounts are expected; certificates with real names or generic “admin”/“devops” etc. are typically human users and should not be used.

        3. **Review GKE authentication configuration and plan an alternative (any machine with gcloud access)**
           * Check whether alternative mechanisms are enabled (e.g., GKE OIDC / workload identity / IAM integration):
             ```bash theme={null}
             gcloud container clusters describe CLUSTER_NAME --zone=CLUSTER_ZONE \
               --format="yaml(masterAuth,authenticatorGroupsConfig,workloadIdentityConfig,clusterIdentity)"
             ```
           * Decide which non-certificate mechanism to use for humans (for CIS: OIDC or GKE/IAM-based auth). Ensure your IdP / IAM groups and RBAC bindings are or can be configured to replace the current certificate-based access.

        4. **Migrate human users off client certificates (any machine with kubectl and gcloud access)**
           * For each affected human user:
             1. Provision or enable OIDC / GKE/IAM-based access for that person.
             2. Create or update Kubernetes RBAC bindings for their new identity:
                ```bash theme={null}
                kubectl create clusterrolebinding USERNAME-oidc-admin \
                  --clusterrole=cluster-admin \
                  --user="IDP_OR_IAM_SUBJECT"
                ```
             3. Update their kubeconfig to remove the client certificate and use the new auth method (example for GKE using `gcloud`):
                ```bash theme={null}
                gcloud container clusters get-credentials CLUSTER_NAME --zone=CLUSTER_ZONE
                ```
             4. Have the user verify access and then delete any contexts that still reference client certs from their kubeconfig:
                ```bash theme={null}
                kubectl config delete-user OLD_CERT_USER_NAME
                kubectl config delete-context OLD_CERT_CONTEXT_NAME
                ```

        5. **Validate that no human users rely on client certificates anymore (any machine with kubectl access)**
           * Re-scan kubeconfigs used by administrators and automation:
             ```bash theme={null}
             kubectl config view --raw -o jsonpath='{range .users[*]}{.name}{"\n"}{.user.client-certificate}{"\n\n"}{end}'
             ```
           * Confirm that any remaining client-certificate users correspond only to system / component identities and not to individual humans.

        6. **Optional: tighten server-side reliance on client certificates for users (every control plane node – review only)**
           * Inspect the control plane static pod manifest but do not change it solely to disable certificates (they are required for component-to-component auth):
             ```bash theme={null}
             sudo cat /etc/kubernetes/manifests/kube-controller-manager.yaml
             ```
           * Verify that you are not adding new human-user client certificates here or elsewhere on control-plane nodes. Any further restriction of client certificate issuance and lifecycle should be done through your PKI / IAM processes, not by disabling component certificates in this manifest.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to remediate this finding because it requires changing the kube-controller-manager static pod manifest on each control plane node at `/etc/kubernetes/manifests/kube-controller-manager.yaml`. To address it, follow the guidance in the Manual Steps section for host-level configuration and authentication method changes (for example, moving to OIDC).
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Check for use of client certificate authentication for USERS in a GKE cluster.
        # Runs with kubectl from any machine that has access to the cluster.
        #
        # Requirements:
        #   - kubectl configured with cluster-admin level access
        #   - jq installed
        #
        # This script does NOT make any changes. It only reports findings for review.

        set -euo pipefail

        echo "=== 1. Cluster info ==="
        kubectl config view --minify -o json | jq '{cluster: .clusters[0].name, user: .users[0].name, context: .contexts[0].name}' || true
        echo

        ###############################################################################
        # 2. Detect kubectl contexts/users that use client certificates
        ###############################################################################
        echo "=== 2. kubectl users in local kubeconfig using client certificates ==="
        kubectl config view -o json | jq -r '
          .users[]?
          | {
              name: .name,
              clientCertificate: (.user."client-certificate" // ""),
              clientCertificateData: (if .user."client-certificate-data" then "INLINE" else "" end),
              authProvider: (.user.auth-provider.name // ""),
              execCommand: (.user.exec.command // "")
            }
        ' | sed 's/^/  /'
        cat <<'EOF'

        Interpretation:
        - Any user entry with "clientCertificate" set to a path OR "clientCertificateData": "INLINE"
          indicates a context using client certificate authentication.
        - For HUMAN users, this is against CIS GKE 2.1.1 and should be migrated to OIDC or another
          revocable mechanism.
        - Service accounts using "token" or "auth-provider" (e.g., gcp) are NOT in scope of this finding.

        Action:
        - Identify which of the above "name" entries correspond to human users.
        - Flag those with clientCertificate/clientCertificateData for remediation (migrate to OIDC).

        EOF

        ###############################################################################
        # 3. Inspect cluster RoleBindings/ClusterRoleBindings that might imply user cert use
        ###############################################################################
        echo "=== 3. ClusterRoleBindings & RoleBindings that bind to explicit user subjects ==="
        echo "ClusterRoleBindings with user subjects:"
        kubectl get clusterrolebindings -o json \
          | jq -r '
            .items[]
            | {name: .metadata.name, subjects: (.subjects // [])}
            | select(.subjects[]? | select(.kind == "User"))
          ' 2>/dev/null | sed 's/^/  /' || echo "  None found"
        echo

        echo "RoleBindings with user subjects (all namespaces):"
        kubectl get rolebindings --all-namespaces -o json \
          | jq -r '
            .items[]
            | {namespace: .metadata.namespace, name: .metadata.name, subjects: (.subjects // [])}
            | select(.subjects[]? | select(.kind == "User"))
          ' 2>/dev/null | sed 's/^/  /' || echo "  None found"
        cat <<'EOF'

        Interpretation:
        - Any subject with "kind": "User" represents a user principal that could be
          authenticated via a client certificate, password, or external IdP.
        - This output does NOT prove client certificate use, but:
          * If user names here match CNs used in client certs from section 2,
            that strongly indicates client certificate-based access for those roles.
        - Focus on bindings that grant high-privilege roles (cluster-admin, admin, edit, etc.).

        Action:
        - Cross-reference the user "name" values here with the client-certificate-based users
          from section 2 and with your identity directory.
        - Where those represent human users, plan to migrate them to OIDC-based authentication.

        EOF

        ###############################################################################
        # 4. GKE-specific: Check API server authentication configuration (read-only)
        ###############################################################################
        echo "=== 4. GKE API server authn-related flags (read-only view) ==="
        echo "NOTE: On managed GKE, API server flags cannot be changed from nodes; this is informational only."
        echo "Attempting to show current API server container command/args from kube-system (may be empty on Autopilot/GKE managed masters)..."
        kubectl -n kube-system get pod -l component=kube-apiserver -o json 2>/dev/null \
          | jq -r '
            .items[]?
            | {
                name: .metadata.name,
                node: .spec.nodeName,
                command: .spec.containers[0].command,
                args: .spec.containers[0].args
              }
          ' | sed 's/^/  /' || echo "  API server pods are not visible in this cluster (expected for fully managed control plane)."
        cat <<'EOF'

        Interpretation:
        - On GKE, you generally cannot disable client certificate auth for control-plane components.
        - This section is NOT about component-to-component auth; it is acceptable and required.
        - The CIS requirement is about avoiding client certificates for HUMAN users, not internal components.

        EOF

        ###############################################################################
        # 5. Optional: Extract CNs from known client certificates (if accessible)
        ###############################################################################
        echo "=== 5. Optional: Inspect local client certificate files referenced in kubeconfig ==="
        echo "Looking for client-certificate file paths in kubeconfig and dumping subjects (CN)..."
        CERT_PATHS=$(kubectl config view -o json \
          | jq -r '.users[]?.user."client-certificate" // empty' | sort -u)

        if [ -z "$CERT_PATHS" ]; then
          echo "  No client-certificate file paths found in current kubeconfig."
        else
          for cert in $CERT_PATHS; do
            if [ -f "$cert" ]; then
              echo "  Certificate: $cert"
              openssl x509 -in "$cert" -noout -subject -issuer || echo "    Failed to read certificate"
            else
              echo "  Certificate path not found on disk: $cert"
            fi
          done
        fi

        cat <<'EOF'

        Interpretation:
        - Any certificate whose subject CN / O fields match human identities (e.g., personal names or
          email-like identifiers) indicates client certificate auth in use for users.
        - Certificates that clearly represent system identities (e.g., "system:masters", "system:node:*")
          are typically internal and not in scope of this finding.

        EOF

        ###############################################################################
        # 6. Summary guidance
        ###############################################################################
        cat <<'EOF'

        === 6. What indicates a PROBLEM (non-compliance with CIS GKE 2.1.1) ===

        Flag as needing remediation when you observe ALL of the following:
        1. A kubeconfig user entry (section 2) has:
           - "clientCertificate" set to a file path, OR
           - "clientCertificateData" present (INLINE),
           AND that user represents a HUMAN user (engineer, operator, etc.), not a system identity.

        2. That user has RoleBindings or ClusterRoleBindings (section 3) granting any meaningful
           Kubernetes permissions (especially cluster-admin/admin/edit/custom high-privilege roles).

        3. The certificate subject CN (section 5, where accessible) corresponds to a human identity.

        These cases violate the intent of CIS GKE 2.1.1: human users should not authenticate to
        the cluster using client certificates because they cannot be revoked reliably.
        They should instead be moved to OIDC (or another revocable, central identity mechanism).

        This script does NOT implement an automatic fix. Use its output to:
        - Inventory all human users currently depending on client certificates.
        - Prioritize migration of those users to OIDC-based authentication and update their kubeconfigs.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
