できない.dev

curl で「SSL certificate problem」が出て HTTPS にアクセスできない

curl は接続先のサーバ証明書を CA バンドルで検証し、たどれなければ exit code 60 で止まる。
原因は CA バンドルの欠落か、社内プロキシ・自己署名証明書による差し替えのどちらかで、正しい CA を渡せば解決する。

公開:

要約

curl: (60) SSL certificate problem: unable to get local issuer certificate は、サーバが提示した証明書からルート証明書までの経路を curl が作れなかったという意味である。
サーバ側が壊れているとは限らず、curl 側が信頼する CA の一覧に、その経路の起点が入っていないことのほうが多い。

したがって直すべきは「どの CA を信頼させるか」であり、検証を切ることではない。-k を付ければ確かに通るが、その時点で通信相手が本物かどうかを確かめる手段を失う。

よくある原因

  1. CA バンドルが古い、または存在しない。
    最小構成の Docker イメージや古い CI ランナーでは ca-certificates が入っていないことがある。
  2. 社内プロキシが TLS を終端して独自の証明書に差し替えている。
    ブラウザは会社が配布した CA を信頼するよう設定済みでも、curl はそれを見ていない。
  3. 接続先が自己署名証明書を使っている。
    開発用のローカルサーバでよくある。
  4. サーバが中間証明書を送っていない。
    ブラウザは不足分を自動で取りに行くことがあるため「ブラウザでは見えるのに curl だけ落ちる」という形で表面化する。

解決策

1. まず -v でチェーンを見る

curl -v https://example.com/ 2>&1 | grep -E "subject:|issuer:|SSL certificate"

issuer: に社名や見慣れない CA が出れば 2 の可能性が高い。subject:issuer: が同じなら自己署名である。

2. CA バンドルを更新する

# Debian / Ubuntu
apt-get update && apt-get install -y --reinstall ca-certificates
 
# Alpine
apk add --no-cache ca-certificates

curl 公式は Mozilla のルート証明書から生成した cacert.pem を配布しているので、パッケージを使えない環境ではこれを取得して使う(Extract CA Certs from Mozilla(新しいタブで開く))。

3. 独自 CA を明示的に渡す

社内 CA や自己署名の CA 証明書が手元にあるなら、それを指定する。

curl --cacert /path/to/corp-ca.pem https://internal.example.com/

毎回書きたくない場合は環境変数を使う。
curl は CURL_CA_BUNDLE が設定されていればそのパスを CA バンドルとして読む(Schannel を使う Windows ビルドを除く)。

export CURL_CA_BUNDLE=/path/to/corp-ca.pem

4. 中間証明書の欠落はサーバ側で直す

クライアント側に足せるのはルート CA までで、中間証明書はサーバが送るべきものである。openssl s_client -connect example.com:443 -showcerts で送られてくる証明書が 1 枚しかない場合は、サーバの証明書ファイルに中間証明書を連結してもらう。

5. -k は調査用にとどめる

curl -k https://example.com/

これは検証そのものを無効にする。
原因の切り分けには使えるが、スクリプトや CI にそのまま残すと中間者攻撃を検知できなくなる。
恒久対応は 2〜4 のいずれかで行う。

この記事は役立ちましたか?