kubectl: "x509: Certificate Signed by Unknown Authority"

kubectl doesn't trust the API server's certificate authority. Either the kubeconfig's certificate-authority-data is wrong/missing, or a corporate proxy intercepts TLS. One curl distinguishes them.

What you'll see

Root causes

kubeconfig missing/mismatched certificate-authority-data

The cluster stanza needs the cluster's real CA bundle in base64. A regenerated kubeconfig, a hand-edited file, or a copied cluster entry without the CA field produces exactly this error.

TLS interception (corporate proxy, AV software)

An on-path box re-signs traffic with its own CA: kubectl sees a cert chain it never agreed to trust. curl -v https://<api> shows the intercepted issuer immediately.

Fix it

  1. See who actually signed the API cert
    openssl s_client -connect <api-host>:6443 -servername <api-host> </dev/null 2>/dev/null | openssl x509 -noout -issuer
  2. Verify the kubeconfig carries the right CA
    kubectl config view --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d | openssl x509 -noout -subject   # empty or wrong = found it
  3. Fix or regenerate the kubeconfig with the cluster's CA
    # for managed clusters: re-download the kubeconfig (aks get-credentials, gcloud container clusters...) — regenerating is safer than hand-patching
  4. TLS-intercepting networks: trust the corporate CA in the OS store (kubectl uses the system pool when no CA is set)
    # Debian: cp corp-ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates

Field note

insecure-skip-tls-verify: true makes the error vanish and removes ALL server identity checking — a debug-only tool, never a kubeconfig you commit. The issuer line is the whole diagnosis: your cluster's internal CA means kubeconfig problems; a corporate CA name means the network path is intercepted.

Common questions

Why does the browser open the API fine but kubectl fails?

The browser (via corporate policy) trusts the intercepting CA; kubectl only trusts the kubeconfig's CA bundle and the system store. Same bytes, different trust anchors.

Can I test without touching the kubeconfig?

Yes: kubectl --insecure-skip-tls-verify get nodes as a one-off proves whether the error is trust-only (it works) or something deeper (it still fails).

Ship it right the first time

Kustomize base with probes, PDBs, and zero-downtime rollouts already wired.

Kubernetes Production Blueprints — $27 →

One-time. Yours to modify. Instant download from the NinjaOps template store.