Tomcat 8からRDS for MySQLへのJDBC SSL接続／2015年の証明書版メモ

Tomcat 8からAWSのRDS for MySQLに、SSL証明書を使ってJDBC接続するための設定です。
2015年に証明書が更新され（ルート証明書・リージョン別中間証明書、新・旧など）複数に分かれた関係で、提供される.pemファイルが（2010年版証明書の手順のままでは）.jksファイルに正しく変換できなくなっていましたので、手順をメモとして残しておきます。

# ポイント

* [https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem](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](http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html)（AWS公式）
* [http://www.infoscoop.org/blogjp/2013/01/25/rds-ssl/](http://www.infoscoop.org/blogjp/2013/01/25/rds-ssl/)（Tomcat 7＋2010年版証明書でDBCPを使う例）
* [https://blog.cles.jp/item/5349](https://blog.cles.jp/item/5349)（↑の元ネタ）
* [http://stackoverflow.com/questions/32156046/using-java-to-establish-a-secure-connection-to-mysql-amazon-rds-ssl-tls](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）に、以下のようなエラーが記録され、正しく接続できません。

```text: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:
mysql> GRANT 【権限】 ON 【対象スキーマ.テーブル名など】 TO 【ユーザ名】 IDENTIFIED BY '【パスワード】' REQUIRE SSL;
```

## 2．証明書を用意する

```bash:
$ 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-root.pem)（ルート証明書）
* [https://s3.amazonaws.com/rds-downloads/rds-ca-2015-ap-northeast-1.pem](https://s3.amazonaws.com/rds-downloads/rds-ca-2015-ap-northeast-1.pem)（東京リージョンの中間証明書）

## 3．server.xmlまたはコンテキストファイルに記述（Commons DBCPを使う場合）

```text:xxx.xml（一部抜粋）
<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&amp;requireSSL=true&amp;verifyServerCertificate=true&amp;trustCertificateKeyStoreUrl=file:///etc/pki/java/amazon.jks&amp;trustCertificateKeyStoreType=JKS&amp;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で名前解決をキャッシュしないように（または短時間のキャッシュにとどめるように）設定する必要もありそうです。