{"record":{"id":"ddb4e347199c1385","repo":"thanos-io/thanos","slug":"could-not-get-required-certificate-field-from-clie","errorCode":null,"errorMessage":"could not get required certificate field from client cert","messagePattern":"could not get required certificate field from client cert","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/tenancy/tenancy.go","lineNumber":102,"sourceCode":"\treturn roundTripperFunc(func(r *http.Request) (*http.Response, error) {\n\t\ttenant, _ := GetTenantFromHTTP(r, customTenantHeader, DefaultTenant, certTenantField)\n\t\tr.Header.Set(DefaultTenantHeader, tenant)\n\t\t// If the custom tenant header is not the same as the default internal header, we want to exclude the custom\n\t\t// one from the request to keep things simple.\n\t\tif customTenantHeader != DefaultTenantHeader {\n\t\t\tr.Header.Del(customTenantHeader)\n\t\t}\n\t\treturn next.RoundTrip(r)\n\t})\n}\n\n// getTenantFromCertificate extracts the tenant value from a client's presented certificate. The x509 field to use as\n// value can be configured with Options.TenantField. An error is returned when the extraction has not succeeded.\nfunc getTenantFromCertificate(r *http.Request, certTenantField string) (string, error) {\n\tvar tenant string\n\n\tif len(r.TLS.PeerCertificates) == 0 {\n\t\treturn \"\", errors.New(\"could not get required certificate field from client cert\")\n\t}\n\n\t// First cert is the leaf authenticated against.\n\tcert := r.TLS.PeerCertificates[0]\n\n\tswitch certTenantField {\n\n\tcase CertificateFieldOrganization:\n\t\tif len(cert.Subject.Organization) == 0 {\n\t\t\treturn \"\", errors.New(\"could not get organization field from client cert\")\n\t\t}\n\t\ttenant = cert.Subject.Organization[0]\n\n\tcase CertificateFieldOrganizationalUnit:\n\t\tif len(cert.Subject.OrganizationalUnit) == 0 {\n\t\t\treturn \"\", errors.New(\"could not get organizationalUnit field from client cert\")\n\t\t}\n\t\ttenant = cert.Subject.OrganizationalUnit[0]","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/tenancy/tenancy.go#L84-L120","documentation":"getTenantFromCertificate extracts the tenant ID from the client's mTLS certificate (OU or CN depending on configured field). If the TLS handshake produced no peer certificates (r.TLS.PeerCertificates empty), the tenant cannot be extracted and this error is returned.","triggerScenarios":"getTenantFromCertificate is called by GetTenantFromHTTP with certTenantField set to a certificate field, but the request was not authenticated with a client certificate — r.TLS is nil-capable or PeerCertificates has length 0 (e.g. request over plain HTTP or server not requesting client certs).","commonSituations":"Client connects without mTLS (plain HTTP or TLS without client auth); server's tls.Config ClientAuth set to NoClientCert/RequestClientCert and client sent no cert; a load balancer terminates TLS so the server never sees peer certs; TenantField option set to certificate while users authenticate via header.","solutions":["Require client certificates: set tls.Config ClientAuth to tls.RequireAndVerifyClientCert and reconfigure the client to present its cert.","If a proxy/LB terminates TLS, enable client-cert passthrough (PROXY protocol / header injection) or move tenant extraction to the proxy.","Confirm the request is HTTPS; GetTenantFromHTTP only inspects r.TLS, which is nil for plain HTTP.","If certs are not your tenant mechanism, switch Options.TenantField to the header-based option instead."],"exampleFix":"// before\ntlsConfig := &tls.Config{} // no client auth\n// after\ntlsConfig := &tls.Config{\n    ClientAuth: tls.RequireAndVerifyClientCert,\n    ClientCAs:  caPool,\n}","handlingStrategy":"validation","validationCode":"if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {\n    http.Error(w, \"client certificate required\", http.StatusUnauthorized)\n    return\n}","typeGuard":"func hasPeerCert(r *http.Request) bool { return r.TLS != nil && len(r.TLS.PeerCertificates) > 0 }","tryCatchPattern":"tenant, err := tenancy.GetTenantFromHTTP(r, header, def, field)\nif err != nil {\n    http.Error(w, err.Error(), http.StatusUnauthorized)\n    return\n}","preventionTips":["Enforce tls.RequireAndVerifyClientCert server-side","Only expose tenant extraction on HTTPS handlers","Handle TLS termination at the proxy correctly (passthrough or header injection)","Decide one tenant source (cert or header) and configure consistently"],"tags":["mtls","tenancy","authentication","certificate"],"backgroundTag":"authentication-required","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}