【備忘録】自己署名証明書を利用した WebSphere Liberty - Db2 間の JDBC 通信暗号化


かつてはファイアウォールの内側にあるDBクライアントとDBサーバ間の通信は暗号化不要とされてきましたが、クラウド化、マイクロサービス化が進むにつれて、DBクライアント(アプリケーションサーバ)とDBサーバの間の通信も暗号化を行うケースが増えているように感じます。
そこで備忘録として、WebSphere Liberty - Db2 データベース間のJDBC通信をTLS/SSL化する手順をメモとして残しておきます。

## 証明書の種類

TLS/SSLを利用する目的は2つあります。

 * 通信の暗号化
 * なりすまし防止(サイト運営者の実在性の証明)

この2つを満たす目的では、信頼ある第三者である認証局(CA)によって署名された「TLS/SSLサーバ証明書」を取得する必要があります。
しかし、第三者認証局から証明書を発行してもらうにはお金がかかります。
インフラエンジニアが構成方法を確認してみたいだけのテスト目的であるなど、証明書は信頼度のあるものでなくてよい場合には、自己署名証明書が便利です。

「TLS/SSLサーバ証明書(認証局に署名されたもの)」
「自己署名証明書(自分で署名したもの)」
のどちらかを使うのかによって、Db2サーバ側で行う鍵ストアの構成手順が変わるため完全に同じ手順とはなりませんが、JDBCクライアント側の構成は基本的に共通です。



## 一方向の認証と双方向の認証

一方向の認証では、DBサーバはクライアントにサーバの証明書を提示し、クライアントはその証明書が本物であり、改ざんされていないことを確認します。
双方向の認証では、クライアントとサーバの両方がそれぞれの証明書を相互の妥当性検証のために送信します。
一方向の認証のプロセスに加えて、クライアントが自分のクライアント証明書を提示し、その証明書に対応する秘密鍵所持をサーバへ示すプロセスが追加されることで、より強いセキュリティが提供できるようになります。
ここではDBクライアント-DBサーバ間では現状、最も典型的な構成パターンと思われる一方向認証を想定します。

<!--補足：余談として、オンプレミス環境などファイアウォールの内側が完全に閉じた状態で、ミドルウェア間で行われる通信がとりあえず暗号化されていればOKと割り切れる場合にも、自己署名証明書で十分という判断がされることもあります。※：自己署名証明書の利用をおすすめする意図ではありません、念のため。-->


## 作業の流れ
以下の流れで構成を進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468682/6cbe3a1b-0b5c-6165-3349-a9d79e3265b0.png)

#### 各作業の実行サーバ/ユーザ

| 手順 |実行サーバ | 実行ユーザ|
|:-:|:-:|:-:|
| Step1  | DBサーバ  |  Db2インスタンスオーナ |
| Step2  | DBサーバ   |Db2インスタンスオーナ  |
| Step3  | DBサーバ| Db2インスタンスオーナ<br>(/etc/services編集時は、root)  |
| Step4  |  DBクライアント| DBクライアント実行ユーザ <br>(/etc/hosts編集時は、root) |



## 構成図
DBサーバ(Db2)と、クライアント(Liberty)に、それぞれ以下の図のような構成を行っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468682/a2ef5a60-e8dd-390c-3666-c494effd4bdf.png)

自己署名証明書を使うので、証明書のコモンネーム(CN)と合わせるhostsエントリが必要になります。

## JDBC over SSL 構成手順

### Step1. 鍵ストア/自己署名証明書の作成

 * 実行サーバ：Db2サーバ
 * 実行ユーザ：Db2インスタンスオーナ


####1-1. 鍵ストア作成

鍵ファイルの出力先ディレクトリを作成

```
mkdir /database/mytmp/ssl
```

鍵データベースを作成

```
$ ~/sqllib/gskit/bin/gsk8capicmd_64 -keydb -create -db "/database/mytmp/ssl/mydbserver.kdb" -pw "PASSWORD" -stash
```

作成されたファイルを確認

```
$ ls -ltr /database/mytmp/ssl
total 16
-rw------- 1 db2inst1 db2iadm1  88 Aug 17 07:19 mydbserver.rdb
-rw------- 1 db2inst1 db2iadm1  88 Aug 17 07:19 mydbserver.kdb
-rw------- 1 db2inst1 db2iadm1  88 Aug 17 07:19 mydbserver.crl
-rw------- 1 db2inst1 db2iadm1 193 Aug 17 07:19 mydbserver.sth

```

