エラーメッセージ
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
OpenSSL ベースの curl(Linux、macOS の Homebrew、Git for Windows)はこのテキストを出力します。新しいリリース(ecdsa.com のテストでは 8.22.0。8.14.1 ではまだ上記のテキストでした)では、OpenSSL の検証コードが付いた「SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)」という表記になります。Windows で Schannel を使ってビルドされた curl は同じ状況を「schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)」と報告し、GnuTLS ベースの libcurl(Debian や Ubuntu の git が使用)は「server verification failed: certificate signer not trusted」と報告します。PHP や git など libcurl を利用するツールは、「(60) 」より後の部分に独自のプレフィックスを付けて出力します。
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)
このエラーの意味
curl はサーバーの証明書を受け取りましたが、チェーンの最後の証明書を発行した証明書を、自身の CA バンドル内で見つけられませんでした。証明書自体は完全に有効な場合もあります。curl は、信頼しているどのルートにもその証明書をつなげられないというだけの理由で、接続を拒否しています(終了コード 60)。足りないのは、サーバーが送信し忘れた中間証明書か、マシンにないルート CA のどちらかです。
発生する理由
サーバーが自身の証明書しか送信していない
よくあるパブリック CA はサーバー証明書を中間証明書で署名するため、サーバーはリーフ証明書と一緒にその中間証明書を送信しなければなりません。fullchain.pem ではなく cert.pem を設定したサーバーでも、中間証明書をキャッシュし不足分を取得するデスクトップブラウザーでは動作しますが、curl はそのどちらも行わないため、リーフ証明書で止まってしまいます。
証明書がプライベート CA または社内 CA から発行されている
よくある社内サービス、ステージング環境、Kubernetes の Ingress では、社内 CA や自作の CA が発行した証明書がよく使われます。そのルートは curl が使う公開バンドル(Debian と Ubuntu では /etc/ssl/certs/ca-certificates.crt)に含まれていないため、ルートをインストールするまで、そのルートが発行した証明書はどれも検証できません。
TLS を検査するプロキシが通信を再署名している
ときどき企業のプロキシやエンドポイントセキュリティ製品(Zscaler、Netskope、Palo Alto など)は、あらゆるサイトの証明書を独自の CA が発行した証明書に置き換えます。ブラウザーは OS のキーチェーンを通じてその CA を信頼しますが、curl、コンテナー、WSL はその CA を含まない別のバンドルを使うため、すべての HTTPS 呼び出しが同じように失敗します。
マシンに使用できる CA バンドルがない
ときどきslim 版や distroless のコンテナーイメージには ca-certificates パッケージが含まれておらず、古いシステムには現在のルートより前に作られたバンドルが入っています。バンドルが空だったり古かったりすると、よく知られた公開サイトの証明書でさえ、このメッセージで失敗します。
この curl が読み込まないストアに CA が追加されている
まれcurl のビルドはそれぞれ特定のバンドルを 1 つだけ読み込みます。Homebrew の curl、conda 環境、Git for Windows はそれぞれ独自のバンドルを同梱しており、Windows には Schannel 版と OpenSSL 版の両方のビルドがあります。Windows の証明書ストアや macOS のキーチェーンに追加した CA は、そのストアを読み込むビルドにしか反映されません。
修正方法
- 1.
サーバーが実際に送信しているものを確認する
ハンドシェイクに含まれる証明書を一覧表示してください。1 つしか表示されず(depth 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.
サーバーを修正する:完全なチェーンを返す
リーフ証明書に続けてすべての中間証明書を含むバンドルを Web サーバーに設定してください。これで、管理下にないモバイルアプリや 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 をシステムのストアにインストールする
チェーンが正当な理由で社内または組織内部のルートで終わっている場合は、そのルートを OS のバンドルに追加してください。ルートは IT チームやプラットフォームチーム、または 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.
1 つのコマンドまたは 1 つのシェルに限って curl にバンドルを指定する
--cacert はシステムに手を加えずに 1 回の呼び出しに限って信頼を追加し、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