Сообщение об ошибке
curl: (60) SSL certificate problem: self-signed certificate
Также выводится как
- curl: (60) SSL certificate problem: self signed certificate
- curl: (60) SSL certificate OpenSSL verify result: self-signed certificate (18)
OpenSSL 3.0 и новее выводят «self-signed certificate», OpenSSL 1.1.1 — «self signed certificate». Более новые версии curl (8.22.0 в нашей проверке) формулируют это как «SSL certificate OpenSSL verify result: self-signed certificate (18)». Не путайте эту ошибку с «self-signed certificate in certificate chain»: там есть цепочка CA, которая заканчивается недоверенным корневым сертификатом.
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
Что это означает
Сервер предъявил единственный сертификат, подписанный его собственным ключом (издатель совпадает с субъектом), а именно этого сертификата в хранилище доверенных сертификатов curl нет. За ним не стоит CA, который curl мог бы найти, поэтому проверка останавливается на глубине 0. Для серверов разработки и специализированных устройств это нормально, и установка промежуточного сертификата здесь никогда не помогает.
Почему это происходит
Сервер разработки или тестовый сервер со сгенерированным сертификатом
частоЛокальные HTTPS-серверы, стеки docker-compose и тестовые фикстуры часто генерируют самоподписанный сертификат при запуске (например, командой openssl req -x509). Каждая повторная генерация создаёт новый сертификат, поэтому копия, которой доверяли вчера, сегодня может уже не совпадать.
Устройство или продукт всё ещё использует заводской сертификат
частоМаршрутизаторы, NAS-устройства, принтеры, интерфейсы iDRAC/iLO, установки Proxmox, Jenkins или Elasticsearch поставляются с самоподписанным сертификатом и работают с ним, пока кто-нибудь не установит нормальный.
Прокси или ingress отдаёт свой сертификат по умолчанию
иногдаЕсли запрошенному имени не соответствует ни один сертификат, ingress-nginx отдаёт «Kubernetes Ingress Controller Fake Certificate», а Traefik — «TRAEFIK DEFAULT CERT»; оба сертификата самоподписанные. Настоящая проблема тогда в маршрутизации или SNI, а не в доверии.
Сервер указывает не на тот файл
редкоКонфигурация, которая ссылается на временный самоподписанный сертификат (snakeoil.pem в Debian, localhost.crt из пакета) вместо выпущенного, приводит к этой ошибке, даже если настоящий сертификат лежит на диске.
Как исправить
- 1.
Доверяйте именно этому сертификату, предварительно проверив его
Для одного внутреннего эндпоинта сохраните сертификат, сверьте его отпечаток SHA-256 по доверенному каналу (консоль устройства, администратор, который его создал) и передайте его через --cacert. Тогда curl в этом вызове примет только этот сертификат и никакой другой.
bash # Fetch the certificate once, compare its fingerprint out of band, then trust exactly that file openssl s_client -connect api.example.com:8443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -out api.example.com.pem openssl x509 -in api.example.com.pem -noout -fingerprint -sha256 curl --cacert api.example.com.pem https://api.example.com:8443/ - 2.
При разработке: выпускайте сертификаты от локального CA
Локальный CA (mkcert, step-ca или созданный вручную корневой сертификат) позволяет один раз настроить доверие к одному корневому сертификату и выпускать сертификаты для любого числа хостов разработки. Такие сертификаты уже не самоподписанные, поэтому ошибка исчезает везде, где установлен корневой сертификат.
bash # mkcert creates a local CA, installs it into the system and browser stores, # and issues certificates signed by it (no longer self-signed) mkcert -install mkcert api.example.test localhost 127.0.0.1 - 3.
Для публичных хостов: установите сертификат от публичного CA
Всем сервисам, к которым обращаются другие люди или сторонние клиенты, следует предъявлять сертификат от публично доверенного CA; ACME-клиенты, такие как certbot, lego или Caddy, получают и продлевают его автоматически.
bash # Public names: get a certificate from a public CA, e.g. with certbot (ACME) sudo certbot certonly --nginx -d api.example.com - 4.
Временная диагностика: обратитесь к сервису с отключённой проверкой
С -k curl пропускает проверку сертификатов, поэтому ответ 200 подтверждает, что не хватает только доверия. Не оставляйте этот флаг: самоподписанный сертификат, принятый без проверки, в любой момент можно подменить сертификатом злоумышленника, и curl этого не заметит.
bash # Diagnostic only: confirms the service answers when certificate checks are skipped curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com:8443/
Связанные ошибки
curl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)verify error:num=18:self-signed certificateOpenSSL CLI (openssl s_client / openssl verify)code: 'DEPTH_ZERO_SELF_SIGNED_CERT'Node.js tls / https / fetch