Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

Kubernetes Error Guide: 'x509: certificate signed by unknown authority' TLS Trust Failures

Fix x509 certificate signed by unknown authority in Kubernetes: stale kubeconfig CA, expired kubeadm certs, SAN mismatch, MITM proxies, and untrusted webhook CAs.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #tls

Exact Error Message

The most common form appears when kubectl cannot validate the API server’s serving certificate against the CA in your kubeconfig:

Unable to connect to the server: x509: certificate signed by unknown authority

Two closely related variants point at a hostname/IP mismatch or an expired certificate:

Unable to connect to the server: x509: certificate is valid for kubernetes, kubernetes.default, not 10.0.5.20
Unable to connect to the server: x509: certificate has expired or is not yet valid: current time 2026-06-25T14:02:11Z is after 2026-05-30T09:14:00Z

The same class of error also surfaces from inside the cluster when an admission webhook or aggregated API server presents a certificate the kube-apiserver does not trust:

Internal error occurred: failed calling webhook "vpod.kb.io": failed to call webhook: Post "https://webhook-svc.kube-system.svc:443/validate": x509: certificate signed by unknown authority

What the Error Means

TLS verification has three parts: the client must trust the CA that signed the server’s certificate, the certificate’s Subject Alternative Names (SANs) must include the host/IP the client dialed, and the certificate must be within its validity window. An x509 error means one of those checks failed.

certificate signed by unknown authority means the CA that signed the API server’s serving certificate is not in the trust bundle the client is using — for kubectl that bundle is certificate-authority-data in your kubeconfig; for in-cluster clients it is /var/run/secrets/kubernetes.io/serviceaccount/ca.crt or a webhook’s caBundle. certificate is valid for X, not Y means trust is fine but you connected to a name or IP that is not listed in the cert’s SANs. certificate has expired means the validity window has passed — extremely common on kubeadm clusters, whose control-plane certificates default to a one-year lifetime.

Common Causes

  • Stale kubeconfig after a cluster rebuild. The cluster was torn down and recreated (or kubeadm reset + init), generating a brand-new CA. Your old kubeconfig still embeds the previous CA, which no longer matches the API server.
  • Self-signed API server with no CA distributed. A custom or self-signed serving cert was installed but its CA was never added to clients’ trust bundles.
  • Expired CA or serving certificate. kubeadm control-plane certs expire after 365 days; clusters that are never upgraded silently cross the expiry date.
  • SAN mismatch. You reach the API server through a load balancer hostname or a node IP that was not included in --apiserver-cert-extra-sans when the cert was generated.
  • MITM / TLS-inspecting proxy. A corporate egress proxy re-signs TLS with its own CA; kubectl sees the proxy’s cert instead of the cluster’s.
  • Untrusted custom CA for webhooks or aggregated APIs. A validating/mutating webhook or extension API server presents a cert signed by a CA that is not in its caBundle (or cert-manager’s CA injection failed).

How to Reproduce the Error

Rebuild a kubeadm cluster while keeping an old kubeconfig:

# On the control plane: wipe and recreate (new CA generated)
sudo kubeadm reset -f
sudo kubeadm init --pod-network-cidr=10.244.0.0/16

# On your laptop, keep using the OLD kubeconfig
kubectl get nodes
Unable to connect to the server: x509: certificate signed by unknown authority

To reproduce the SAN variant, reach the server by an IP that is not in the cert SANs:

kubectl --server=https://10.0.5.20:6443 get nodes
Unable to connect to the server: x509: certificate is valid for 10.0.5.10, 127.0.0.1, kubernetes.default, not 10.0.5.20

Diagnostic Commands

Pull the certificate the API server actually presents and read its issuer, SANs, and validity:

openssl s_client -connect 10.0.5.10:6443 -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates -ext subjectAltName
issuer=CN=kubernetes
subject=CN=kube-apiserver
notBefore=Jun 25 09:14:00 2025 GMT
notAfter=May 30 09:14:00 2026 GMT
X509v3 Subject Alternative Name:
    DNS:kubernetes, DNS:kubernetes.default, IP Address:10.0.5.10, IP Address:127.0.0.1

On kubeadm control planes, check every certificate’s expiry at once:

sudo kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
admin.conf                 May 30, 2026 09:14 UTC   <invalid>       no
apiserver                  May 30, 2026 09:14 UTC   <invalid>       no
ca                         Jun 23, 2035 09:14 UTC   8y              no

Decode the CA embedded in your kubeconfig and compare its fingerprint to the live server’s CA:

# CA your kubeconfig trusts
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
  | base64 -d | openssl x509 -noout -fingerprint -sha256

# CA actually serving the API
openssl s_client -connect 10.0.5.10:6443 -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256
sha256 Fingerprint=A1:B2:...:9F   # kubeconfig
sha256 Fingerprint=7C:3D:...:11   # live server  <-- mismatch == stale/wrong CA

