【Apache】mod_sslとmod_nssの違い

# はじめに
アクセス数のそこそこ多いサイト・Webサービスを運営する場合は、Webサーバ内でSSL処理を行うようなことはありませんが、アクセス数の少ないサイトではWebサーバ内でSSL/TLSの処理が必要なケースがあるかと思います。

ApacheでSSL/TLSを利用するためのモジュールとして、多くのケースではmod_sslが利用されていますが、近年ではmod_nssというモジュールも利用できます。

mod_nssについて良く知らなかったので、わかる範囲で調べてみました。
おもに、mod_sslとmod_nssとどう違うか、という観点からまとめています。

# 検証した環境
OS: CentOS7.4 (7.4.1708)
Apache: 2.4.6-67.el7.centos.6

mod_ssl: mod_ssl-2.4.6-67.el7.centos.6
mod_nss: mod_nss-1.0.14-10.el7_4.1

# 各モジュールの概要
## mod_ssl
ApacheでSSL/TLSを利用するための定番のモジュールです。

Apache2.4におけるmod_sslの公式ドキュメントは下記になります。
[mod_ssl - Apache HTTP Server Version 2.4](http://httpd.apache.org/docs/2.4/mod/mod_ssl.html)

このモジュールの実装はOpenSSLに依存します。
したがって、OpenSSLの脆弱性が発見された時には、mod_sslのバージョンアップが必要となる可能性があります。
dependencyを確認してみると、確かにlibssl.soに依存していることがわかります。

```bash:mod_sslの依存関係
# yum deplist mod_ssl
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: mirror.dmmlabs.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
package: mod_ssl.x86_64 1:2.4.6-67.el7.centos.6
  dependency: /bin/cat
   provider: coreutils.x86_64 8.22-18.el7
  dependency: /bin/sh
   provider: bash.x86_64 4.2.46-29.el7_4
  dependency: httpd
   provider: httpd.x86_64 2.4.6-67.el7.centos.6
  dependency: httpd = 2.4.6-67.el7.centos.6
   provider: httpd.x86_64 2.4.6-67.el7.centos.6
  dependency: httpd-mmn = 20120211x8664
   provider: httpd.x86_64 2.4.6-67.el7.centos.6
  dependency: libc.so.6(GLIBC_2.14)(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  dependency: libcrypto.so.10()(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: libcrypto.so.10(OPENSSL_1.0.1_EC)(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: libcrypto.so.10(OPENSSL_1.0.2)(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: libcrypto.so.10(libcrypto.so.10)(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: libdl.so.2()(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  dependency: libpthread.so.0()(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  dependency: libssl.so.10()(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: libssl.so.10(libssl.so.10)(64bit)
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
  dependency: openssl
   provider: openssl.x86_64 1:1.0.2k-8.el7
  dependency: openssl-libs >= 1:1.0.1e-37
   provider: openssl-libs.x86_64 1:1.0.2k-8.el7
   provider: openssl-libs.i686 1:1.0.2k-8.el7
  dependency: rtld(GNU_HASH)
   provider: glibc.x86_64 2.17-196.el7_4.2
   provider: glibc.i686 2.17-196.el7_4.2
```

## mod_nss
ApacheでSSL/TLSを利用するためのモジュールです。
SSL/TLSおよび各種暗号化の実装は、NSS(Network Security Services)というライブラリを使っています。

NSSの公式サイトは下記となります。
[Network Security Services - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS)

パッケージの依存関係を調べてみると、確かにNSSに依存しています。

```bash:mod_nssの依存関係
# yum deplist mod_nss
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: mirror.dmmlabs.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
package: mod_nss.x86_64 1.0.14-10.el7_4.1
  dependency: /bin/bash
   provider: bash.x86_64 4.2.46-29.el7_4
  dependency: /bin/sh
   provider: bash.x86_64 4.2.46-29.el7_4
  dependency: /usr/lib64/libnssckbi.so
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: httpd
   provider: httpd.x86_64 2.4.6-67.el7.centos.6
  dependency: httpd-mmn = 20120211x8664
   provider: httpd.x86_64 2.4.6-67.el7.centos.6
  dependency: libc.so.6(GLIBC_2.14)(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  dependency: libgcc_s.so.1()(64bit)
   provider: libgcc.x86_64 4.8.5-16.el7_4.1
  dependency: libgcc_s.so.1(GCC_3.0)(64bit)
   provider: libgcc.x86_64 4.8.5-16.el7_4.1
  dependency: libgcc_s.so.1(GCC_3.3.1)(64bit)
   provider: libgcc.x86_64 4.8.5-16.el7_4.1
  dependency: libnspr4.so()(64bit)
   provider: nspr.x86_64 4.13.1-1.0.el7_3
  dependency: libnss3.so()(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.10)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.10.2)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.11.7)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.12)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.2)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.3)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.4)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.5)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.6)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.9)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libnss3.so(NSS_3.9.2)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libplc4.so()(64bit)
   provider: nspr.x86_64 4.13.1-1.0.el7_3
  dependency: libplds4.so()(64bit)
   provider: nspr.x86_64 4.13.1-1.0.el7_3
  dependency: libsmime3.so()(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so()(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so(NSS_3.12.6)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so(NSS_3.14)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so(NSS_3.2)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so(NSS_3.4)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: libssl3.so(NSS_3.7.4)(64bit)
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: nss(x86-64) >= 3.19.1
   provider: nss.x86_64 3.28.4-15.el7_4
  dependency: nss-tools
   provider: nss-tools.x86_64 3.28.4-15.el7_4
  dependency: rtld(GNU_HASH)
   provider: glibc.x86_64 2.17-196.el7_4.2
   provider: glibc.i686 2.17-196.el7_4.2
```

NSSとOpenSSLとの大きな違いは、PKCS #11をサポートしているかどうかのようです。

How does NSS compare to OpenSSL?
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/FAQ

PKCS #11とは何か、といった話は、下記資料のPart4が分かりやすいと思います。
[セキュリティAPIに関する技術調査](https://www.ipa.go.jp/security/fy15/reports/sec_api/)

mod_nssの公式サイトは…恐らく下記URLだと思います…
\# yum infoから確認できる「URL」の項が下記URLです。
https://fedorahosted.org/mod_nss/


NSSは証明書データベースという形で証明書を管理します。この操作のためにcertutilコマンドが用意されています。
certutilコマンドのドキュメントは下記にあります。
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/tools/NSS_Tools_certutil

コマンドの操作であれば、Red Hat Enterprise Linuxの下記ドキュメントも参考になるでしょう。
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-web_servers#s2-apache-mod_nss-enabling



# 両者の差異
## 証明書・秘密鍵ファイルの管理・保存

両モジュールの大きな違いは、各種ファイルをどのように保持・管理するかという点にあります。
WebサーバにSSL証明書をインストールする際、最低限、以下のファイルを保持する必要があります。

- 秘密鍵
- サーバ証明書
- 中間証明書

mod_sslでは秘密鍵にはパスフレーズをかけた状態で／非暗号化した状態で保存しておくことができます。
またサーバ証明書・中間証明書はPEMファイルとして保存しておくケースが殆どかと思います。


一方、mod_nssでは（というかNSSライブラリにおいては）、certificate database（証明書データベース？）というデータベースファイルに上記情報を格納します。
これは秘密鍵を保持するファイルにパスフレーズをかけ、保護することができます。

certutilというコマンドで操作します。

下記のような感じで操作します。
カレントディレクトリ配下に「nssdb」というディレクトリを作成し、ここにcertificate databaseを作成・操作する例です。

```bash:
### certificate databaseの初期化
# mkdir ./nssdb/
# certutil -N -d ./nssdb/
Enter a password which will be used to encrypt your keys.
The password should be at least 8 characters long,
and should contain at least one non-alphabetic character.

Enter new password:
Re-enter password:
#
```

```bash:
### certificate databaseに格納された証明書を確認する
# certutil -L -d ./nssdb/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI
```

より詳しい操作は公式ドキュメントにあります。
[NSS tools : certutil - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_tools_:_certutil)

### mod_sslの設定例
mod_sslを利用する場合は、以下の3つのファイルが少なくとも必要になります。
\# CentOS 7.4で標準でインストールされるApacheは2.4.6ベースですので、SSLCertificateChainFileの指定が必要です。
\# http://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile

- SSLCertificateKeyFile: 秘密鍵
- SSLCertificateFile: サーバ証明書
- SSLCertificateChainFile: 中間証明書


デフォルトのssl.confの設定を元に、サイト固有の設定を作ってみます。
ドメイン名はexample.comとします。

ファイル名は下記のように仮定します。

|ファイル種類|ディレクティブ|ファイル名|
|----|----|----|
|秘密鍵|SSLCertificateKeyFile|privatekey.pem|
|サーバ証明書|SSLCertificateFile|servercert.pem|
|中間証明書|SSLCertificateChainFile|intermediate.pem|


```:httpd.confの設定例(mod_ssl)
Listen 443
SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog

SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300

SSLRandomSeed startup file:/dev/urandom  256
SSLRandomSeed connect builtin

SSLCryptoDevice builtin

<VirtualHost *:443>
    DocumentRoot /var/www/html/example.com
    ServerName example.com
    ErrorLog logs/example.com-443-error_log
    CustomLog logs/example.com-443-access_log combined

    <Directory "/var/www/html/example.com">
        Options -Indexes
        AllowOverride All
        Allow from All
    </Directory>

    SSLEngine on

    SSLProtocol -All +TLSv1.1 +TLSv1.2
    SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA

    SSLCertificateKeyFile /path/to/keys/example.com/privatekey.pem
    SSLCertificateFile /path/to/certs/example.com/servercert.pem
    SSLCertificateChainFile /path/to/certs/intermediate.pem
</VirtualHost>
```

恐らく上記のような設定ファイルになるでしょう。
これを直接httpd.confに記述するのではなく、httpd.confにincludeさせる構成にした方が便利でしょう。

### mod_nssの設定例
デフォルトのnss.confの設定を元に、サイト固有の設定を作ってみます。

ドメイン名はexample.comとします。
下記のファイルがPEM形式で既に存在しているものとします。

|ファイル種類|ファイル名|
|----|----|
|秘密鍵|privatekey.pem|
|サーバ証明書|servercert.pem|
|中間証明書|intermediate.pem|

まず、certificate databaseに秘密鍵と証明書をインストールする必要がありますが、下記のQ&Aによると、certutilにはPEM形式で秘密鍵をインポートする機能は用意されていないようです。

https://serverfault.com/questions/647658/how-to-add-an-existing-key-to-the-certutil-key-database

代わりに、PKCS#12形式（証明書と秘密鍵を併せて保存し、パスフレーズにより保護することができる）をインポートする機能があるようです。
pk12utilを使います。

したがって、秘密鍵とサーバ証明書ファイルがそれぞれPEM形式で存在する場合、以下の手順でインポートすることとなります。
※詳細は検証中。下記コマンドで上手くいくかどうかは確認中。

1. 秘密鍵・サーバ証明書をPKCS#12形式に変換
2. pk12utilで上記ファイルをインポート

```bash:
### PKCS#12形式に変換
$ openssl pkcs12 -export -out server-cert-and-key.pfx -inkey privatekey.pem -in servercert.pem -certfile intermediate.pem
```

```bash:
### pk12utilによるインポート
# pk12util -i server-cert-and-key.pfx -d /etc/httpd/alias
Enter Password or Pin for "NSS Certificate DB":
Enter password for PKCS12 file:
pk12util: PKCS12 IMPORT SUCCESSFUL

### certutilにより、インポートされたことを確認
# certutil -L -d /etc/httpd/alias
```

上記までの操作で、必要な証明書・秘密鍵がNSSのcertificate databaseに登録されることとなります。
これを踏まえて、httpd.confの設定を見ていきましょう。

なお、certificate databaseは、基本的にnicknameを指定して操作することとなります。
下記の設定例では、必要な証明書と秘密鍵が「Server-Cert-expample.com」というnicknameで保存されていることを前提としています。

```:httpd.confの設定例(mod_nss)
Listen 443

AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl    .crl

NSSPassPhraseDialog  builtin

NSSPassPhraseHelper /usr/libexec/nss_pcache

NSSSessionCacheSize 10000
NSSSessionCacheTimeout 100
NSSSession3CacheTimeout 86400

NSSRandomSeed startup builtin

NSSRenegotiation off

NSSRequireSafeNegotiation off

<VirtualHost *:443>
    DocumentRoot /var/www/html/example.com
    ServerName example.com
    ErrorLog logs/example.com-443-error_log
    CustomLog logs/example.com-443-access_log combined

    <Directory "/var/www/html/example.com">
        Options -Indexes
        AllowOverride All
        Allow from All
    </Directory>

    NSSEngine on

    NSSProtocol TLSv1.1,TLSv1.2
    NSSCipherSuite +aes_128_sha_256,+aes_256_sha_256,+ecdhe_ecdsa_aes_128_gcm_sha_256,+ecdhe_ecdsa_aes_128_sha,+ecdhe_ecdsa_aes_256_sha,+ecdhe_rsa_aes_128_gcm_sha_256,+ecdhe_rsa_aes_128_sha,+ecdhe_rsa_aes_256_sha,+rsa_aes_128_gcm_sha_256,+rsa_aes_128_sha,+rsa_aes_256_sha

    NSSNickname Server-Cert-expample.com
    NSSCertificateDatabase /etc/httpd/alias

</VirtualHost>
```

恐らく上記のような設定ファイルになるでしょう。
これを直接httpd.confに記述するのではなく、httpd.confにincludeさせる構成にした方が便利でしょう。

# Let's Encryptとの併用
Production環境での利用はともかく、開発・検証用の環境で無料のSSLサーバ証明書を利用しているケースは多いかと思います。
Let's Encryptを利用する場合、下記ドキュメントに従い、Certbotをインストールすることになるかと思います。

https://certbot.eff.org/#centosrhel7-apache


Certbotは、証明書・秘密鍵ファイルがPEM形式で保存されることを前提としています。
したがって、mod_sslであればこれを直接利用できますが、mod_nssで利用するには少々細工が必要です。

Certbotにより生成されたファイルをmod_nssで利用したい場合、これをcertificate databaseに取り込む必要がありますが、この操作を自動化する手段は現時点では用意されていないようです。
必要な場合は自前で作る必要があります。


# おわりに
mod_nssは情報が少なすぎて、何とも善し悪しを判断するのが難しい状況でした。
ドキュメントがヤドカリ状態だし、もしかしてメンテナンスされていないのかもしれません。