kubernetes/kops · critical

error reading client keypair

Error message

error reading client keypair: %v

What it means

Thrown in run() when tls.LoadX509KeyPair fails to load or parse the client certificate/key pair configured via --client-cert and --client-key. Either file is missing/unreadable or the pair is malformed or mismatched, so mTLS authentication to the API server cannot be set up.

Solutions

  1. Verify both files exist, are readable, and are valid PEM (openssl x509 / openssl rsa on each).
  2. Confirm cert and key are a matching pair (compare modulus/public key hashes).
  3. Fix the --client-cert/--client-key paths or re-mount the correct secret.
  4. Regenerate the client keypair if rotated or mismatched, then restart the healthcheck.

Example fix

// before (paths swapped)
run("--client-cert", "/pki/client.key", "--client-key", "/pki/client.crt")
// after
run("--client-cert", "/pki/client.crt", "--client-key", "/pki/client.key")
Defensive patterns

Strategy: validation

Validate before calling

// preflight: load the pair before handing paths to the server
if _, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath); err != nil {
    return fmt.Errorf("client keypair preflight failed: %w", err)
}

Try / catch

// Go: fail fast with context at startup
if err := run(); err != nil {
    if strings.Contains(err.Error(), "client keypair") {
        klog.Fatalf("TLS client config invalid: %v", err)
    }
    klog.Fatalf("healthcheck terminated: %v", err)
}

Prevention

When it happens

Trigger: --client-key set but the cert/key files don't exist or are unreadable; cert and key don't match; files are not valid PEM; key is an unsupported format/encryption.

Common situations: Secrets mounted as separate volumes with one path wrong; expired/rotated certificate with mismatched key; PEM blocks corrupted by secret encoding; typo swapping cert and key flags.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/663fd4286aaf55ca. Report an issue: GitHub.

Appendix: source

Thrown at cmd/kube-apiserver-healthcheck/main.go:166

	flag.Parse()

	tlsConfig := &tls.Config{}

	if caCert != "" {
		b, err := os.ReadFile(caCert)
		if err != nil {
			return fmt.Errorf("error reading certificate %q: %v", caCert, err)
		}
		rootCAs := x509.NewCertPool()
		rootCAs.AppendCertsFromPEM(b)
		tlsConfig.RootCAs = rootCAs
	}

	if clientKey != "" {
		keypair, err := tls.LoadX509KeyPair(clientCert, clientKey)
		if err != nil {
			return fmt.Errorf("error reading client keypair: %v", err)
		}

		tlsConfig.Certificates = []tls.Certificate{keypair}
	}

	transport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	s := &healthCheckServer{
		transport: transport,
	}

	http.HandleFunc("/", s.handler)

	klog.Infof("listening on %s", listen)

	if err := http.ListenAndServe(listen, nil); err != nil {

View on GitHub (pinned to 4c8573c808)