#### 1-2. 自己署名証明書の作成 / 鍵ストアへの追加
下記コマンドを実行し、サーバの自己署名証明書を鍵データベースに追加します。

```
$ ~/sqllib/gskit/bin/gsk8capicmd_64 -cert -create -db "/database/mytmp/ssl/mydbserver.kdb" -pw "PASSWORD" -label "myselfsigned" -dn "CN=db2.myssl-test.com,O=myOrganization,OU=myOrganizationUnit,L=myLocation,ST=ON,C=CA"
```

上記コマンドで指定する証明書関連の情報のうち、下記項目は後の構成時に必要となります

 * ラベル(label)：
	 * コマンドで指定した値：myselfsigned 
	 * Db2のTLS/SSLサーバ証明書のラベルを指定するDBM構成パラメータ ssl_srv_label に設定
 * コモンネーム(CN)： 
	 * コマンドで指定した値：db2.myssl-test.com
	 * Liberty の server.xml で、接続先ホスト名として指定する。/etc/hosts にも記述する

鍵データベースが更新されていることを確認

```
$ ls -ltr /database/mytmp/ssl
total 20
-rw------- 1 db2inst1 db2iadm1   88 Aug 17 07:19 mydbserver.rdb
-rw------- 1 db2inst1 db2iadm1   88 Aug 17 07:19 mydbserver.crl
-rw------- 1 db2inst1 db2iadm1  193 Aug 17 07:19 mydbserver.sth
-rw------- 1 db2inst1 db2iadm1 5088 Aug 17 07:20 mydbserver.kdb
　　←最終更新時刻が変わっている
```

#### 1-3. 証明書の抽出

下記コマンドを実行して作成した自己署名証明書をファイルに抽出します。
  (→  ここで抽出した証明書ファイルは、後述の  Step2. でクライアントに配置するためのトラストファイルにimportする)

```
$ ~/sqllib/gskit/bin/gsk8capicmd_64 -cert -extract -db "/database/mytmp/ssl/mydbserver.kdb" -pw "PASSWORD" -label "myselfsigned" -target "/database/mytmp/ssl/mydbserver.arm" -format ascii -fips
```

サーバ証明書(mydbserver.arm)が作成されていることを確認

```
$ ls -ltr /database/mytmp/ssl
total 24
-rw------- 1 db2inst1 db2iadm1   88 Aug 17 07:19 mydbserver.rdb
-rw------- 1 db2inst1 db2iadm1   88 Aug 17 07:19 mydbserver.crl
-rw------- 1 db2inst1 db2iadm1  193 Aug 17 07:19 mydbserver.sth
-rw------- 1 db2inst1 db2iadm1 5088 Aug 17 07:20 mydbserver.kdb
-rw-r--r-- 1 db2inst1 db2iadm1 1375 Aug 17 07:20 mydbserver.arm
　　←作成されたファイル
```

### Step2. トラストストアの作成/転送

 * 実行サーバ：Db2サーバ
 * 実行ユーザ：Db2インスタンスオーナ

トラストストアを作成し、ひとつ前の手順で作成(extract) したサーバ証明書「mydbserver.arm」をトラストストアに追加します。

トラストストアとは、クライアントが信頼するすべての証明書を保存する場所です。
Db2サーバが利用する鍵ストアファイル(拡張子 .kdb) は ikeyman で作成します。
Db2クライアントが非Javaであれば、クライアント側にも ikeyman で作成した鍵ストアファイル( .kdb) を配置しますが、今回のクライアントはLiberty(Javaランタイム)であるため .kdb の鍵ストアファイルの利用は出来ません。
代わりに、JDKが提供する keytool コマンドを利用してトラストストアファイル(拡張子 .jks) を作成します。

__補足：__

 * ikeyman(GSKit) も keytool コマンドも、キーと証明書を管理するためのユーティリティです。
 * ikeyman は、IBM 固有の形式であるCMS形式(Certificate Management System)(拡張子 .kdb) と、 Public-Key Cryptography Standards #12 (PKCS12) オープン・スタンダード形式(拡張子 .jks) を作成できます。
 * Db2 は、CMS 鍵ストア (拡張子 .kdb) 、PKCS#12 鍵ストア (拡張子 .p12)  の両方をサポートします。



#### 2-1. トラストストアの作成 / 証明書のインポート
以下のコマンドを実行すると、トラストストア「myTrustStore.jks」が存在しない場合は新規作成し、サーバ証明書「mydbserver.arm」が「myTrustStore.jks」に追加されます。

