エラーメッセージ
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 のリリース(ecdsa.com のテストでは 8.22.0)では「SSL certificate OpenSSL verify result: self-signed certificate (18)」と表記されます。CA のチェーンが存在し、信頼されていないルートで終わる「self-signed certificate in certificate chain」と混同しないでください。
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
このエラーの意味
サーバーは自身の鍵で署名された(発行者とサブジェクトが同じ)証明書を 1 枚だけ提示しましたが、curl のトラストストアにはその証明書そのものがありません。curl が参照できる CA が背後に存在しないため、検証は深さ 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 です。
サーバーが誤ったファイルを参照している
まれ発行された証明書ではなく、一時的な自己署名証明書(Debian の snakeoil.pem、パッケージに付属する localhost.crt)を参照している設定では、ディスク上に本物の証明書があってもこのエラーになります。
修正方法
- 1.
確認したうえで、この証明書だけを信頼する
社内のエンドポイントが 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、または自作のルート)を使えば、1 つのルートを一度信頼するだけで、いくつもの開発用ホストに証明書を発行できます。証明書は自己署名ではなくなるため、ルートがインストールされているすべての環境でエラーが解消されます。
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 の証明書を提示すべきです。certbot、lego、Caddy などの ACME クライアントは、証明書を自動的に取得・更新します。
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