Перейти к содержимому
ecdsa.com
curl (OpenSSL backend)curl

Сообщение об ошибке

curl: (60) SSL: no alternative certificate subject name matches target hostname 'api.example.com'

Также выводится как

  • curl: (60) SSL: no alternative certificate subject name matches target host name 'api.example.com'

curl 8.x выводит «target hostname», curl 7.x выводил «target host name». Хост в кавычках — то, что было указано в URL. Очень старые версии curl (до 7.62.0) использовали для этой проверки код завершения 51.

curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, hostname check in lib/vtls/openssl.c

Что это означает

Цепочка сертификатов доверенная, но ни одно из DNS-имён (или IP-адресов) в расширении «альтернативное имя субъекта» (SAN) сертификата не совпадает с хостом из URL. curl сравнивает хост из URL только с записями SAN; если расширение SAN есть, общее имя (CN) субъекта игнорируется. Имя в кавычках в конце сообщения — это хост, к которому curl попросили подключиться.

Почему это происходит

Как исправить

  1. 1.

    Сравните список SAN с URL

    Выведите расширение subjectAltName сертификата, который отдаёт сервер. Решение — либо сертификат, в котором указано это имя, либо URL с одним из указанных имён.

    bash
    # Names the served certificate actually covers
    echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \
      | openssl x509 -noout -subject -ext subjectAltName
  2. 2.

    Перевыпустите сертификат со всеми используемыми именами

    Запросите все имена хостов (и IP-адреса, если клиенты подключаются по IP) как записи SAN. Имя, указанное только в поле общего имени (CN), не работает ни с одним современным клиентом.

    bash
    # ACME: list every name the certificate must cover
    sudo certbot certonly --nginx -d example.com -d www.example.com -d api.example.com
    
    # Private CA: put all names into subjectAltName (CN is ignored for matching)
    openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \
      -addext "subjectAltName=DNS:api.example.com,DNS:www.example.com,IP:10.0.0.5"
  3. 3.

    Сохраните имя, меняйте только адрес

    Если нужно обратиться к конкретному адресу (узлу балансировщика нагрузки, туннелю, IP-адресу staging-окружения), --resolve или --connect-to сохраняют хост из URL, так что SNI и проверка имени хоста по-прежнему используют имя из сертификата.

    bash
    # Keep the certificate's name in the URL and steer the connection to another address
    curl --resolve api.example.com:443:10.0.0.5 https://api.example.com/
    curl --connect-to api.example.com:443:lb-internal.example.net:443 https://api.example.com/
  4. 4.

    Владельцам серверов: проверьте маршрутизацию по SNI

    Если сертификат в запросах с SNI и без него различается, добавьте для этого имени блок server (или правило ingress) и убедитесь, что для реальных имён хостов клиенты не получают сертификат по умолчанию.

    bash
    # Compare what the server returns with and without SNI
    openssl s_client -connect 10.0.0.5:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject
    openssl s_client -connect 10.0.0.5:443 -noservername </dev/null 2>/dev/null | openssl x509 -noout -subject

Связанные ошибки

Открыть всю базу ошибок подписей