読者です 読者をやめる 読者になる 読者になる

MySQL5.0系のSSL接続について

実家から水が大量に届いたよ!…どうしたらいいんだこれは


MySQLではクライントとの通信にSSL接続が可能ですが、この設定にハマるにハマったのでメモ。


経緯としては、5.0.67を5.0.91にアップデートしたところ、
5.0.67では正常に接続できていたものが、5.0.91では接続エラーがでました。

結論的にはSSL証明書に原因があり、証明書をつくり直すことにより対応しました。
そこに至るまでの試行錯誤もろもろ

エラー内容

MySQL-5.0.91でSSL接続するとこんな感じでエラーがでます。

$ mysql -uuser --ssl-ca=/foo/bar/ca-cert.pem                                                                       
ERROR 2026 (HY000): SSL connection error

エラー内容としてはこれだけで特にログファイルには出力されません。
warningレベルを変えたりしましたが、ログには出力されないようです。


古いバージョンでは接続できたことから、リリースノートを検索してSSL関連で変更がなかったか調べます。
その結果5.0.88で変更があったことがわかりました。


てことで5.0.88と5.0.87をダウンロード/インストールして、接続確認を行いました。
(その際に使用したSSLの証明書は同じです)
でやはり
5.0.88→接続不可
5.0.87→接続可能
の結果だったので、5.0.87との違いを意識しつつ調べていきます。

デバッグ

上述の通りログレベルの変更では調査不可能なのでデバッグモードにて調べます。
デバッグモードはconfigure時に --with-debug=full を付加することにより使用できます。

$./configure --prefix=/usr/local/mysql-5.0.xx_debug --with-openssl --with-debug=full

こんな感じ。prefixは適当で。


デバッグの使用方法はマニュアル通りです。

クライアントを実行する前に、MYSQL_DEBUG 環境変数を指定してください。

shell> MYSQL_DEBUG=d:t:O,/tmp/client.trace
shell> export MYSQL_DEBUG

その結果、クライアントによってトレースファイルが /tmp/client.trace に生成されます。

http://dev.mysql.com/doc/refman/4.1/ja/debugging-client.html

なので

$ MYSQL_DEBUG=d:t:O,/tmp/client_5.0.xx.trace
$ export MYSQL_DEBUG

としておいて、MySQLに接続します。

$ mysql -uroot --ssl-ca=/etc/mysql/newcerts/ca-cert.pem

-------------------------------------------------------
MYSQL_DEBUG found. libmysql started with the following:
d:t:O,/tmp/client_5.0.xx.trace
-------------------------------------------------------

これで、トレースログが指定した場所にはかれます。
でエラー箇所をみてみると

 >report_errors
 | error: OpenSSL: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed:s3_clnt.c:912:
 | error: error: error:00000005:lib(0):func(0):DH lib
 | info: socket_errno: 0
 <report_errors

となっており、エラーを出しているのは、s3_clnt.cであることがわかります。
s3_clnt.cは、MySQLではなくOpenSSLのソースコードなので、
実際にエラーをだしているのは、MySQLではなくOpenSSLであることがわかりました。


さらにエラー内容の"SSL3_GET_SERVER_CERTIFICATE:certificate verify failed"は
サーバ証明書の検証に失敗した時に出るエラーなので、だいぶ原因が特定できてきました。


で成功してる5.0.87で同じくトレースログをとって、5.0.87では何の認証チェックをしているかを見てみます。

>vio_verify_callback
| enter: ok: 0  ctx: 0xbf85f8f8
| info: cert: /C=GB/ST=Berkshire/L=Newbury/O=My Company Ltd
| error: verify error: 18  'self signed certificate'
| exit: 1
<vio_verify_callback

↑ここの部分。
そうです。これオレオレ証明書なんです。ここでエラーを返してたようです。これで原因がわかりました。


で、まぁコードを最後まで追ってないのでたぶんの部分があるんですけど、
リリースノートやコードの差分なんかみると、ここの認証チェックの部分を5.0.88からはOpenSSL側にまかせたみたいですね。
逆にいうと5.0.87以前はMySQL側でやっていて(vio_verify_callback)、こいつはオレオレ証明でも通しちゃうみたいです。
たぶん

てことで

Webと違って証明書がブラウザに表示されるわけじゃないからって、証明書を適当に作ると痛い目にあうんですね。わかります。
なのでまじめにっていうか

http://dev.mysql.com/doc/refman/5.1/ja/secure-create-certs.html

ここのページでいう
ca-cert.pemをつくるときと、server-req.pemやclient-req.pemを作るときに入れる
SSLの情報(Country NameやOrganization Name等々)を別々の名前にしたりしてまじめに作りましょうって話で解決しました。
(エンター連打じゃだめよってこと)

ちなみに

MySQL 5.1.11からはコマンドオプションとして--ssl-verify-server-certが追加され、検証の有無を選択できるようです。