標準パッケージのApache httpdのmod_sslだけ入れ替えてTLS1.3に対応する

# TLS1.3 使いたい（けど理由はない）

CentOS 7 系では、パッケージ管理されている Apache httpd が openssl 1.0.x 系だから TLS1.3 が使えない[^amazonlinux]……でも使いたい、理由はないけどなんとなく使いたい、という気持ちに応えるのが趣旨であります。

ググってみると、自分で build してみよう！　という記事が多いですが、パッケージ管理されている httpd 用のパッケージが使えなくなってしまいます。

……とかなんとか理由をつけて、需要なさそうだけど**とりあえず面白い気がしたのでやってみよう！** といういつものパターンです。

[^amazonlinux]: Amazon Linux 2 も CentOS ベースなのでまだ openssl 1.0.2 なんすよね。

# 流れ

TLS1.3 を使うには openssl 1.1.1 が必要です[^openssl111]。まだ 1.1.1 が入っている環境は少ないので、まずそれを作ります。それから mod_ssl を作る、という流れです。

[^openssl111]: これが入ってるなら、こんな手間暇かかる作業は必要ないはず。

1. まず openssl のソースコードを入手して make する。他で使うこともあるかもしれないので make install もする
2. パッケージ管理でインストールされたバージョン[^httpdver]と同じ httpd のソースコードを入手して make する。こちらは本体は使わないので install しない 
3. mod_ssl を抜き出して httpd のモジュール位置に置く
4. mod_ssl 用の設定を加える

[^httpdver]: majorとminorがいっしょの、最新のものを使えばいけるんじゃないかなあと思ってます。

# 免責

楽しそうだから作っただけなので、ここで作ったものを本番環境で使うのはお勧めしません。すべて自己責任でお願いします。

# openssl 1.1.1 を build する

## build に必要なパッケージを入れる

はじめに gcc を入れる。何をするにも必要だしすでに入っている人も多いかと思いますが。

```
sudo yum install gcc
```

openssl のソースコード一式をダウンロードします。下のURLは、2020年12月9日現在の最新バージョン 1.1.1i へのURLになっています。

```
curl https://www.openssl.org/source/openssl-1.1.1i.tar.gz -O
```

バージョンの部分は、つねに最新のものに置き換えてください。

サクッと build してインストール。configure じゃないんだね。知らなかった。

```
tar zxvf openssl-1.1.1i.tar.gz
cd openssl-1.1.1i
./config
make
sudo make install_sw
```

## インストールできたか確認する

オプションを指定しなかったら openssl 1.1.1 は `/usr/local` 以下にインストールされる。

### ライブラリ

ライブラリは `lib64` にあるはず。

```
$ ls -la /usr/local/lib64
total 11076
drwxr-xr-x  4 root root     204 Dec  9 21:27 ./
drwxr-xr-x 13 root root     142 Jan 21  2020 ../
drwxr-xr-x  2 root root      55 Dec  9 21:27 engines-1.1/
-rw-r--r--  1 root root 5630034 Dec  9 21:27 libcrypto.a
lrwxrwxrwx  1 root root      16 Dec  9 21:27 libcrypto.so -> libcrypto.so.1.1*
-rwxr-xr-x  1 root root 3334216 Dec  9 21:27 libcrypto.so.1.1*
-rw-r--r--  1 root root 1017776 Dec  9 21:27 libssl.a
lrwxrwxrwx  1 root root      13 Dec  9 21:27 libssl.so -> libssl.so.1.1*
-rwxr-xr-x  1 root root  672536 Dec  9 21:27 libssl.so.1.1*
drwxr-xr-x  2 root root      61 Jan 21  2020 pkgconfig/
```

`/usr/local/lib64` が ld に設定されていないなら設定します。