```
$ cd /database/mytmp/ssl
$ /opt/ibm/db2/V11.5/java/jdk64/bin/keytool -import -trustcacerts -alias myalias -file mydbserver.arm -keystore myTrustStore.jks
Enter keystore password:    【 ← パスワードを指定 】
Re-enter new password:      【 ← パスワードを指定(再入力) 】
Owner: CN=db2.myssl-test.com, OU=myOrganizationUnit, O=myOrganization, L=myLocation, ST=ON, C=CA
Issuer: CN=db2.myssl-test.com, OU=myOrganizationUnit, O=myOrganization, L=myLocation, ST=ON, C=CA
Serial number: 2548b9b4b8a2ea3c
Valid from: 8/16/21 7:20 AM until: 8/17/22 7:20 AM
Certificate fingerprints:
         MD5:  7E:83:F6:C3:37:B7:98:74:3A:8E:2D:23:61:8D:22:E2
         SHA1: B8:C4:1E:D2:32:FA:8B:39:7C:94:8E:93:6E:D7:58:E1:15:0E:B2:05
         SHA256: 92:BC:24:C3:97:B9:BD:1F:7D:56:D4:28:69:A7:58:E6:CB:7F:A9:C9:5C:BD:FF:98:91:7E:95:33:D1:CB:D2:8E
         Signature algorithm name: SHA256withRSA
         Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: e6 3f 66 67 a8 0c 2a 69  01 3e 0b 90 fb 9e 48 58  ..fg...i......HX
0010: 56 74 14 5e                                        Vt..
]
]

#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: e6 3f 66 67 a8 0c 2a 69  01 3e 0b 90 fb 9e 48 58  ..fg...i......HX
0010: 56 74 14 5e                                        Vt..
]
]

Trust this certificate? [no]:  yes　　　【 ← yesを入力、Enter 】
Certificate was added to keystore

```

#### 2-2. TrustStoreに証明書が存在することを確認

2-1、で指定したトラストストアのパスワードを入力する必要があります。
キーストアの形式(ここではjks)や、このトラストストアに格納される証明書の情報などを確認することができます。
例えば、コモンネームはdb2.myssl-test.comで、期限はデフォルトだと1年間、などといった情報が確認できます。

```
$ /opt/ibm/db2/V11.5/java/jdk64/bin/keytool -list -v -keystore myTrustStore.jks
Enter keystore password:　←2-1.(トラストストア作成時)で決めたパスワード

Keystore type: jks
Keystore provider: IBMJCE

Your keystore contains 1 entry

Alias name: myalias
Creation date: Aug 17, 2021
Entry type: trustedCertEntry

Owner: CN=db2.myssl-test.com, OU=myOrganizationUnit, O=myOrganization, L=myLocation, ST=ON, C=CA
Issuer: CN=db2.myssl-test.com, OU=myOrganizationUnit, O=myOrganization, L=myLocation, ST=ON, C=CA
Serial number: 2548b9b4b8a2ea3c
Valid from: 8/16/21 7:20 AM until: 8/17/22 7:20 AM
Certificate fingerprints:
         MD5:  7E:83:F6:C3:37:B7:98:74:3A:8E:2D:23:61:8D:22:E2
         SHA1: B8:C4:1E:D2:32:FA:8B:39:7C:94:8E:93:6E:D7:58:E1:15:0E:B2:05
         SHA256: 92:BC:24:C3:97:B9:BD:1F:7D:56:D4:28:69:A7:58:E6:CB:7F:A9:C9:5C:BD:FF:98:91:7E:95:33:D1:CB:D2:8E
         Signature algorithm name: SHA256withRSA
         Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: e6 3f 66 67 a8 0c 2a 69  01 3e 0b 90 fb 9e 48 58  ..fg...i......HX
0010: 56 74 14 5e                                        Vt..
]
]

#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: e6 3f 66 67 a8 0c 2a 69  01 3e 0b 90 fb 9e 48 58  ..fg...i......HX
0010: 56 74 14 5e                                        Vt..
]
]



*******************************************
*******************************************

```

#### 2-3. 作成したトラストストアファイルをクライアント側に転送
scpコマンド、WinSCPなどのユーティリティを用いてクライアント(Liberty)稼働サーバにトラストストアを転送しておきます。

後続のStep4.で、転送したトラストファイルのフルパスをLiberty server.xml に指定します。


### Step3. Db2サーバの構成

 * 実行サーバ：Db2サーバ
 * 実行ユーザ：Db2インスタンスオーナ
    * ※ /etc/services 編集作業は root ユーザで実行

