Error message
x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
Also printed as
- x509: certificate signed by unknown authority (possibly because of "x509: ECDSA verification failure" while trying to verify candidate authority certificate "Example Root CA")
kubectl prints it after "Unable to connect to the server: tls: failed to verify certificate: ". For ECDSA CAs the inner reason reads "x509: ECDSA verification failure" instead of "crypto/rsa: verification error"; the quoted name is the CA's common name.
kubectl and other Go clients — a CA with the right name but the wrong key (e.g. a kubeconfig from before kubeadm reset)
What it means
Go found a CA in your trust configuration whose name matches the issuer of the server's certificate ("kubernetes" in kubeadm clusters), tried it, and the signature did not verify with that CA's key. Two different CAs share the same name: the one in your kubeconfig and the one that actually signed the API server's certificate. This is the classic result of rebuilding a cluster (kubeadm reset and init) while keeping the old ~/.kube/config.
Why it happens
The cluster was recreated with kubeadm
commonkubeadm init generates a new CA called "kubernetes" every time; the kubeconfig from the previous cluster still embeds the old CA with the same name.
An old ~/.kube/config or KUBECONFIG entry
commonSeveral local clusters (kind, minikube, k3d) recreated under the same context name leave stale CA data behind.
The cluster CA was rotated
occasionalAfter a CA rotation, clients that were not given the new CA bundle fail this way until their kubeconfig is updated.
How to fix it
- 1.
Confirm that the CA keys differ
Equal names with different public-key hashes prove the kubeconfig holds another cluster's CA.
bash # Same name, different key? Compare the public keys of the two CAs kubectl config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \ | base64 -d | openssl x509 -noout -pubkey | openssl sha256 sudo openssl x509 -in /etc/kubernetes/pki/ca.crt -noout -pubkey | openssl sha256 # on a control-plane node - 2.
Use the new cluster's kubeconfig
After kubeadm init, copy /etc/kubernetes/admin.conf (or regenerate user kubeconfigs) so the embedded CA matches the new cluster.
bash # After kubeadm init/reset: replace the old kubeconfig with the new admin.conf mkdir -p "$HOME/.kube" sudo cp /etc/kubernetes/admin.conf "$HOME/.kube/config" sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config" - 3.
Check which kubeconfig is in effect
KUBECONFIG may list several files; the first one defining the context wins.
bash # Make sure no other kubeconfig overrides it echo "$KUBECONFIG"; kubectl config current-context