エラーメッセージ
tls: failed to verify certificate: x509: certificate signed by unknown authority
Go crypto/tls (Go ≥ 1.20) — wrapping x509 verification errors
このエラーの意味
Go 1.20 以降、TLS ハンドシェイクの失敗では、元の x509 エラーが「tls: failed to verify certificate:」というプレフィックスでラップされます。そのため、ログにはレイヤー(TLS ハンドシェイク)と根本原因(チェーンの構築に失敗)の両方が表示されます。後ろに続く部分は、未知の認証局、期限切れ、ホスト名の不一致などさまざまで、対処すべきなのはこの部分です。プレフィックスはログの絞り込みにも便利です。これで grep すれば、TLS の信頼に関する失敗をアプリケーションレベルのエラーと区別できます。まさにそのためにラップが追加されました。
発生する理由
未知の認証局エラーと同じ原因のいずれか
よくある中間証明書の不足、プライベート CA、CA 証明書を持たないコンテナーなど、「x509: certificate signed by unknown authority」を引き起こすものはすべて、明示的な Verify の呼び出しではなく TLS 接続の確立(dial)の中で発生した場合、このプレフィックス付きで表示されるようになりました。
デフォルトのクライアントから開発用・自己署名のエンドポイントにアクセスしている
よくある自己署名証明書を使うローカルサーバー(別プロセスの httptest.NewTLSServer、minikube の Ingress、開発用プロキシ)に対する http.Get は、サーバーの CA をクライアントに渡さない限り、ここで失敗します。
後ろに続く部分が異なる:そこを読む
ときどき「...: x509: certificate has expired」や「...: x509: certificate is valid for X, not Y」は、同じプレフィックスが付いているだけの別の問題です。対処法は TLS のレイヤーではなく、後ろに続く部分のほうにあります。
修正方法
- 1.
クライアントに正しいトラストアンカーを渡す
プレフィックスのない x509 エラーと同じ対処法です。発行元の CA を含むルートプールを用意し、実際に接続(dial)に使われるトランスポートに設定してください。
go pool := x509.NewCertPool() caPEM, _ := os.ReadFile("ca.pem") pool.AppendCertsFromPEM(caPEM) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool}, }} resp, err := client.Get("https://internal.example:8443/healthz") - 2.
サーバーが提示する内容を確認する
クライアントのコードに手を付ける前に、サーバーが返すチェーン、その発行者、有効期間を確認してください。ほとんどの場合、修正すべきなのはサーバー側のチェーン設定です。
bash openssl s_client -connect internal.example:8443 -servername internal.example \ </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates - 3.
テストでも検証を有効にしておく
httptest のサーバーは設定済みのクライアントを提供します。検証をグローバルに無効にするのではなく、そのクライアントを使ってください。
go srv := httptest.NewTLSServer(handler) defer srv.Close() resp, err := srv.Client().Get(srv.URL) // trusts srv's cert, nothing else