Сообщение об ошибке
tls: failed to verify certificate: x509: certificate signed by unknown authority
Go crypto/tls (Go ≥ 1.20) — wrapping x509 verification errors
Что это означает
Начиная с Go 1.20 ошибки TLS-рукопожатия оборачивают исходную ошибку x509, добавляя префикс «tls: failed to verify certificate:», поэтому в журналах видны и уровень (TLS-рукопожатие), и первопричина (не удалось построить цепочку). Суффикс бывает разным (неизвестный CA, истёкший срок действия, несовпадение имени хоста), и действовать нужно именно по нему. Префикс к тому же удобен для фильтрации журналов: поиск по нему через grep отделяет ошибки доверия TLS от ошибок уровня приложения, ради чего обёртку и добавили.
Почему это происходит
Любая из причин ошибки unknown authority
частоОтсутствующие промежуточные сертификаты, частные CA и контейнеры без сертификатов CA — всё, что приводит к «x509: certificate signed by unknown authority», теперь появляется с этим префиксом, если происходит при TLS-подключении (dial), а не при явном вызове Verify.
Клиент по умолчанию обращается к dev-эндпоинту с самоподписанным сертификатом
частоhttp.Get к локальному серверу с самоподписанным сертификатом (httptest.NewTLSServer из другого процесса, ingress в minikube, прокси для разработки) завершается этой ошибкой, если не передать клиенту CA сервера.
Суффикс другой — прочитайте его
иногда«...: x509: certificate has expired» и «...: x509: certificate is valid for X, not Y» — разные проблемы под одним и тем же префиксом; исправление определяется суффиксом, а не уровнем TLS.
Как исправить
- 1.
Передайте клиенту правильные якоря доверия
То же решение, что и для ошибки x509 без префикса: пул корневых сертификатов с CA, выпустившим сертификат, задаётся в том транспорте, через который действительно идёт подключение.
go pool := x509.NewCertPool() caPEM, _ := os.ReadFile("ca.pem") pool.AppendCertsFromPEM(caPEM) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool}, }} resp, err := client.Get("https://internal.example:8443/healthz") - 2.
Посмотрите, что предъявляет сервер
Прежде чем трогать клиентский код, посмотрите отдаваемую цепочку, её издателей и сроки действия — чаще всего исправление оказывается в конфигурации цепочки на стороне сервера.
bash openssl s_client -connect internal.example:8443 -servername internal.example \ </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates - 3.
Не отключайте проверку даже в тестах
Серверы httptest выдают заранее настроенный клиент; используйте его, а не отключайте проверку глобально.
go srv := httptest.NewTLSServer(handler) defer srv.Close() resp, err := srv.Client().Get(srv.URL) // trusts srv's cert, nothing else