Сообщение об ошибке
remote error: tls: unknown certificate authority
Серверы на Go записывают в журнал «http: TLS handshake error from <client>: remote error: tls: unknown certificate authority». В наших тестах сервер на Go 1.27 записывал отказ клиентов OpenSSL 3.5, отвергавших тот же сертификат по TLS 1.3, как «local error: tls: bad record MAC», а клиенты на Go отправляют bad_certificate (в журнале — «remote error: tls: bad certificate»).
Go crypto/tls — TLS alert 48 (unknown_ca) received from the peer, e.g. an OpenSSL-based client rejecting a Go server's certificate
Что это означает
Другая сторона отправила оповещение TLS 48 (unknown_ca): ей не удалось построить цепочку от полученного сертификата до CA, которому она доверяет. В журнале сервера на Go это значит, что клиент (на базе OpenSSL: curl, Python, Ruby, PHP, nginx в роли прокси) отверг сертификат сервера во время рукопожатия TLS 1.2. В клиенте на Go это значит, что сервер отверг клиентский сертификат при взаимной TLS-аутентификации.
Почему это происходит
Клиент не доверяет CA, выпустившему сертификат
частоЧастные или корпоративные CA, которых нет на клиентских машинах или в контейнерах.
Сервер на Go отправляет только конечный сертификат
частоListenAndServeTLS отправляет ровно те сертификаты, которые есть в файле; если в файле только конечный сертификат, клиенты не могут построить цепочку до корневого.
Взаимная TLS-аутентификация: сервер не доверяет CA клиента
иногдаНа стороне клиента это оповещение означает, что в ClientCAs сервера нет издателя клиентского сертификата.
Как исправить
- 1.
Проверьте со стороны клиента, какую цепочку отдаёт сервер
Если отправляется только конечный сертификат, исправьте файл; если цепочка заканчивается частным корневым сертификатом, установите его на клиентах.
bash openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:|Verify return code' - 2.
Отдавайте из Go полную цепочку
Поместите конечный сертификат и его промежуточные сертификаты в PEM-файл, который передаётся в ListenAndServeTLS (или tls.LoadX509KeyPair).
go // Leaf + intermediates in one file; Go sends every certificate in it srv := &http.Server{Addr: ":443"} log.Fatal(srv.ListenAndServeTLS("fullchain.pem", "server.key")) - 3.
Установите частный корневой сертификат на клиентах
Для внутренних сервисов распространите корневой сертификат компании на системы и контейнеры, которые обращаются к серверу.
bash # Private CA: install the root on the clients (Debian/Ubuntu example) sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && sudo update-ca-certificates