kubernetes/kops · error
no TLS connection
Error message
no TLS connection
What it means
AuthenticateClientToUniverse extracts identity from the client's mTLS certificate, but requires the request to be TLS-terminated by Go's http server (r.TLS populated). If the request arrived over plain HTTP, or TLS was terminated upstream without propagating connection state, r.TLS is nil and authentication cannot proceed.
Source
Thrown at discovery/pkg/discovery/auth.go:38
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"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 {View on GitHub (pinned to 4c8573c808)
Solutions
- Send the request over HTTPS with a client certificate so net/http populates r.TLS.
- Configure the server with tls.Listen / ListenAndServeTLS using the CA that issued client certs.
- If TLS terminates at a proxy, either pass through TLS or configure the proxy to inject the client cert (e.g. X-Forwarded-Client-Cert) and adapt the handler accordingly.
- Point probes/tests at the TLS port, not the plain-HTTP one.
Example fix
// before
client.Get("http://discovery.internal/validate")
// after
cert, _ := tls.LoadX509KeyPair("client.crt", "client.key")
client.Transport = &http.Transport{TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{cert}}}
client.Get("https://discovery.internal/validate") Defensive patterns
Strategy: type-guard
Validate before calling
// caller-side pre-check before auth logic
if r.URL.Scheme != "https" {
return nil, fmt.Errorf("request must use HTTPS")
} Type guard
func hasTLS(r *http.Request) bool { return r != nil && r.TLS != nil && len(r.TLS.PeerCertificates) > 0 } Try / catch
// Go: guard before authenticating
u, err := AuthenticateClientToUniverse(r, universeID)
if err != nil {
http.Error(w, "mTLS required", http.StatusUnauthorized)
return
} Prevention
- Serve the endpoint only via HTTPS (ListenAndServeTLS).
- Configure probes/tests to hit the TLS port, never plain HTTP.
- If using a proxy, prefer TLS passthrough so r.TLS is populated at the server.
- Reject non-TLS traffic at ingress.
When it happens
Trigger: An HTTP request reaches a handler calling AuthenticateClientToUniverse with r.TLS == nil — i.e. served over plain HTTP instead of HTTPS/mTLS, or a reverse proxy terminated TLS without setting X-Forwarded/Forwarded cert headers and the code expects *tls.ConnectionState.
Common situations: Misconfigured ingress/load balancer forwarding to the backend as plain HTTP; developer testing against http://localhost; health probes hitting an mTLS-only endpoint over HTTP.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- no client certificate presented
- failed to verify client certificate chain: %w
- Unauthorized
- Forbidden: cannot register node name '%s' with client cert '
- DIGITALOCEAN_ACCESS_TOKEN is required
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/538490c51ff17a46.
Report an issue: GitHub.