Сообщение об ошибке
curl: (60) SSL certificate problem: unable to get local issuer certificate
Также выводится как
- curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)
- SSL certificate problem: unable to get local issuer certificate
curl на базе OpenSSL (Linux, Homebrew в macOS, Git for Windows) выводит этот текст; более новые версии (8.22.0 в нашей проверке, тогда как 8.14.1 ещё выводил текст выше) формулируют его как «SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)», добавляя код проверки OpenSSL. curl, собранный с Schannel в Windows, сообщает о той же ситуации как «schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)», а libcurl на базе GnuTLS (её использует git в Debian и Ubuntu) — как «server verification failed: certificate signer not trusted». Инструменты на основе libcurl, например PHP и git, выводят часть после «(60) » со своим префиксом.
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)
Что это означает
curl получил сертификаты сервера, но не нашёл в своём пакете сертификатов CA (CA bundle) сертификат, которым выпущен последний сертификат цепочки. Сам сертификат может быть полностью действительным: curl просто не может связать его ни с одним корневым сертификатом, которому доверяет, поэтому отказывается устанавливать соединение (код завершения 60). Недостающее звено — либо промежуточный сертификат, который сервер забыл отправить, либо корневой CA, которого нет на вашей машине.
Почему это происходит
Сервер отправляет только собственный сертификат
частоПубличные CA подписывают сертификаты серверов промежуточным сертификатом, и сервер обязан отправлять этот промежуточный сертификат вместе со своим конечным. Сервер, настроенный на cert.pem вместо fullchain.pem, работает в настольных браузерах, которые кешируют промежуточные сертификаты и сами загружают недостающие, но curl не делает ни того ни другого и останавливается на конечном сертификате.
Сертификат выпущен частным или корпоративным CA
частоВнутренние сервисы, staging-окружения и ingress-ресурсы Kubernetes часто используют сертификаты от корпоративного или самостоятельно созданного CA. Этого корневого сертификата нет в публичном пакете сертификатов, который использует curl (/etc/ssl/certs/ca-certificates.crt в Debian и Ubuntu), поэтому ни один выпущенный им сертификат не пройдёт проверку, пока корневой сертификат не установлен.
Прокси с инспекцией TLS переподписывает трафик
иногдаКорпоративные прокси и средства защиты конечных точек (Zscaler, Netskope, Palo Alto и подобные) подменяют сертификат каждого сайта сертификатом, выпущенным их собственным CA. Браузер доверяет этому CA через хранилище ключей операционной системы, а curl, контейнеры и WSL используют отдельный пакет сертификатов, где его нет, поэтому каждый HTTPS-запрос завершается одной и той же ошибкой.
На машине нет пригодного пакета сертификатов CA
иногдаОблегчённые (slim) и distroless-образы контейнеров поставляются без пакета ca-certificates, а на старых системах лежат пакеты сертификатов, собранные ещё до появления нынешних корневых сертификатов. С пустым или устаревшим пакетом сертификатов даже для известных публичных сайтов curl выдаёт это сообщение.
CA добавлен в хранилище, которое этот curl не читает
редкоКаждая сборка curl читает один определённый пакет сертификатов: curl из Homebrew, окружение conda и Git for Windows поставляют каждый свой, а в Windows есть сборки и с Schannel, и с OpenSSL. CA, добавленный в хранилище сертификатов Windows или связку ключей macOS, видят только те сборки, которые читают это хранилище.
Как исправить
- 1.
Посмотрите, что на самом деле отправляет сервер
Выведите список сертификатов в рукопожатии. Если там только один (глубина 0) и его издатель — промежуточный CA, конфигурация сервера неполна и её обязательно нужно исправить на сервере. Если цепочка заканчивается корневым сертификатом компании или самостоятельно созданным корневым сертификатом, на клиенте не хватает этого корневого сертификата.
bash # Which certificates does the server send, and who issued the last one? openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' # 0 s:CN=api.example.com # i:C=US, O=Example Trust Services, CN=Example TLS Issuing CA <- leaf only: intermediate missing - 2.
Исправьте сервер: отдавайте полную цепочку
Укажите веб-серверу файл цепочки, в котором за конечным сертификатом следуют все промежуточные. Это сразу исправляет ошибку для всех клиентов, включая мобильные приложения и API-клиенты, которые вы не контролируете.
bash # Leaf first, then each intermediate in order; the root itself is optional cat api.example.com.crt intermediate.crt > fullchain.pem # nginx: ssl_certificate /etc/nginx/tls/fullchain.pem; # Apache: SSLCertificateFile /etc/apache2/tls/fullchain.pem # certbot: use live/<name>/fullchain.pem, never cert.pem - 3.
Установите частный корневой CA в системное хранилище
Если цепочка правомерно заканчивается корневым сертификатом компании или внутренним корневым сертификатом, добавьте его в пакет сертификатов операционной системы. Получите корневой сертификат у ИТ-отдела или платформенной команды либо у самого CA, но никогда не берите его из сертификата, который предъявило проблемное соединение: именно ему вы пока и не можете доверять.
bash # Debian, Ubuntu, Alpine (the file must end in .crt) sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates # RHEL, Fedora, Amazon Linux sudo cp corp-root-ca.pem /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust - 4.
Укажите curl пакет сертификатов для одной команды или одного сеанса оболочки
--cacert добавляет доверие для одного вызова, не затрагивая систему; CURL_CA_BUNDLE задаёт пакет сертификатов для всех вызовов curl в окружении. Файл может содержать несколько PEM-сертификатов.
bash curl --cacert corp-root-ca.pem https://api.example.com/ # or for every curl command in this shell (a file may hold several PEM certificates) export CURL_CA_BUNDLE=/opt/certs/corp-and-public-roots.pem - 5.
Установите пакет сертификатов CA в минимальные контейнеры
Если внутри контейнера ошибку дают даже публичные сайты, в образе нет пакета сертификатов. Установите ca-certificates в образ (или скопируйте /etc/ssl/certs/ca-certificates.crt из стадии сборки), а не отключайте проверку.
bash # Debian/Ubuntu-based images apt-get update && apt-get install -y --no-install-recommends ca-certificates # Alpine apk add --no-cache ca-certificates - 6.
Временная диагностика: убедитесь, что проблема в доверии
-k (--insecure) отключает проверку сертификатов, поэтому успешный ответ лишь доказывает, что сетевой путь работает, а сбой связан с доверием. Никогда не оставляйте этот флаг в скриптах: с -k любой на пути трафика может читать и изменять его, включая учётные данные и скачиваемый код.
bash # Diagnostic only: if this works, the problem is trust, not the network. curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com/
Связанные ошибки
curl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)curl: (60) SSL certificate problem: self-signed certificatecurl (OpenSSL backend)verify error:num=20:unable to get local issuer certificateOpenSSL CLI (openssl s_client / openssl verify)fatal: unable to access 'https://git.example.com/team/app.git/': SSL certificat…git over HTTPS with libcurl + OpenSSL (Git for Windows default, macOS Homebrew, Alpine, RHEL)x509: certificate signed by unknown authorityGo crypto/x509