エラーメッセージ
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)」です。ecdsa.com のテストでは、openssl gem 3.0.1〜4.0.2 で peeraddr が含まれていました。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 が使っており、そのためほとんどの Ruby の HTTP クライアントも使っています)は、読み込んだ CA の中に、サーバーの証明書チェーンの発行者を見つけられませんでした。括弧内のテキストは OpenSSL が示す検証失敗の理由で、それより前の部分は SSL_connect のどこで失敗が起きたかを示しています。通常は、CA がプライベートなものであるか、プロキシが間に入っているか、あるいはこの Ruby が参照している CA バンドルにその CA が含まれていないかのいずれかです。
発生する理由
プライベート CA または TLS を検査するプロキシ
よくあるチェーンの終点のルートが、Ruby が使う OpenSSL のストアに含まれていません。
独自の OpenSSL でビルドされた Ruby(macOS の rbenv、asdf、rvm)
よくあるこのような Ruby は、macOS のキーチェーンではなく、Homebrew の OpenSSL や同梱の OpenSSL の CA バンドルを使います。IT 部門がキーチェーンに追加したルートは適用されません。
サーバーが中間証明書を送信していない
ときどきOpenSSL は不足している中間証明書を取得しないため、リーフ証明書だけを送るサーバーは、ブラウザーでは受け入れられてもここで失敗します。
Windows の RubyInstaller
ときどきWindows 版の Ruby は独自の CA バンドルを同梱しており、Windows のストアにある会社のルートは自動的には含まれません。
修正方法
- 1.
Ruby の OpenSSL が参照する場所にルートをインストールする
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.
1 つのクライアントに専用のストアを持たせる
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 を渡す
ほとんどの Ruby の HTTP ライブラリは、Net::HTTP の ca_file、ca_path、cert_store に対応する ssl オプションを受け付けます。
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)