#### 3-1. 鍵ストアファイル、スタッシュファイルの配置

鍵ストアファイルを、Step1で鍵ストア作成等の作業を行ったワーク用ディレクトリ(/database/mytmp/ssl)から、実際に鍵ストアファイル/スタッシュファイルを配置するためのディレクトリにコピーします。

 * コピー先：$HOME/sqllib/security/keystore

```
$ cp -p /database/mytmp/ssl/mydbserver.kdb $HOME/sqllib/security/keystore/
$ cp -p /database/mytmp/ssl/mydbserver.sth $HOME/sqllib/security/keystore/
$ ls -ltr
total 12
-rw------- 1 db2inst1 db2iadm1  193 Aug 17 07:19 mydbserver.sth
-rw------- 1 db2inst1 db2iadm1 5088 Aug 17 07:20 mydbserver.kdb
```

#### 3-2. Db2 パラメータ設定
レジストリー変数とデータベースマネージャー構成パラメータを設定します。

##### レジストリー変数
Db2が受け付ける通信プロトコルを設定します。
(V11.5デフォルトではTCPIPのみ設定されています。SSLは明示的に追加設定する)

```
$ db2set db2comm=ssl,tcpip
```

##### データベースマネージャー構成パラメータ ① ssl_svr_keydb
鍵ストアファイルの絶対パス名を指定します。

```
$ db2 update dbm cfg using SSL_SVR_KEYDB $HOME/sqllib/security/keystore/mydbserver.kdb
DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed
successfully.
```

##### データベースマネージャー構成パラメータ ② ssl_svr_stash
スタッシュファイルの絶対パス名を指定します。
(スタッシュファイルは、鍵ストアファイル作成時に -stash オプションを付与することで同時作成されます)

```
$ db2 update dbm cfg using SSL_SVR_STASH $HOME/sqllib/security/keystore/mydbserver.sth
DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed
successfully.
```


