Rails から MySQL に SSL を用いて通信を行う

ActiveRecord には SSL によるデータベースアクセスをサポートする機能が付属しているので、それを使って通信を暗号化してみます。検証には VirtualBox にインストールした CentOS 5.6 を使用し、作業はすべて root ユーザで行いました。

前提

mysql と openssl は yum からインストールしており Rails アプリケーションを通常の方法で動作させる環境は整っているものとします。

証明書の作成

今回は動作の確認が目的なので、自己証明書を使ってテストします。証明書の作り方は以下のアドレスを参考にしました。

http://blog.livedoor.jp/burtman/archives/51962114.html

記事中で www.hoge.com となっている箇所を 192.168.1.156(テスト用仮想マシンのプライベートIP)に置き換えて、パスフレーズはすべて「password」とします。途中、国の指定など色々聞かれますが、メモしつつ適当に答えてしまえば問題ありません。

MySQL サーバ側の設定

上記の手順の通りに証明書を作成したものとして、以下のように証明書をコピーし、ファイルの所有権を mysql ユーザに移譲します。

mkdir /etc/mysql
cp /var/ssl/CA/cacert.pem /etc/mysql/
cp /var/ssl/CA/server_cert/192.168.1.156/192.168.1.156.cer /etc/mysql/
cp /var/ssl/CA/server_cert/192.168.1.156/192.168.1.156.key /etc/mysql/
chown -R mysql:mysql /etc/mysql

次に my.cnf を開いて、以下の行を追加します。

ssl-ca=/etc/mysql/cacert.pem
ssl-cert=/etc/mysql/192.168.1.156.cer
ssl-key=/etc/mysql/192.168.1.156.key

完了したら mysqld を再起動します。

/etc/init.d/mysqld restart

うまく起動しない時は、記述にミスがないか確認して下さい。起動が終わったら mysql コマンドから対話型ウインドウを開き、SSL が有効になっているかチェックします。

show variables like 'have_ssl';

結果が YES になっていれば SSL が有効になっています。

次に、アプリケーションを動作させる MySQL のユーザを SSL を用いる前提で作成します。

grant all on some_database.* to rails@localhost identified by 'password' require ssl;

この状態で接続できるかチェックしてみます。

mysql -u rails -p --ssl-ca=/etc/mysql/cacert.pem

うまく接続できていれば、サーバ側の設定は完了です。

アプリケーション側の設定

まずはサーバ側同様に証明書をコピーします。今回は root 権限でアプリケーションを動かすのでファイルの所有権は変更しませんが、実運用の場合は適宜変更して下さい。

cp /var/ssl/CA/cacert.pem /path/to/rails/root/db/
cp /var/ssl/CA/client_cert/test01.cer /path/to/rails/root/db/
cp /var/ssl/CA/client_cert/test01.key /path/to/rails/root/db/

次にデータベースへの接続設定を変更します。config/database.yml を開いて、先ほど設定した値にそれぞれ修正します。

development:
  adapter: mysql2
  database: some_database
  host: localhost
  username: rails
  password: password
  sslca: /path/to/rails/root/db/cacert.pem
  sslkey: /path/to/rails/root/db/test01.key
  sslcert: /path/to/rails/root/db/test01.cer
  # 以下略

この状態で rails console を実行して、以下のコマンドを実行します。

ActiveRecord::Base.connection.execute("show status like 'Ssl_cipher';").first

うまく実行できると、"DHE-RSA-AES256-SHA" と値が返されるはずです。これで、データベースとの接続が暗号化されました。