（原理などはこちらの記事が詳しかった→ [CentOS7でldconfigを使って共有ライブラリを追加する](https://qiita.com/Esfahan/items/0064d845ca6faf7f3d47)）

```
echo "/usr/local/lib64" > usr.local.lib64.conf
sudo cp usr.local.lib64.conf /etc/ld.so.conf.d
sudo ldconfig
```

### コマンドラインツール

コマンドラインツールは、その下の `bin` にあります。

```
$ /usr/local/bin/openssl version
OpenSSL 1.1.1i  8 Dec 2020
```

# Apache httpd を build する

では次に mod_ssl を作ります。ほしいのは mod_ssl だけですが、まるっと httpd を build します。

## ホンモノをいれる

パッケージの httpd が入っていないなら入れましょう。

```
sudo yum install httpd
```

## 必要なパッケージをいれる

httpd の build に必要なパッケージを入れます。

```
sudo yum install apr-devel apr-util-devel pcre-devel
```

## build する

必要なのは mod_ssl だけなので、影響しなさそうなオプションは気にしない気にしない。

```
curl http://ftp.riken.jp/net/apache//httpd/httpd-2.4.46.tar.bz2 -O
tar jxvf httpd-2.4.46.tar.bz2
cd httpd-2.4.46
./configure --with-ssl=/usr/local/lib64
make
```

## ちゃんと openssl 1.1.1 を呼んでいるかな

mod_ssl のヘッダを見てみます。

```
readelf -d modules/ssl/.libs/mod_ssl.so
Dynamic section at offset 0x48d48 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libssl.so.1.1]
 0x0000000000000001 (NEEDED)             Shared library: [libcrypto.so.1.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [mod_ssl.so]
 0x000000000000000c (INIT)               0xa940
 0x000000000000000d (FINI)               0x3ade0
 0x0000000000000019 (INIT_ARRAY)         0x247df0
：
：
```

`libssl.so.1.1` を呼んでいればよし、そうでなければ `ldconfig` がうまくいっていない可能性が高いので見直してみてください。

## mod_ssl を設置、読み込むように設定

まず設置します。

```
sudo cp modules/ssl/.libs/mod_ssl.so /etc/httpd/modules/
```

`cond/httpd.conf` か、パッケージで導入したときと同じように `conf.modules.d/00-ssl.conf` を作るかして、以下の行を追加します。

```
LoadModule ssl_module modules/mod_ssl.so
```

## ssl.conf を作る

`ssl.conf` も自分で作らないといけません。最低限必要な部分は詰め込んであるはずなので、あとはお好みでカスタマイズしてください。

```conf:conf.d/ssl.conf
Listen 443 https

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

SSLStaplingCache shmcb:/tmp/stapling_cache(128000)

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

SSLCryptoDevice builtin
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite !DSS:ECDSA+AESGCM:EECDH+AESGCM
```

# 起動

これであとは virtualhost の設定、SSLの鍵・証明書があれば動くはず！

```
sudo systemctl start httpd.service
```

# 確認

起動したら、たぶん大丈夫。[ssllabs](https://www.ssllabs.com/ssltest/index.html) さんで確認します。

<img width="759" alt="tls13_sslabs_result.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223758/201f7b0d-3171-3599-d5dd-a7a74e0088db.png">

お、いけてますね。

<img width="404" alt="server_info.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223758/6f096c12-04e4-37b6-25d2-ae1f5d49431b.png">

openssl も 1.1.1X が使われていることがわかります。

# まとめ

やってみたら意外と簡単でした。

楽しそうだから、という理由でやったので効果はあまり考えてないのですが、[TLS1.3 では接続速度が改善された](https://qiita.com/il-m-yamagishi/items/54c20ae7579959a6e67d)そうです。あまり興味がないのでパフォーマンス検証はしていないのですが。

openssl のバージョンに関係なく、暗号化方式にRSAではなくECDSAを使うと[接続速度が速くなります](https://http2.try-and-test.net/ecdsa.html)が、OpenSSL 1.1.0 以降だとECDSAを含む[楕円曲線暗号の処理が速くなった](https://http2.try-and-test.net/openssl1_1_0_ciphers.html#benchmark)そうなので、必然的に 1.1.1 以降を使う TLS1.3 ではより効果が期待できそうです。

TLS1.3 を普通に使える日がはやくくるといいですね。