はじめに
こんにちは、@prpr_manです。現在、Go言語を用いたマイクロサービスの開発に携わっています。我々のプロジェクトでは、Go 1.22系を使用していましたが、先日1.23にバージョンアップしました。初めは特に問題なく本番環境でも順調に動いていたので「これでヨシッ!」と思っていましたが……。
CIがたまに失敗するようになった
GitHub Actionsで実行しているテストが時折失敗するようになりました。失敗したテストでは、Dockerコンテナで立ち上げたSQL Serverに接続する箇所で以下のエラーが発生していました。
panic: query select: TLS Handshake failed: tls: failed to parse certificate from server: x509: negative serial number
問題なくテストが通るときもあればなぜか失敗することもある……。もちろん該当の箇所のコードやテストコードを変更した覚えもありません。この挙動に戸惑いつつ、原因を調査することにしました。
原因
調べた結果、原因は比較的早く判明しました。Go 1.23では、crypto/x509
パッケージにおいて、負のシリアル番号を持つX.509証明書が拒否される仕様に変更されていたのです。公式ドキュメントにも明記されています。
一見、この変更自体に問題はありません。しかし、SQL Serverが提供するX.509証明書のシリアル番号が負の値である場合があることが判明しました。
つまり、証明書のシリアル番号が正の場合は問題なくテストが通るが、負の場合にテストが失敗するという状況が発生していました。
対応策
この問題を回避する方法として、GODEBUG
環境変数にx509negativeserial=1
を設定することで、負のシリアル番号を許可することができます。
実際にGo 1.23のcrypto/x509
パッケージのコードを見ると、環境変数が設定されている場合、負のシリアル番号も許容する処理が追加されています。
if serial.Sign() == -1 {
if x509negativeserial.Value() != "1" {
return nil, errors.New("x509: negative serial number")
} else {
x509negativeserial.IncNonDefault()
}
}
我々のプロジェクトでは、docker-compose.yml
に以下のような設定を追加し、テスト環境では負のシリアル番号を許容することで問題を回避しました。
services:
app:
build:
context: .
target: debugger
environment:
... 環境変数色々
- GODEBUG=x509negativeserial=1
Go 1.23での仕様変更の背景
この変更の背景には、X.509証明書に関するRFC 5280の規定があります。
The serial number MUST be a positive integer assigned by the CA to each certificate.
RFC 5280では、シリアル番号は正の整数である必要があると明記されています。しかし、以下のような但し書きにもある通り、RFCに非準拠なCAではゼロや負のシリアル番号を持つ証明書を発行する事があり、これらの証明書を適切に処理する必要があることも書かれています。
Note: Non-conforming CAs may issue certificates with serial numbers that are negative or zero. Certificate users SHOULD be prepared to gracefully handle such certificates.
これに基づき、Go 1.22以前では負のシリアル番号を許可するようになっていました。ただし、負またはゼロのシリアル番号を持つ証明書を発行するCAは年々減少しており、2024年現在ではこの問題に該当するCAは1件のみ1となっているようです。
そのため、Go 1.23では仕様をRFCに準拠させ、デフォルトでは負のシリアル番号を持つ証明書を拒否するよう変更されたようです。
まとめ
Go 1.23におけるX.509証明書の変更点とその背景、そしてその対応策について解説しました。特に、Dockerコンテナで立ち上げたSQL Serverを利用する場合、この問題に直面する可能性があります。
本記事が、同様の課題に悩む方の参考になれば幸いです。Go 1.23以降の環境に移行する際には、ぜひこの変更を頭に入れておくと良いでしょう。