{"record":{"id":"538490c51ff17a46","repo":"kubernetes/kops","slug":"no-tls-connection","errorCode":null,"errorMessage":"no TLS connection","messagePattern":"no TLS connection","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"discovery/pkg/discovery/auth.go","lineNumber":38,"sourceCode":"\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype UserInfo struct {\n\tUniverseID string\n\tClientID   string\n}\n\n// AuthenticateClientToUniverse extracts the Universe ID and Client ID from the mTLS connection.\n// The Universe ID is defined as the SHA256 hash of the root CA certificate (DER bytes)\n// presented in the client's certificate chain.\n// The Client ID is taken from the Common Name (CN) of the leaf certificate.\nfunc AuthenticateClientToUniverse(r *http.Request, universeID string) (*UserInfo, error) {\n\tif r.TLS == nil {\n\t\treturn nil, fmt.Errorf(\"no TLS connection\")\n\t}\n\tif len(r.TLS.PeerCertificates) == 0 {\n\t\treturn nil, fmt.Errorf(\"no client certificate presented\")\n\t}\n\n\t// Verify the chain is valid, though we don't validate that the CA certificate is trusted.\n\tvar verifiedChains [][]*x509.Certificate\n\t{\n\t\tpeerCertificates := r.TLS.PeerCertificates\n\n\t\topts := x509.VerifyOptions{\n\t\t\tRoots:         x509.NewCertPool(),\n\t\t\tIntermediates: x509.NewCertPool(),\n\t\t\tKeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},\n\t\t}\n\n\t\tfor i := 1; i < len(peerCertificates); i++ {\n\t\t\tif i == len(peerCertificates)-1 {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/discovery/pkg/discovery/auth.go#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclient.Get(\"http://discovery.internal/validate\")\n// after\ncert, _ := tls.LoadX509KeyPair(\"client.crt\", \"client.key\")\nclient.Transport = &http.Transport{TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{cert}}}\nclient.Get(\"https://discovery.internal/validate\")","handlingStrategy":"type-guard","validationCode":"// caller-side pre-check before auth logic\nif r.URL.Scheme != \"https\" {\n    return nil, fmt.Errorf(\"request must use HTTPS\")\n}","typeGuard":"func hasTLS(r *http.Request) bool { return r != nil && r.TLS != nil && len(r.TLS.PeerCertificates) > 0 }","tryCatchPattern":"// Go: guard before authenticating\nu, err := AuthenticateClientToUniverse(r, universeID)\nif err != nil {\n    http.Error(w, \"mTLS required\", http.StatusUnauthorized)\n    return\n}","preventionTips":["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."],"tags":["mtls","authentication","http","security"],"backgroundTag":"no-tls-connection","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}