Tomcat 8からAWSのRDS for MySQLに、SSL証明書を使ってJDBC接続するための設定です。
2015年に証明書が更新され(ルート証明書・リージョン別中間証明書、新・旧など)複数に分かれた関係で、提供される.pemファイルが(2010年版証明書の手順のままでは).jksファイルに正しく変換できなくなっていましたので、手順をメモとして残しておきます。
ポイント
- https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem ファイルを使う
- ↑を.jksファイルに変換する際、証明書毎に1つずつ.jksファイルに追加していく
参考にしたもの
- http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html(AWS公式)
- http://www.infoscoop.org/blogjp/2013/01/25/rds-ssl/(Tomcat 7+2010年版証明書でDBCPを使う例)
- https://blog.cles.jp/item/5349(↑の元ネタ)
- http://stackoverflow.com/questions/32156046/using-java-to-establish-a-secure-connection-to-mysql-amazon-rds-ssl-tls(stack overflowでのQ&A)
2010年版証明書と同じ手順でインポートすると…
Tomcatのログ(catalina.out)に、以下のようなエラーが記録され、正しく接続できません。
23-Jan-2017 09:34:13.247 WARNING [localhost-startStop-2] org.apache.naming.NamingContext.lookup Unexpected exception resolving reference
org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Could not create connection to database server. Attempted reconnect 3 times. Giving up.)
(中略)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.
(中略)
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
(中略)
The last packet successfully received from the server was 9 milliseconds ago. The last packet sent successfully to the server was 9 milliseconds ago.
(中略)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
(中略)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
手順
1.MySQLのユーザを「REQUIRE SSL」付きで発行する
mysql> GRANT 【権限】 ON 【対象スキーマ.テーブル名など】 TO 【ユーザ名】 IDENTIFIED BY '【パスワード】' REQUIRE SSL;
2.証明書を用意する
$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem
$ csplit -b %02d.pem -z rds-combined-ca-bundle.pem /-----BEGIN/ {*}
※2015年版の場合、15個のファイルに分割される。
$ for i in {0..14}; do name=$(printf xx%02d $i); keytool -keystore amazon.jks -alias $name -importcert -file $name.pem; done;
※最初だけ、同じパスワードを2回入力して「yes」。2個目からは同じパスワードを1回ずつ入力する。
$ sudo cp amazon.jks /etc/pki/java/
$ sudo chmod 444 /etc/pki/java/amazon.jks
※念のため、既存の他ファイルと置き場所・パーミッションを合わせておいた。なお、600・400などにしてしまうと読めないので注意。
なお、東京リージョンのみで使う場合は、15個の証明書を変換・連結して.jksファイルに入れなくても、以下の2ファイルのみ変換して1個の.jksファイルにまとめるだけでも問題なく使うことができます。
- https://s3.amazonaws.com/rds-downloads/rds-ca-2015-root.pem(ルート証明書)
- https://s3.amazonaws.com/rds-downloads/rds-ca-2015-ap-northeast-1.pem(東京リージョンの中間証明書)
3.server.xmlまたはコンテキストファイルに記述(Commons DBCPを使う場合)
<Resource name="jdbc/mykomon" auth="container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
url="jdbc:mysql://xxx.xxx.ap-northeast-1.rds.amazonaws.com/【DB名】?useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:///etc/pki/java/amazon.jks&trustCertificateKeyStoreType=JKS&trustCertificateKeyStorePassword=【JKSファイル生成時パスワード】"
username="【MySQLユーザ名】"
password="【MySQLパスワード】"
/>
※Amazon LinuxでTomcat 8を使う場合、本家には含まれている「tomcat-dbcp.jar」がRPMパッケージに含まれず「commons-dbcp.jar」になっているため、「factory="org.apache.commons.dbcp.BasicDataSourceFactory"」が必要。
なお、SSL証明書を使う場合、ドメイン名(FQDN)が気になるところですが、urlに記述するホスト名はRoute53のPrivateゾーンに登録した別名(CNAMEレコード)などでも正しく接続できるようです。
※(別名を使うなど)接続先RDSのIPアドレスが変化することを想定するのであれば、JVMで名前解決をキャッシュしないように(または短時間のキャッシュにとどめるように)設定する必要もありそうです。