本文へスキップ
ecdsa.com
Go crypto/tls (Go ≥ 1.20)Go

エラーメッセージ

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 の信頼に関する失敗をアプリケーションレベルのエラーと区別できます。まさにそのためにラップが追加されました。

発生する理由

修正方法

  1. 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. 2.

    サーバーが提示する内容を確認する

    クライアントのコードに手を付ける前に、サーバーが返すチェーン、その発行者、有効期間を確認してください。ほとんどの場合、修正すべきなのはサーバー側のチェーン設定です。

    bash
    openssl s_client -connect internal.example:8443 -servername internal.example \
      </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
  3. 3.

    テストでも検証を有効にしておく

    httptest のサーバーは設定済みのクライアントを提供します。検証をグローバルに無効にするのではなく、そのクライアントを使ってください。

    go
    srv := httptest.NewTLSServer(handler)
    defer srv.Close()
    resp, err := srv.Client().Get(srv.URL)   // trusts srv's cert, nothing else

関連するエラー

署名エラーデータベースをすべて見る