エラーメッセージ
x509: certificate signed by unknown authority
Go crypto/x509 — returned by Certificate.Verify and TLS dials
このエラーの意味
Go はサーバーの証明書から信頼済みのルートまでのチェーンを構築しようとしましたが、失敗しました。設定されたルートプールに含まれる証明書で終わるパスが 1 つもありません。証明書自体はまったく問題なく有効な場合もあり、単に Go にその発行者を信頼する理由がないだけです。これは、Go アプリケーションの運用で最もよく遭遇する TLS エラーです。
発生する理由
サーバーが中間証明書を送信していない
よくあるトラストストアのルートが中間証明書に署名し、中間証明書がリーフ証明書に署名します。サーバーにリーフ証明書だけを設定していると(fullchain.pem ではなく cert.pem)、Go はリーフ → ルートの間をつなげません。ブラウザーはキャッシュした中間証明書でこの問題を覆い隠すことがよくありますが、Go はそうしません。
社内 CA やプライベート CA がプールに含まれていない
よくある企業の CA、開発用の自己署名証明書、サービスメッシュが発行した証明書は、設計上、システムのストアには含まれていません。クライアントに明示的に指定する必要があります。
CA 証明書を含まない最小構成のコンテナーイメージ
よくあるscratch イメージや slim イメージには /etc/ssl/certs が含まれていません。そのため、接続先の証明書がパブリックで有効なものであっても、TLS の接続はすべてこのエラーになります。
通信を傍受するプロキシが再署名している
ときどき企業の TLS インスペクション用ミドルボックスは、証明書を社内 CA が署名したものに差し替えます。その CA がインストールされていないマシンでは、外部のサイトがすべて信頼できないものとして扱われます。
修正方法
- 1.
クライアントのルートプールに CA を追加する
システムのプールを置き換えるのではなく、拡張してください。そうすれば、社内 CA を使うサイトに加えて、パブリックなサイトにも引き続き接続できます。
go roots, err := x509.SystemCertPool() if err != nil { roots = x509.NewCertPool() } pem, _ := os.ReadFile("internal-ca.pem") roots.AppendCertsFromPEM(pem) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: roots}, }} - 2.
サーバー側の修正:完全なチェーンを返す
サーバーが実際に何を送信しているかを確認してください。証明書が 1 つしか表示されない場合は、完全なチェーンを含むファイル(Let's Encrypt の fullchain.pem など)をサーバーに指定してください。
bash openssl s_client -connect api.example.com:443 -showcerts </dev/null \ | grep -c "BEGIN CERTIFICATE" # 1 = leaf only (broken), 2+ = chain present - 3.
イメージに CA 証明書を含める
最小構成のイメージでは、ビルド時に CA バンドルをコピーしてください。InsecureSkipVerify でこの問題を「解決」してはいけません。検証が完全に無効になります。
docker FROM alpine AS certs RUN apk add --no-cache ca-certificates FROM scratch COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/