kubernetes/kops · error

no client certificate presented

Error message

no client certificate presented

What it means

The mTLS connection state exists but carries no peer certificate chain (r.TLS.PeerCertificates is empty), so AuthenticateClientToUniverse cannot identify the client. The server requires a client certificate to derive Universe/Client IDs and rejects requests without one.

Source

Thrown at discovery/pkg/discovery/auth.go:41

	"fmt"
	"net/http"
)

type UserInfo struct {
	UniverseID string
	ClientID   string
}

// AuthenticateClientToUniverse extracts the Universe ID and Client ID from the mTLS connection.
// The Universe ID is defined as the SHA256 hash of the root CA certificate (DER bytes)
// presented in the client's certificate chain.
// The Client ID is taken from the Common Name (CN) of the leaf certificate.
func AuthenticateClientToUniverse(r *http.Request, universeID string) (*UserInfo, error) {
	if r.TLS == nil {
		return nil, fmt.Errorf("no TLS connection")
	}
	if len(r.TLS.PeerCertificates) == 0 {
		return nil, fmt.Errorf("no client certificate presented")
	}

	// Verify the chain is valid, though we don't validate that the CA certificate is trusted.
	var verifiedChains [][]*x509.Certificate
	{
		peerCertificates := r.TLS.PeerCertificates

		opts := x509.VerifyOptions{
			Roots:         x509.NewCertPool(),
			Intermediates: x509.NewCertPool(),
			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		}

		for i := 1; i < len(peerCertificates); i++ {
			if i == len(peerCertificates)-1 {
				// Last cert is the root
				opts.Roots.AddCert(peerCertificates[i])
			} else {

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Configure the server's tls.Config with ClientAuth: tls.RequireAndVerifyClientCert and ClientCAs set to the trust pool.
  2. Ensure clients present a certificate (set TLSClientConfig.Certificates in Go, --cert/--key in curl).
  3. If behind a proxy, use TLS passthrough or have the proxy re-present a client cert to the backend.
  4. Test the handshake: openssl s_client -connect host:443 -cert client.crt -key client.key and confirm a peer cert reaches the server.

Example fix

// before
cfg := &tls.Config{ClientCAs: caPool} // client certs requested but not required
// after
cfg := &tls.Config{ClientCAs: caPool, ClientAuth: tls.RequireAndVerifyClientCert}
Defensive patterns

Strategy: validation

Validate before calling

// enforce client certs at server startup
cfg := &tls.Config{
    ClientCAs:  caPool,
    ClientAuth: tls.RequireAndVerifyClientCert,
}
server := &http.Server{Addr: ":443", TLSConfig: cfg}

Type guard

func hasClientCert(r *http.Request) bool {
    return r.TLS != nil && len(r.TLS.PeerCertificates) > 0
}

Try / catch

// Go: reject missing client certs with 401
u, err := AuthenticateClientToUniverse(r, universeID)
if err != nil {
    http.Error(w, "client certificate required", http.StatusUnauthorized)
    return
}

Prevention

When it happens

Trigger: A TLS client connects without presenting a certificate (no ClientCert requested or client skipped it); TLS config lacks RequestClientCert/RequireAndVerifyClientCert; the client cert failed to load client-side and the handshake proceeded anonymously.

Common situations: Server tls.Config missing ClientAuth: tls.RequireAndVerifyClientCert; client built without TLSClientConfig.Certificates; mutual-TLS terminated at proxy then re-originated without a cert; curl test without --cert/--key.

Understand the failure class

Related errors


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