エラーメッセージ
remote error: tls: bad certificate
Go のサーバーは「http: TLS handshake error from <client>: remote error: tls: bad certificate」とログに記録します。OpenSSL ベースのクライアントは、同じ拒否に対して別のアラート(TLS 1.2 では unknown_ca)を送信するため、サーバー側に記録されるテキストはクライアントの TLS ライブラリによって異なります。
Go crypto/tls — TLS alert 42 received from the peer; typically logged by a Go server whose certificate a Go client rejected
このエラーの意味
ピアが TLS アラート 42(bad_certificate)を送信しました。Go サーバーのログ(「http: TLS handshake error from …: remote error: tls: bad certificate」)に出ている場合は、クライアントがサーバーの証明書を拒否したことを意味します。Go のクライアントは、検証に失敗するとその理由(不明な認証局、期限切れ、名前の不一致)を問わずこのアラートを送信します。Go のクライアントで出ている場合は、相互 TLS でサーバーがクライアント証明書を拒否したことを意味します。本当の理由は、アラートを送信した側でしか確認できません。
発生する理由
Go クライアントがサーバーの証明書を信頼していない
よくある自己署名証明書やプライベート CA の証明書、中間証明書の欠落、期限切れの証明書、名前の不一致が該当します。クライアント自身のログには「x509: …」と表示されます。
相互 TLS:サーバーがクライアント証明書を拒否した
よくあるクライアントの証明書が期限切れである場合、信頼されていない CA によって発行されている場合、または clientAuth の用途を持たない場合に、クライアント側で発生します。
Kubernetes のアドミッション webhook とオペレーター
ときどきcaBundle が一致しない場合、API サーバー(Go クライアント)は webhook の証明書を拒否し、webhook サーバー側ではこれが bad certificate としてログに記録されます。
修正方法
- 1.
拒否しているクライアントを特定し、そのエラーを確認する
ログに記録されたクライアントのアドレスから、どのサービスを調べればよいかがわかります。そのサービスのエラーに、実際の検証失敗の内容が示されています。
bash # The log line names the client; its own error message says why it rejected the certificate journalctl -u myservice | grep 'remote error: tls: bad certificate' | grep -oE 'from [0-9a-f.:]+' | sort | uniq -c - 2.
信頼される完全なチェーンを送信する
すべてのクライアントが信頼する CA が発行した、フルチェーンの PEM ファイルを Go サーバーに設定してください。
go // Serve the full chain: leaf first, then intermediates, in one PEM file log.Fatal(http.ListenAndServeTLS(":443", "fullchain.pem", "server.key", nil)) - 3.
Kubernetes:webhook の caBundle を確認する
caBundle には、webhook のサーバー証明書を発行した CA が含まれていなければなりません。cert-manager の CA インジェクターを使うと、両者が同期された状態に保たれます。
bash # Kubernetes webhooks: the API server verifies the webhook's certificate with caBundle kubectl get validatingwebhookconfiguration my-webhook -o jsonpath='{.webhooks[0].clientConfig.caBundle}' \ | base64 -d | openssl x509 -noout -subject -enddate