Step-by-Step Resolution

1. Identify which check failed. Read the error: unknown authority = trust/CA mismatch, valid for X, not Y = SAN, expired = validity. The fingerprint comparison above confirms a CA mismatch.

2. For a stale kubeconfig (CA mismatch), copy the fresh admin config from the control plane:

# On the control-plane node
sudo cat /etc/kubernetes/admin.conf

# On your workstation, replace the cluster context
scp root@10.0.5.10:/etc/kubernetes/admin.conf ~/.kube/config
kubectl get nodes

3. For expired kubeadm certs, renew them and restart the control plane:

sudo kubeadm certs renew all
# Restart static pods so they reload the new certs
sudo systemctl restart kubelet
# Refresh your admin kubeconfig (renew regenerates admin.conf)
sudo cp /etc/kubernetes/admin.conf ~/.kube/config
sudo kubeadm certs check-expiration | head -5

4. For a SAN mismatch, regenerate the apiserver cert with the missing name/IP:

sudo rm /etc/kubernetes/pki/apiserver.{crt,key}
sudo kubeadm init phase certs apiserver \
  --apiserver-cert-extra-sans=10.0.5.20,k8s.example.com
sudo systemctl restart kubelet

5. For a MITM proxy, either exclude the API endpoint from TLS inspection (NO_PROXY=10.0.5.10) or append the proxy’s CA to your kubeconfig’s certificate-authority-data.

6. For untrusted webhook/aggregated-API CAs, patch the correct CA into the caBundle:

CA=$(kubectl get secret webhook-server-cert -n kube-system \
  -o jsonpath='{.data.ca\.crt}')
kubectl patch validatingwebhookconfiguration my-webhook \
  --type=json \
  -p "[{\"op\":\"replace\",\"path\":\"/webhooks/0/clientConfig/caBundle\",\"value\":\"$CA\"}]"

Prevention and Best Practices

  • Treat kubeconfig as ephemeral: re-fetch admin.conf whenever a cluster is rebuilt instead of editing the old one.
  • Monitor certificate expiry. Alert at least 30 days before any control-plane cert expires; kubeadm certs check-expiration can be scraped in a cron job or exporter.
  • Plan an annual cert renewal (or a cluster upgrade, which renews them) for kubeadm clusters before the 365-day window closes.
  • Enumerate every name and IP clients will use — LB DNS, VIPs, external IPs — in --apiserver-cert-extra-sans at init time.
  • Distribute custom CAs through your trust-store automation so webhooks and clients trust them out of the box; let cert-manager inject caBundle values automatically.
  • Set NO_PROXY for cluster endpoints so TLS-inspecting egress proxies never sit between kubectl and the API server.
  • Unable to connect to the server: dial tcp ...: connect: connection refused — the API server is unreachable rather than untrusted; a networking/port problem, not TLS.
  • error: You must be logged in to the server (Unauthorized) — TLS succeeded but authentication failed. See the Unauthorized error guide.
  • tls: failed to verify certificate: x509: certificate signed by unknown authority from kubelet or etcd — same root cause inside other components’ client connections.
  • x509: cannot validate certificate for 10.0.5.20 because it doesn't contain any IP SANs — a stricter SAN failure where the cert has no IP SANs at all.

Frequently Asked Questions

Why did kubectl work yesterday and fail today with “unknown authority”? The most likely cause is that the cluster (or just its CA) was regenerated — a rebuild, a kubeadm reset, or a control-plane reprovision — while your local kubeconfig still embeds the old CA. Compare the SHA-256 fingerprints of the kubeconfig CA and the live server CA; if they differ, copy a fresh admin.conf.

Can I skip certificate verification to get unblocked quickly? You can with kubectl --insecure-skip-tls-verify, but it disables MITM protection entirely and should only be used for a one-off diagnostic. Fix the trust bundle instead — using insecure mode in scripts or CI permanently is a security hole.

My certificate is “valid for X, not Y” — how do I add Y? That is a SAN mismatch, not a trust problem. Regenerate the apiserver certificate including the missing host or IP via kubeadm init phase certs apiserver --apiserver-cert-extra-sans=Y, then restart the kubelet so the static pod reloads it.

Do kubeadm certificates really expire after one year? Yes. Control-plane leaf certificates (apiserver, admin.conf, etc.) default to 365 days, though the cluster CA itself lasts 10 years. Upgrading the cluster renews leaf certs automatically; long-lived clusters that are never upgraded must run kubeadm certs renew all before the deadline.

Why does only my webhook fail with x509 while kubectl works fine? kubectl and the webhook use different trust bundles. The webhook presents a cert that must be signed by the CA in its caBundle (or the aggregated API’s caBundle). If cert-manager’s CA injection failed or the bundle is stale, patch the correct CA into the webhook configuration.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.