Ubuntu 10.10のMySQLとSSL通信でJDBC接続する
apt-getでUbuntu 10.10にインストールしたMySQLはmy.cnfへ設定を追加すればSSLでの接続が可能です。
MySQLのSSL接続については、パブリッククラウド上のMySQLと社内サーバーをセキュアに繋ぐ方法の1つとして調べています。
社内サーバーであればLANを切ってセキュリティを確保しますが、クラウド上のMySQLサーバーと通信する場合は、VPNか今回試すSSL接続で行う必要があります。
また、AWSやニフティクラウド*1の場合、内部ネットワークは共有なのでセキュリティの重要度が高いデータは同様に暗号化しなければなりません。
今回、MySQLのSSL接続を有功にしてJavaのアプリケーションからSSLで接続するところまで通して試してみました。
以降、その手順のメモになります。
(クラウド環境をターゲットとしているのであれば、CentOSの方が利用できるところが多いですが、とりあえず手元のUbuntuの環境で試しています)
MySQLの設定は以下のサイトを参考にしました。
http://mifosforge.jira.com/wiki/display/MIFOS/How+to+enable+MySQL+SSL+on+Ubuntu
公式のドキュメントはこちら:
http://dev.mysql.com/doc/refman/5.1/ja/secure-create-certs.html
SSLが有効か確認
mysql -u root -p mysql> show variables like 'have_ssl'; +---------------+----------+ | Variable_name | Value | +---------------+----------+ | have_ssl | DISABLED | +---------------+----------+ 1 row in set (0.00 sec)
接続用のキーを生成する
以下のコマンドで必要な証明書やキーを生成します。オプションの箇所は適当に指定しました。
$ cd /etc/mysql $ sudo openssl genrsa -out ca-key.pem 2048 Generating RSA private key, 2048 bit long modulus .....................+++ .........+++ e is 65537 (0x10001) $ sudo openssl req -new -x509 -nodes -days 1000 -key ca-key.pem -out cacert.pem You are about to be asked to enter information that will be incorporated into your certificate request. ... Country Name (2 letter code) [AU]:JP State or Province Name (full name) [Some-State]:Tokyo Locality Name (eg, city) :Shinjuku Organization Name (eg, company) [Internet Widgits Pty Ltd]:co-meeting Inc Organizational Unit Name (eg, section) : Common Name (eg, YOUR name) :Hiroyuki Endoh Email Address :hrendoh@gmail.com $ sudo openssl req -newkey rsa:2048 -days 1000 -nodes -keyout server-key.pem -out server-req.pem Generating a 2048 bit RSA private key .................+++ ... Please enter the following 'extra' attributes to be sent with your certificate request A challenge password : An optional company name : $ sudo openssl x509 -req -in server-req.pem -days 1000 -CA cacert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem Signature ok subject=/C=JP/ST=Tokyo/L=Shinjuku/O=co-meeting Inc/CN=hrendoh/emailAddress=hrendoh@gmail.com Getting CA Private Key $ sudo openssl req -newkey rsa:2048 -days 1000 -nodes -keyout client-key.pem -out client-req.pem Generating a 2048 bit RSA private key ............................+++ ... Please enter the following 'extra' attributes to be sent with your certificate request A challenge password : An optional company name : $ sudo openssl x509 -req -in client-req.pem -days 1000 -CA cacert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem Signature ok subject=/C=JP/ST=Tokyo/L=Shinjuku/O=co-meeting Inc/CN=hrendoh/emailAddress=hrendoh@gmail.com Getting CA Private Key
2011/06/24追記
注意: CentOS 5.5でも試して見たところ、server-key.pemとclient-key.pemの「Common Name」が同じだと接続がエラーとなりました。「Common Name」にそれぞれ異なる値を指定してください。
my.cnfに設定を追加する
$ sudo vi my.cnf
クライアントは太字箇所を追加
# This will be passed to all mysql clients # It has been reported that passwords should be enclosed with ticks/quotes # escpecially if they contain "#" chars... # Remember to edit /etc/mysql/debian.cnf when changing the socket location. [client] port = 3306 socket = /var/run/mysqld/mysqld.sock ssl-ca=/etc/mysql/cacert.pem ssl-cert=/etc/mysql/client-cert.pem ssl-key=/etc/mysql/client-key.pem
サーバー側は太字箇所のコメント外すだけ
# * Security Features # # Read the manual, too, if you want chroot! # chroot = /var/lib/mysql/ # # For generating SSL certificates I recommend the OpenSSL GUI "tinyca". # ssl-ca=/etc/mysql/cacert.pem ssl-cert=/etc/mysql/server-cert.pem ssl-key=/etc/mysql/server-key.pem
設定の確認
$ mysql -uroot mysql> show variables like 'have_ssl'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | have_ssl | YES | +---------------+-------+ 1 row in set (0.00 sec)
有効になりました。
補足:
mysqlに接続するとき、以下のようなエラーが出たら
$ mysql -uroot ERROR 2026 (HY000): SSL connection error
各ファイルの設定が問題ないかチェック。それでもエラーが出るときは、以下のコマンドでpemファイルを検証
$ sudo openssl verify -purpose sslserver -CAfile cacert.pem /etc/mysql/server-cert.pem
OKの時は以下の出力が出ます。エラーの時は各証明書とキーを作り直した方が解決が早そうです。
/etc/mysql/server-cert.pem: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd error 18 at 0 depth lookup:self signed certificate OK
2011/06/24追記
CentOS 5.5では、server-key.pemとclient-key.pemの「Common Name」が同じ値の場合も同様の接続エラーとなります。その場合は「Common Name」に異なる値を指定してキーを作り直してください。
2011/07/01追記
CentOS 5.5では、my.cnfの[client]にUbuntuではデフォルトで設定されているportとsocketの定義があるとMySQLの起動が出来なくなるので注意。
tcpflowで暗号化を確認
show databasesの通信を見てみました。ちゃんと暗号化されています。
sudo tcpflow -c port 3306 -i wlan0 tcpflow[10901]: listening on wlan0 192.168.100.100.35577-192.168.100.102.03306: ....0N.Mo........D.o5:T.Y.....1..\:........<.t".cS]N. 192.168.100.102.03306-192.168.100.100.35577: .... r.../..k..s.>J.t.d~...@.jJ. .x.y.......PR...z.1.^......d.Ys...\..".3[...n...4..!.....V...>.>o ...X"b.@..Q_(:....4..V..~...(.y:......=.....I9..V}.>0...YI..G.y....[]...W.......+..ti.....qLw
元はこれ
hrendoh@hrendoh-UL20A:~$ sudo tcpflow -c port 3306 -i wlan0 tcpflow[11055]: listening on wlan0 192.168.100.100.48896-192.168.100.102.03306: .....show databases 192.168.100.102.03306-192.168.100.100.48896: .....K....def.information_schema.SCHEMATA.SCHEMATA.Database.SCHEMA_NAME...@................"......information_schema.....mysql.....performance_schema.....test.......".
JDBCで接続してみる
公式ドキュメントに従って設定してみました。
英語:http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-using-ssl.html
日本語:http://dev.mysql.com/doc/refman/5.1/ja/connector-j-reference-using-ssl.html
ドキュメントによるとSSLを使うとクエリーの実行が30〜50%遅くなるらしいです。
「The performance penalty for enabling SSL is an increase in query processing time between 35% and 50%」
keytoolでトラストストア?を作成
上で作成したサーバーの証明書cacert.pemをインポートします。
$ keytool -import -alias mysqlServerCACert -file /etc/mysql/cacert.pem -keystore truststore キーストアのパスワードを入力してください: 新規パスワードを再入力してください: 所有者: O=Internet Widgits Pty Ltd, ST=Some-State, C=AU 発行者: O=Internet Widgits Pty Ltd, ST=Some-State, C=AU シリアル番号: c8445e5e08aa9cf5 ... この証明書を信頼しますか? [no]: yes 証明書がキーストアに追加されました。
クライアント証明書をDER形式に変換後、キーストアにインポート
証明書はDER形式に変換してからじゃないとキーストアにインポート出来ないらしいです。変換はopensslコマンドで実行します。
$ sudo openssl x509 -outform DER -in /etc/mysql/client-cert.pem -out client.cert $ keytool -import -file client.cert -keystore keystore -alias mysqlClientCertificate キーストアのパスワードを入力してください: 新規パスワードを再入力してください: 所有者: EMAILADDRESS=hrendoh@gmail.com, CN=hrendoh, O=co-meeting Inc, L=Shinjuku, ST=Tokyo, C=JP 発行者: O=Internet Widgits Pty Ltd, ST=Some-State, C=AU シリアル番号: 1 有効期間の開始日: Thu Jun 23 21:57:22 JST 2011 終了日: Wed Mar 19 21:57:22 JST 2014 証明書のフィンガープリント: MD5: 74:8D:12:1F:47:23:39:A4:E9:32:FF:CC:20:EC:27:24 SHA1: E2:48:4F:C8:70:FD:32:C4:FF:AC:F8:B4:DA:2E:22:8B:FD:75:0E:C1 署名アルゴリズム名: SHA1withRSA バージョン: 1 この証明書を信頼しますか? [no]: yes 証明書がキーストアに追加されました。
Java実行時にシステムパラメータを指定
-Djavax.net.ssl.keyStore=path_to_keystore_file -Djavax.net.ssl.keyStorePassword=********* -Djavax.net.ssl.trustStore=path_to_truststore_file -Djavax.net.ssl.trustStorePassword=*********
Tomcatであればbin/startup.shを呼ぶ際にJAVA_OPTSに上記のシステムパラメータを指定します。
$ export JAVA_OPTS="-Djavax.net.ssl.keyStore=/keystore -Djavax.net.ssl.keyStorePassword=keystor -Djavax.net.ssl.trustStore= /truststore -Djavax.net.ssl.trustStorePassword=keystore"
JDBCの設定
太字の「useSSL=true」と「requireSSL=true」を追加します。
&useSSL=true&requireSSL=true" username="root" password="" validationQuery="select 1" />
以上で、設定終了です。