Сообщение об ошибке
state=error: certificate verify failed (unable to get local issuer certificate)
Также выводится как
- OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:8443 state=error: certificate verify failed (unable to get local issuer certificate)
Полное сообщение: «SSL_connect returned=1 errno=0 peeraddr=<ip>:<port> state=error: certificate verify failed (unable to get local issuer certificate)»; в наших проверках peeraddr присутствовал в версиях гема openssl от 3.0.1 до 4.0.2. Faraday оборачивает эту ошибку в Faraday::SSLError с тем же сообщением; библиотеки, которые вызывают Net::HTTP напрямую, пробрасывают OpenSSL::SSL::SSLError как есть.
Ruby openssl (Net::HTTP, Faraday, HTTParty, RubyGems) — OpenSSL::SSL::SSLError from SSL_connect
Что это означает
Привязка Ruby к OpenSSL (её использует Net::HTTP, а значит, и большинство HTTP-клиентов на Ruby) не нашла издателя цепочки сертификатов сервера среди загруженных CA. Текст в скобках — причина, которую указывает проверка OpenSSL; всё, что перед ним, описывает, на каком этапе SSL_connect произошёл сбой. Обычно CA частный или принадлежит прокси, либо этот Ruby смотрит в пакет сертификатов CA, в котором этого CA нет.
Почему это происходит
Частный CA или прокси с инспекцией TLS
частоЦепочка заканчивается корневым сертификатом, которого нет в хранилище OpenSSL, используемом Ruby.
Ruby, собранный с собственным OpenSSL (rbenv, asdf, rvm в macOS)
частоТакие сборки Ruby используют пакет сертификатов CA из OpenSSL от Homebrew или из встроенного OpenSSL, а не связку ключей macOS; корневые сертификаты, которые ИТ-отдел добавил в связку ключей, на них не действуют.
Сервер не отправляет промежуточный сертификат
иногдаOpenSSL не загружает недостающие промежуточные сертификаты, поэтому с сервером, который отдаёт только конечный сертификат, проверка здесь не проходит, хотя браузеры его принимают.
RubyInstaller в Windows
иногдаRuby для Windows поставляется с собственным пакетом сертификатов CA; корневые сертификаты компании из хранилища Windows автоматически в него не попадают.
Как исправить
- 1.
Установите корневой сертификат туда, где его ищет OpenSSL в Ruby
Пути показывают OpenSSL::X509::DEFAULT_CERT_FILE и DEFAULT_CERT_DIR; в Debian и Ubuntu файлы сертификатов по этим путям обновляет update-ca-certificates.
bash # Ruby's OpenSSL reads the system bundle (see its default paths) ruby -ropenssl -e 'p OpenSSL::X509::DEFAULT_CERT_FILE, OpenSSL::X509::DEFAULT_CERT_DIR' sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && sudo update-ca-certificates - 2.
Задайте SSL_CERT_FILE для процесса
OpenSSL учитывает SSL_CERT_FILE и SSL_CERT_DIR; укажите в них пакет сертификатов, в котором есть и публичные корневые сертификаты, и ваш CA.
bash # One process / environment: point OpenSSL at a bundle with the needed roots export SSL_CERT_FILE=/etc/ssl/private-ca/corp-ca-bundle.pem ruby app.rb - 3.
Создайте собственное хранилище для одного клиента
OpenSSL::X509::Store с set_default_paths и add_file сохраняет публичные корневые сертификаты и добавляет частный CA, при этом проверка выполняется полностью.
ruby # One client: default roots plus a private CA store = OpenSSL::X509::Store.new store.set_default_paths store.add_file("/etc/ssl/private-ca/corp-root-ca.pem") http = Net::HTTP.new("api.example.com", 443) http.use_ssl = true http.cert_store = store - 4.
Faraday и другие обёртки: передайте ca_file или cert_store
Большинство HTTP-библиотек для Ruby принимают параметры ssl, которые соответствуют параметрам ca_file, ca_path или cert_store в Net::HTTP.
ruby conn = Faraday.new("https://api.example.com", ssl: { ca_file: "/etc/ssl/private-ca/corp-root-ca.pem" })
Связанные ошибки
state=error: certificate verify failed (self-signed certificate in certificate…Ruby openssl (Net::HTTP, Faraday, RubyGems)Could not verify the SSL certificate for https://gems.example.com/.Bundlercurl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)