##### データベースマネージャー構成パラメータ ③ ssl_svr_label
Step1.で自己署名証明書を作成した際のコマンドオプションに指定したラベル(-label オプションに続いて入力した値）を指定します。

```
$ db2 update dbm cfg using SSL_SVR_LABEL myselfsigned
DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed
successfully.
```


##### データベースマネージャー構成パラメータ ④ ssl_versions
Db2 11.5.6 でサポートされるTLSバージョンはTLSv1.2までです。
TLSv1.2もデフォルトでは有効にならず、製品デフォルトの状態では TLSv1.0/v1.1が有効になるとマニュアルに記載されているので、ここで明示的に変更しておきます。

```
$ db2 update dbm cfg using SSL_VERSIONS TLSv12
DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed
successfully.
SQL1362W  One or more of the parameters submitted for immediate modification
were not changed dynamically. Client changes will not be effective until the
next time the application is started or the TERMINATE command has been issued.
Server changes will not be effective until the next DB2START command.
```

＊上のメッセージの通り、TLSv1.2通信を開始するためににはクライアント側からは接続を一度切断し、新規接続を作り直す必要があります。

##### データベースマネージャー構成パラメータ ⑤ ssl_svcename
Db2がSSH接続をListenするポートを設定します。
SSH用Listenポートは、ポート番号を直接記述してもいいですし、 /etc/services に記載されるサービス名を指定しても良いです。
(ポート番号を変えざるを得なくなった時にDb2構成に影響を与えない目的では、サービス名を指定するほうが望ましい)

```
$ db2 update dbm cfg using ssl_svcename db2inst1_ssl
DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed
successfully.
```

Tips：
DBM構成パラメータ SSL_SVCENAME に指定する サービス名が長すぎると文字数制限で update dbm cfg コマンドが失敗します。サービス名は14文字以内である必要があります。

```
$ db2 update dbm cfg using ssl_svcename db2c_db2inst1_ssl
SQL5132N  The configuration parameter "ssl_svcename" is null or too long.  The
maximum length is "14".
```


##### 補足： /etc/services ファイルの編集

デフォルトでTLS/SSL用通信ポートが/etc/servicesファイルに定義されていない場合、/etc/services を編集してTLS/SSL通信用ポートを設定します。
/etc/services ファイルの編集は root ユーザで行います。

/etc/services サンプル(抜粋)：

```
db2c_db2inst1   50000/tcp
db2inst1_ssl    50001/tcp　　← TLS/SSL通信用ポートを追記
```


##### Db2 インスタンス再起動

上記設定変更を反映するため、インスタンスの再起動を行います。

```
$ db2stop
$ db2start
```



### Step4. Db2クライアント(Liberty)の構成

 * 実行サーバ：Db2クライアント(Libertyサーバ)
 * 実行ユーザ：Liberty実行ユーザ
      * ※ /etc/hosts 編集作業は root ユーザで実行

#### 4-1. トラストストア・ファイルの配置

Step2. で作成して Liberty稼働サーバに転送しておいたトラストストア「myTrustStore.jks」のロケーションを確認してください。
今回は、下記ロケーションに配置。
[Liberty_root]/usr/shared/resources/db2

* usr/shared/resources までは Liberty に存在するディレクトリ
* Db2との通信用に使われるJDBCドライバーファイル、トラストファイルの置き場所として db2 サブディレクトリを手動作成


#### 4-2. JDBC データソース関連プロパティの構成

WebSphere Liberty の構成情報は、server.xml ファイルに記述します。

最も単純な構成では下記のロケーションに server.xml ファイルを作成/編集します。
[Liberty_root]/usr/servers/liberty1

TLS/SSL通信を行うために設定する必要のなるJDBCドライバープロパティは下記の通りです。

* serverName="DBサーバのホスト名"
* portNumber="DBサーバのTLS/SSL通信用ポート番号"
* databaseName="接続先DB名"
* user="DB接続ユーザID"
* password="DB接続ユーザのパスワード"
* sslConnection="true"
* sslTrustStoreLocation="トラストストアのファイルパス"
* sslTrustStorePassword="トラストストアのパスワード"

server.xml記述例(抜粋)：

```xml
<!-- Enable features -->
<featureManager>
 <feature>webProfile-8.0</feature>
    ---中略---
<library id="DB2JCC4Lib">
  <fileset dir="${shared.resource.dir}/db2" includes="db2jcc4.jar"/>
</library>
<dataSource id="Db2-DataSource" jndiName="jdbc/sample">
 <jdbcDriver libraryRef="DB2JCC4"/>
 <properties.db2.jcc databaseName="SAMPLE" driverType="4" serverName="db2.myssl-test.com" portNumber="50001" user="user01" password="password" sslConnection="true" sslTrustStoreLocation="${shared.resource.dir}/db2/myTrustStore.jks" sslTrustStorePassword="password"/>
</dataSource>
```


#### 4-3. /etc/hosts ファイルの編集
上の server.xml に記述したホスト名が名前解決できる状態であるか確認します。
今回は /etc/hosts ファイルに下記のエントリを記載します。
/etc/hosts ファイル編集は root ユーザで実行します。

```
192.168.0.2       db2.myssl-test.com
```

以上で、必要な構成は完了です。


### Step5. 接続確認

ブラウザから、Liberty上で稼働するDB接続アプリケーションを実行した後、
DBサーバ上でTLS/SSL用ポートで通信が行われていることを確認しておきます。

```
$ netstat -ano
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer
tcp        0      0 0.0.0.0:50000           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:50001           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 192.168.50.180:50001        192.168.50.178:52538    ESTABLISHED keepalive (3.51/0/0)
　　← TLS/SSL通信用ポート(50001)の通信が確立されESTABLISED状態となっている
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
```
<br>

以上です。


### 参考資料
[[DB2 LUW] SSL によってクライアント/サーバー間の通信を暗号化する方法](
https://www.ibm.com/support/pages/db2-luw-ssl-%E3%81%AB%E3%82%88%E3%81%A3%E3%81%A6%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E9%96%93%E3%81%AE%E9%80%9A%E4%BF%A1%E3%82%92%E6%9A%97%E5%8F%B7%E5%8C%96%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
[Complete guide to set up SSL using IBM Data Server Driver for JDBC and SQLJ](
https://www.ibm.com/support/pages/complete-guide-set-ssl-using-ibm-data-server-driver-jdbc-and-sqlj)
[Db2 マニュアル [転送中のデータの暗号化]](
https://www.ibm.com/docs/ja/db2/11.5?topic=encryption-data-in-transit)
[WebSphere Application Server Liberty マニュアル [データ・ソース (dataSource)]](
https://www.ibm.com/docs/ja/was-liberty/nd?topic=configuration-datasource)
[IBM Global Security Kit GSKit version 8 GSKCapiCmd Users Guide](
https://public.dhe.ibm.com/ps/products/db2/info/vr105/pdf/en_US/GSK_8.0.15_CapiCmd_UserGuide.pdf)