Сообщение об ошибке
x509: certificate signed by unknown authority
Go crypto/x509 — returned by Certificate.Verify and TLS dials
Что это означает
Go попытался построить цепочку от сертификата сервера до корневого сертификата, которому он доверяет, и не смог: ни один путь не заканчивается сертификатом из настроенного пула корневых сертификатов. Сам сертификат может быть полностью действительным — просто у Go нет причин доверять его издателю. Это самая частая ошибка TLS в развёртываниях на Go.
Почему это происходит
Сервер не отправляет промежуточный сертификат
частоПромежуточные сертификаты подписаны корневыми из хранилищ доверенных сертификатов, а конечные — промежуточными. Если сервер отдаёт только конечный сертификат (cert.pem вместо fullchain.pem), Go не может построить путь от конечного сертификата к корневому. Браузеры часто скрывают эту проблему за счёт закешированных промежуточных сертификатов; Go так не делает.
Внутреннего или частного CA нет в пуле
частоКорпоративных CA, самоподписанных сертификатов для разработки и сертификатов, выпущенных service mesh, в системном хранилище нет, и так задумано. Клиенту о них нужно сообщить явно.
Минимальный образ контейнера без сертификатов CA
частоВ образах scratch и slim нет /etc/ssl/certs. Поэтому каждое TLS-подключение завершается этой ошибкой, каким бы публичным и действительным ни был сертификат целевого сервера.
Перехватывающий прокси переподписывает трафик
иногдаКорпоративные устройства инспекции TLS подменяют сертификаты на подписанные CA компании. Машины, на которых этот CA не установлен, считают любой внешний сайт недоверенным.
Как исправить
- 1.
Добавьте CA в пул корневых сертификатов клиента
Дополните системный пул, а не заменяйте его, чтобы публичные сайты продолжали работать вместе с вашим внутренним CA.
go roots, err := x509.SystemCertPool() if err != nil { roots = x509.NewCertPool() } pem, _ := os.ReadFile("internal-ca.pem") roots.AppendCertsFromPEM(pem) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: roots}, }} - 2.
Исправьте сервер: отдавайте полную цепочку
Проверьте, что сервер отправляет на самом деле; если в ответе только один сертификат, укажите в конфигурации сервера файл с полной цепочкой (например, fullchain.pem от Let's Encrypt).
bash openssl s_client -connect api.example.com:443 -showcerts </dev/null \ | grep -c "BEGIN CERTIFICATE" # 1 = leaf only (broken), 2+ = chain present - 3.
Добавьте сертификаты CA в образ
Для минимальных образов копируйте пакет сертификатов CA в образ при сборке. Никогда не «решайте» проблему через InsecureSkipVerify — это полностью отключает проверку.
docker FROM alpine AS certs RUN apk add --no-cache ca-certificates FROM scratch COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/