Error message
remote error: tls: bad certificate
Go servers log "http: TLS handshake error from <client>: remote error: tls: bad certificate". OpenSSL-based clients send different alerts for the same rejection (unknown_ca with TLS 1.2), so the server-side text depends on the client's TLS library.
Go crypto/tls — TLS alert 42 received from the peer; typically logged by a Go server whose certificate a Go client rejected
What it means
The peer sent TLS alert 42 (bad_certificate). In a Go server's log ("http: TLS handshake error from …: remote error: tls: bad certificate") it means a client refused the server's certificate; Go clients send this alert for every verification failure (unknown authority, expiry, wrong name). In a Go client it means the server rejected the client certificate in mutual TLS. The real reason is only visible on the side that sent the alert.
Why it happens
A Go client does not trust the server's certificate
commonSelf-signed or private-CA certificates, missing intermediates, expired certificates or a wrong name; the client's own log shows "x509: …".
Mutual TLS: the server rejected the client certificate
commonSeen on the client side when its certificate is expired, issued by an untrusted CA or lacks the clientAuth usage.
Kubernetes admission webhooks and operators
occasionalThe API server (a Go client) rejects a webhook's certificate when the caBundle does not match, which the webhook server logs as bad certificate.
How to fix it
- 1.
Find the rejecting client and read its error
The client address in the log tells you which service to look at; its error states the actual verification failure.
bash # The log line names the client; its own error message says why it rejected the certificate journalctl -u myservice | grep 'remote error: tls: bad certificate' | grep -oE 'from [0-9a-f.:]+' | sort | uniq -c - 2.
Serve a complete, trusted chain
Give the Go server a full-chain PEM file, issued by a CA all clients trust.
go // Serve the full chain: leaf first, then intermediates, in one PEM file log.Fatal(http.ListenAndServeTLS(":443", "fullchain.pem", "server.key", nil)) - 3.
Kubernetes: check the webhook's caBundle
The caBundle must contain the CA that issued the webhook's serving certificate; cert-manager's CA injector keeps them in sync.
bash # Kubernetes webhooks: the API server verifies the webhook's certificate with caBundle kubectl get validatingwebhookconfiguration my-webhook -o jsonpath='{.webhooks[0].clientConfig.caBundle}' \ | base64 -d | openssl x509 -noout -subject -enddate