> ## 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 rotate, making them unsuitable for user authentication. Use stronger mechanisms such as OIDC instead.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify user client certificates currently in use**
           * Run on: any machine with `kubectl` access
           * List all kubeconfigs your users are using (for example, in a shared repo or profile locations), then inspect for client-certificate auth:
             ```bash theme={null}
             grep -Rni "client-certificate" .
             ```
           * For any kubeconfig found, confirm it is using client certs instead of tokens/OIDC:
             ```bash theme={null}
             yq '.users[].user | keys' /path/to/user-kubeconfig
             ```

        2. **Review and decide which authentication mechanisms to keep**
           * Run on: any machine with `kubectl` access
           * Get the current authentication configuration of the API server (in OKE this is managed by the control plane and configured via the cloud console/API/IaC, not directly on nodes):
             ```bash theme={null}
             kubectl get --raw /api/v1/namespaces/kube-system/configmaps/extension-apiserver-authentication || true
             ```
           * Decide that interactive human access will move to OIDC or other token-based methods supported by OKE (as per your organization’s IdP and OKE documentation) and that client certificates will only remain, if at all, for tightly controlled system components.

        3. **Configure OIDC (or other non-certificate user auth) in OKE**
           * Run in: Oracle Cloud Console or your Terraform/OCI CLI environment (cannot be done with `kubectl` or host-level edits on OKE control plane nodes).
           * In the OKE cluster configuration, enable OIDC (or your chosen IdP) for Kubernetes API authentication and obtain new kubeconfigs for users that use OIDC tokens instead of client certs, following OKE’s documented procedure.
           * Distribute updated kubeconfigs to users and ensure they can authenticate and perform their required actions using OIDC.

        4. **Phase out user client-certificate-based kubeconfigs**
           * Run on: any machine with `kubectl` access and wherever user configs are stored
           * Revoke or delete any PKI material used for user auth (CA or user certs) in your enterprise PKI/secret store according to your PKI process.
           * Remove or replace client-certificate fields from user kubeconfigs so they no longer rely on them:
             ```bash theme={null}
             yq 'del(.users[].user["client-certificate"]) | del(.users[].user["client-key"])' \
               /path/to/user-kubeconfig > /path/to/user-kubeconfig.no-certs
             mv /path/to/user-kubeconfig.no-certs /path/to/user-kubeconfig
             ```

        5. **Ensure OKE control plane is not issuing new user client certificates**
           * Run in: Oracle Cloud Console or your Terraform/OCI CLI environment
           * Review your cluster/IaC configuration for any automation that requests or distributes user X.509 client certificates (for example, scripts that call `openssl` and then embed cert/key into kubeconfigs). Disable or remove these flows so new user access is provisioned only with OIDC or other non-certificate methods supported by OKE.

        6. **Verification (no remaining user client certificate auth)**
           * Run on: any machine with `kubectl` access
           * Confirm that current kubeconfigs do not contain client certs:
             ```bash theme={null}
             grep -RniE "client-certificate|client-key" /path/to/all/kubeconfigs || echo "No client-certificate entries found"
             ```
           * Attempt a `kubectl` command using an old client-certificate-based kubeconfig; it should fail, while the OIDC-based kubeconfig continues to work:
             ```bash theme={null}
             KUBECONFIG=/path/to/old-cert-kubeconfig kubectl get ns || echo "Old client-cert kubeconfig no longer works"
             KUBECONFIG=/path/to/oidc-kubeconfig kubectl get ns
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot change how users authenticate to the OKE control plane, because this is configured at the managed control-plane / cloud console / IaC layer, not via Kubernetes API objects. To remediate this finding, follow the guidance in the Manual Steps section for updating your OKE control-plane authentication configuration.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Disable client certificate authentication for users in an Oracle Container Engine for Kubernetes (OKE) cluster
        # by rotating to, and enforcing, non-certificate based kubeconfig authentication.
        #
        # NOTE:
        # - OKE is a managed control plane: client certificate auth is configured in the cloud console / IaC, not on nodes.
        # - There is no supported OCI CLI/API call that directly "turns off" client-cert auth for users.
        # - This script therefore:
        #   * Audits existing kubeconfig files for client-certificate/-key usage.
        #   * Generates replacement kubeconfigs using OCI identity auth (token / instance principal / user principal),
        #     which OKE supports and which do not rely on client certificates.
        #   * Provides an audit step you can use to enforce non-cert-based kubeconfigs in your own access controls.
        #
        # This must be run from:
        #   - Any machine with:
        #       * OCI CLI installed and configured (https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm)
        #       * kubectl installed
        #
        # Idempotency:
        #   - Existing non-certificate kubeconfigs are left unchanged.
        #   - Existing certificate-based kubeconfigs are NOT overwritten automatically; new non-cert kubeconfigs
        #     are written alongside them and can be rolled out gradually.
        #   - Re-running the script will only re-generate non-cert kubeconfigs with the same names/locations.

        set -euo pipefail

        ########################
        # CONFIGURATION
        ########################

        # The OCI region containing the OKE cluster
        REGION="${REGION:-us-ashburn-1}"

        # Compartment OCID where the OKE cluster lives
        COMPARTMENT_OCID="${COMPARTMENT_OCID:-}"

        # Name or OCID of the OKE cluster to remediate
        # If CLUSTER_OCID is provided, it will be used directly.
        # Otherwise CLUSTER_NAME is used to look up the cluster OCID in the compartment.
        CLUSTER_NAME="${CLUSTER_NAME:-}"
        CLUSTER_OCID="${CLUSTER_OCID:-}"

        # Directory that holds kubeconfig files for human users and automation.
        # Adjust this to match your environment (e.g. /home, /etc/kubernetes, CI/CD secrets dir, etc.).
        KUBECONFIG_ROOT_DIR="${KUBECONFIG_ROOT_DIR:-/home}"

        # Output directory for non-certificate kubeconfigs we generate.
        # Per-user files will be stored under ${NON_CERT_KUBECONFIG_DIR}/<username>/
        NON_CERT_KUBECONFIG_DIR="${NON_CERT_KUBECONFIG_DIR:-/var/tmp/oke_non_cert_kubeconfigs}"

        # Authentication method for generated kubeconfigs:
        #   oci   - standard user principal via OCI config + auth token
        #   ipo   - instance principal (for workloads running on OCI compute)
        #   dpo   - dynamic group + instance principal (alias to ipo for kubectl use)
        OKE_AUTH_TYPE="${OKE_AUTH_TYPE:-oci}"

        ########################
        # HELPER FUNCTIONS
        ########################

        log() {
          printf '[%s] %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*" >&2
        }

        require_cmd() {
          if ! command -v "$1" >/dev/null 2>&1; then
            log "ERROR: Required command '$1' not found in PATH"
            exit 1
          fi
        }

        ########################
        # PRE-FLIGHT CHECKS
        ########################

        require_cmd oci
        require_cmd kubectl
        require_cmd jq
        require_cmd grep
        require_cmd find

        if [[ -z "$COMPARTMENT_OCID" && -z "$CLUSTER_OCID" ]]; then
          log "ERROR: You must set either COMPARTMENT_OCID+CLUSTER_NAME or CLUSTER_OCID."
          exit 1
        fi

        if [[ -z "$CLUSTER_OCID" ]]; then
          log "Resolving cluster OCID for cluster name '${CLUSTER_NAME}' in compartment '${COMPARTMENT_OCID}'..."
          CLUSTER_OCID="$(oci ce cluster list \
            --compartment-id "${COMPARTMENT_OCID}" \
            --region "${REGION}" \
            --all \
            --query "data[?name=='${CLUSTER_NAME}'].id | [0]" \
            --raw-output)"

          if [[ -z "$CLUSTER_OCID" || "$CLUSTER_OCID" == "null" ]]; then
            log "ERROR: Could not find cluster with name '${CLUSTER_NAME}' in compartment '${COMPARTMENT_OCID}'."
            exit 1
          fi
        fi

        log "Using cluster OCID: ${CLUSTER_OCID}"

        ########################
        # STEP 1: AUDIT EXISTING KUBECONFIGS FOR CLIENT CERTS
        ########################

        log "Scanning kubeconfig files under ${KUBECONFIG_ROOT_DIR} for client certificate usage..."

        CERT_BASED_FILES="$(find "${KUBECONFIG_ROOT_DIR}" -type f \( -name 'config' -o -name '*.kubeconfig' -o -name '*.kube' \) 2>/dev/null \
          | xargs -r grep -lE 'client-certificate:|client-certificate-data:|client-key:|client-key-data:' || true)"

        if [[ -z "$CERT_BASED_FILES" ]]; then
          log "No kubeconfig files using client certificates were found under ${KUBECONFIG_ROOT_DIR}."
        else
          log "Found kubeconfig files using client certificates:"
          printf '%s\n' "${CERT_BASED_FILES}"
        fi

        ########################
        # STEP 2: GENERATE NON-CERT KUBECONFIG(S) FOR OKE
        ########################

        mkdir -p "${NON_CERT_KUBECONFIG_DIR}"

        generate_non_cert_kubeconfig() {
          local out_file="$1"

          log "Generating non-certificate kubeconfig at ${out_file} using auth type '${OKE_AUTH_TYPE}'..."

          # We use 'oci ce cluster create-kubeconfig' which produces kubeconfigs that use OCI auth plugins.
          # These do NOT embed client certificates for end-user auth.
          oci ce cluster create-kubeconfig \
            --cluster-id "${CLUSTER_OCID}" \
            --file "${out_file}" \
            --region "${REGION}" \
            --kube-endpoint PUBLIC_ENDPOINT \
            --token-version 2.0.0 \
            --auth "${OKE_AUTH_TYPE}" \
            --force

          chmod 600 "${out_file}"
        }

        # 2.1 Create a cluster-wide non-cert kubeconfig (for cluster admins / CI)
        CLUSTER_WIDE_KUBECONFIG="${NON_CERT_KUBECONFIG_DIR}/cluster-admin-kubeconfig"
        if [[ ! -f "${CLUSTER_WIDE_KUBECONFIG}" ]]; then
          generate_non_cert_kubeconfig "${CLUSTER_WIDE_KUBECONFIG}"
        else
          log "Cluster-wide kubeconfig already exists at ${CLUSTER_WIDE_KUBECONFIG}; leaving it unchanged."
        fi

        # 2.2 Optionally, per-user non-cert kubeconfigs mirroring existing certificate-based configs
        if [[ -n "$CERT_BASED_FILES" ]]; then
          log "Preparing per-user non-cert kubeconfigs corresponding to certificate-based kubeconfigs..."

          while IFS= read -r cfg; do
            # Attempt to map kubeconfig path to user home (/home/<user>/...)
            if [[ "$cfg" == /home/* ]]; then
              user_name="$(echo "$cfg" | cut -d'/' -f3)"
              user_dir="${NON_CERT_KUBECONFIG_DIR}/${user_name}"
              mkdir -p "${user_dir}"

              user_kubeconfig="${user_dir}/config"
              if [[ ! -f "${user_kubeconfig}" ]]; then
                generate_non_cert_kubeconfig "${user_kubeconfig}"
              else
                log "Non-cert kubeconfig for user '${user_name}' already exists at ${user_kubeconfig}; leaving it unchanged."
              fi
            else
              log "Found kubeconfig with client cert outside /home: ${cfg}. Skipping per-user mapping; use ${CLUSTER_WIDE_KUBECONFIG} instead."
            fi
          done <<< "${CERT_BASED_FILES}"
        fi

        ########################
        # STEP 3: VERIFICATION
        ########################

        log "Verification: ensuring generated kubeconfigs do not rely on client certificates..."

        NON_CERT_FILES="$(find "${NON_CERT_KUBECONFIG_DIR}" -type f 2>/dev/null || true)"

        if [[ -z "$NON_CERT_FILES" ]]; then
          log "ERROR: No non-cert kubeconfigs were generated. Check earlier log output."
          exit 1
        fi

        CERT_IN_NONCERT="$(printf '%s\n' ${NON_CERT_FILES} | xargs -r grep -lE 'client-certificate:|client-certificate-data:|client-key:|client-key-data:' || true)"

        if [[ -n "$CERT_IN_NONCERT" ]]; then
          log "ERROR: The following generated kubeconfigs still contain client certificate references:"
          printf '%s\n' "${CERT_IN_NONCERT}"
          exit 1
        fi

        log "All generated kubeconfigs under ${NON_CERT_KUBECONFIG_DIR} are free of client certificate authentication."

        ########################
        # STEP 4: AUDIT COMMAND HINT
        ########################

        cat <<'EOF'

        NEXT STEPS (MANUAL POLICY ENFORCEMENT REQUIRED):

        1. Distribute the non-certificate kubeconfigs generated under:
             NON_CERT_KUBECONFIG_DIR (see script variable)
           to all users, CI/CD systems, and automation that currently use certificate-based kubeconfigs.

        2. Revoke or remove access to the old client-certificate-based kubeconfigs:
           - Delete or archive the old files.
           - Update secrets in CI/CD / automation to use the new non-cert kubeconfigs.
           - Update any local tooling to point to the new kubeconfig paths.

        3. To periodically verify that no client-certificate-based kubeconfigs remain in use, run:
           find /home -type f \( -name 'config' -o -name '*.kubeconfig' -o -name '*.kube' \) 2>/dev/null \
             | xargs -r grep -lE 'client-certificate:|client-certificate-data:|client-key:|client-key-data:' || true

        4. In your cloud console / IaC, enforce the use of these non-cert kubeconfigs and disable issuance or
           distribution of client certificate kubeconfigs to users.

        EOF

        log "Completed non-certificate kubeconfig generation for OKE cluster ${CLUSTER_OCID}."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
