MySQLでSSLクライアント証明書を使ってみたくなったので試してみました。
MySQL のバージョンとか
- CentOS 6.4
- MySQL 5.5.34 (mysql-server-5.5.34-1.el6.remi.x86_64)
CA証明書とサーバ証明書を作成
証明書を保存するディレクトリを作成します。
どこでもいいのですがとりあえず /etc/mysql/ssl/
にします。
# mkdir -p /etc/mysql/ssl/
# cd /etc/mysql/ssl/
CAの秘密鍵と自己署名証明書を作成します。
# openssl req -new -out ca.crt -keyout ca.key -x509 -days 3650 -newkey rsa:2048 -nodes -subj "/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=OreOre CA"
Generating a 2048 bit RSA private key
..............................
writing new private key to 'ca.key'
-----
秘密鍵のパーミッションを変更します。0644 のままでも動作しますが変更しておいて損はないでしょう。
# chmod 600 ca.key
サーバ証明書の秘密鍵とCSRを作成します。CN(example.jp
)にはサーバのホスト名を指定します。
# openssl req -new -out server.csr -keyout server.key -newkey rsa:2048 -nodes -subj "/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=example.jp"
Generating a 2048 bit RSA private key
..............................
writing new private key to 'server.key'
-----
秘密鍵のパーミッションとオーナーをを変更します。パーミッションを変更する場合 mysqld から読めるようにオーナーの変更も必要です。
# chmod 600 server.key
# chown mysql: server.key
サーバ証明書を作成します。
# openssl x509 -req -days 3650 -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial
Signature ok
subject=/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=example.jp
Getting CA Private Key
クライアント証明書の秘密鍵とCSRを作成し、CSRをサーバに送信します。 この作業はクライアント側で行います。
$ openssl req -new -out client.csr -keyout client.key -newkey rsa:2048 -nodes -subj "/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=ore@example.jp"
Generating a 2048 bit RSA private key
..............................
writing new private key to 'client.key'
-----
$ chmod 600 client.key
$ scp client.csr example.jp:/tmp/
サーバ側でクライアント証明書を作成します。
# cd /etc/mysql/ssl/
# openssl x509 -req -days 3650 -in /tmp/client.csr -out /tmp/client.crt -CA ca.crt -CAkey ca.key -CAcreateserial
Signature ok
subject=/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=ore@example.jp
Getting CA Private Key
クライアント側にCA証明書とクライアント証明書をダウンロードします。
$ scp example.jp:/etc/mysql/ssl/ca.crt ./
$ scp example.jp:/tmp/client.crt ./
MySQL の設定
MySQLでサーバ証明書を設定します。
とりあえずSSL通信だけを試すので ssl-ca
は設定しません。
ssl-key = /etc/mysql/ssl/server.key
ssl-cert = /etc/mysql/ssl/server.crt
設定を反映します。
# service mysqld restart
設定が有効になっていることを確認します。
# mysql -e "SHOW VARIABLES LIKE '%ssl%'"
次のように出力されるはずです。
+---------------+---------------------------+
| Variable_name | Value |
+---------------+---------------------------+
| have_openssl | YES |
| have_ssl | YES |
| ssl_ca | |
| ssl_capath | |
| ssl_cert | /etc/mysql/ssl/server.crt |
| ssl_cipher | |
| ssl_key | /etc/mysql/ssl/server.key |
+---------------+---------------------------+
SSLが必須なユーザーを作成します。
# mysql
次のように作成します。
GRANT ALL ON test.* TO ssluser@'%' IDENTIFIED BY 'pass' REQUIRE SSL;
SSLで接続
まずは普通に接続してみます。ssluser
はSSLが必須なので弾かれます。
$ mysql -h example.jp -u ssluser -ppass
ERROR 1045 (28000): Access denied for user 'ssluser'@'192.0.2.101' (using password: YES)
--ssl
オプションを指定してみます。が、これだけだとSSL通信にはなりませんでした。
$ mysql -h example.jp -u ssluser -ppass --ssl
ERROR 1045 (28000): Access denied for user 'ssluser'@'192.0.2.101' (using password: YES)
SSLで接続するためには --ssl-cipher
か --ssl-ca
か --ssl-cert
の指定が必要です。
--ssl-cert
はクライアント証明書を指定するものなので今は使いません。ひとまず他の2つで接続してみます。
$ mysql -h example.jp -u ssluser -ppass --ssl-cipher=DHE-RSA-AES256-SHA
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=ca.crt
どちらでも接続できました。
--ssl-ca
を指定する場合はサーバ証明書がそのCA証明書で署名されている必要があります。
なので適当な自己署名証明書を指定しても接続できません。
$ openssl req -new -out another.crt -keyout another.key -x509 -days 3650 -newkey rsa:2048 -nodes -subj "/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=OreOre Another CA"
Generating a 2048 bit RSA private key
.................................
writing new private key to 'another.key'
-----
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=another.crt
ERROR 2026 (HY000): SSL connection error: error:00000001:lib(0):func(0):reason(1)
--ssl-verify-server-cert
を追加で指定するとサーバ証明書の CommonName と接続先ホスト名の比較も行われます。
例えば、次のように IP アドレスを指定すると接続できません。
$ mysql -h 192.0.2.100 -u ssluser -ppass --ssl-ca=ca.crt --ssl-verify-server-cert
ERROR 2026 (HY000): SSL connection error: SSL certificate validation failure
サーバ証明書の CommonName と同じホスト名で接続する必要があります。
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=ca.crt --ssl-verify-server-cert
クライアント証明書を使って接続
次にクライアント証明書を使ってみます。
まずは my.cnf でクライアント証明書に署名したCA証明書を ssl-ca
に指定します。
ssl-key = /etc/mysql/ssl/server.key
ssl-cert = /etc/mysql/ssl/server.crt
ssl-ca = /etc/mysql/ssl/ca.crt
設定を反映します。
# service mysqld restart
設定が反映されていることを確認します。
# mysql -e "SHOW VARIABLES LIKE '%ssl%'"
次のように表示されます。
+---------------+---------------------------+
| Variable_name | Value |
+---------------+---------------------------+
| have_openssl | YES |
| have_ssl | YES |
| ssl_ca | /etc/mysql/ssl/ca.crt |
| ssl_capath | |
| ssl_cert | /etc/mysql/ssl/server.crt |
| ssl_cipher | |
| ssl_key | /etc/mysql/ssl/server.key |
+---------------+---------------------------+
クライアント証明書が必要なユーザーを作成します。
$ mysql
次のように作成します。
GRANT ALL ON test.* TO ssluser@'%' IDENTIFIED BY 'pass' REQUIRE X509;
クライアント証明書を使わずに接続してみます。ssluser
はクライアント証明書が必要なので接続できません。
$ mysql -h example.jp -u ssluser -ppass --ssl-cipher=DHE-RSA-AES256-SHA
ERROR 1045 (28000): Access denied for user 'ssluser'@'192.0.2.101' (using password: YES)
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=ca.crt
ERROR 1045 (28000): Access denied for user 'ssluser'@'192.0.2.101' (using password: YES)
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=ca.crt --ssl-verify-server-cert
ERROR 1045 (28000): Access denied for user 'ssluser'@'192.0.2.101' (using password: YES)
次のようにクライアント証明書を指定すれば接続出来ます。
$ mysql -h example.jp -u ssluser -ppass --ssl-cert=client.crt --ssl-key=client.key
もちろん --ssl-ca
や --ssl-verify-server-cert
を一緒に指定することも出来ます。
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=ca.crt --ssl-verify-server-cert --ssl-cert=client.crt --ssl-key=client.key
その他
今回はCA証明書とサーバ証明書を別にしましたが、同じでも大丈夫です。
その場合は次のようにサーバ証明書を自己署名で作成し、
$ openssl req -new -out server.crt -keyout server.key -x509 -days 3650 -newkey rsa:2048 -nodes -subj "/C=JP/ST=Tokyo/L=SonoHen/O=OreOre/OU=Dev/CN=example.jp"
クライアント証明書にはサーバ証明書で署名して、
$ openssl x509 -req -days 3650 -in /tmp/client.csr -out /tmp/client.crt -CA server.crt -CAkey server.key -CAcreateserial
my.cnf でCA証明書にサーバ証明書を指定して、
ssl-key = /etc/mysql/ssl/server.key
ssl-cert = /etc/mysql/ssl/server.crt
ssl-ca = /etc/mysql/ssl/server.crt
クライアント側は --ssl-ca
にサーバ証明書を指定します。
$ mysql -h example.jp -u ssluser -ppass --ssl-ca=server.crt --ssl-verify-server-cert --ssl-cert=client.crt --ssl-key=client.key