CentOS7 + RPM版 Apache HTTPD 2.4.6 で Let's Encrypt 導入メモ : certbot, 2017年1月時点

2017年1月時点で、CentOS7とそのRPM版 Apache HTTPD 2.4.6 にて Let's Encryptを導入してみたので、そのメモです。

* https://letsencrypt.org/
* https://letsencrypt.jp/

検証環境：

```
$ cat /etc/centos-release
CentOS Linux release 7.3.1611 (Core)

関連RPM : 
httpd-2.4.6-45.el7.centos.x86_64
openssl-1.0.1e-60.el7.x86_64
openssl-libs-1.0.1e-60.el7.x86_64
mod_ssl-2.4.6-45.el7.centos.x86_64
```

2015～2016年にかけての記事では https://github.com/letsencrypt/letsencrypt を使う手法が紹介されていました。
しかし、2016年後半くらいから Certbot を使う記事が増えてます。

今回参考にさせて頂いた Certbot + CentOS7 + Apache の記事：

* CentOS 7 で Let's Encrypt - ライ麦畑をさがして
  * http://dacci.hatenablog.com/entry/2016/05/01/CentOS_7_%E3%81%A7_Let's_Encrypt
* SE奮闘記: Let's Encryptで無料SSLをCentOS7に設定（certbot）
  * http://blog.dksg.jp/2016/08/lets-encryptsslcentos7certbot.html
* CentOS7でLet's Encryptを使う - Qiita
  * http://qiita.com/yasushi00/items/40bdaf8f05017e212325

実際、2017年1月時点で https://github.com/letsencrypt/letsencrypt にアクセスすると、Certbot の GitHub にリダイレクトされてしまいます。

ということで、2017年1月時点ではCertbotを使う方法で挑戦してみました。

* https://certbot.eff.org/
* https://github.com/certbot/certbot

Certbotにはいくつかプラグインが用意されており、ApacheやNginxであれば設定ファイルについてもある程度自動調整してくれるようです。
今回は勉強のため、稼働中のApacheとその設定ファイルは可能な限り触らない方向で試してみました。

# インストール

https://certbot.eff.org/#centosrhel7-apache に従い、EPELリポジトリを追加した後、以下実行すればインストール完了です。

```
$ sudo yum install python-certbot-apache
(...)
Installing:
 python2-certbot-apache                              noarch                 0.9.3-1.el7                          epel                 135 k
Installing for dependencies:
 certbot                                             noarch                 0.9.3-1.el7                          epel                  16 k
(...)
```

CentOS7で、かつApacheを選んだためか、インストールするよう指示されたパッケージは `python-certbot-apache` でした。 `certbot` パッケージ自体は `python-certbot-apache` の依存パッケージとしてインストールされました。

パッケージ内容を確認してみます。

```
$ rpm -qil --scripts python2-certbot-apache
Name        : python2-certbot-apache
Version     : 0.9.3
Release     : 1.el7
Architecture: noarch
Install Date: Sat 14 Jan 2017 12:42:22 PM JST
Group       : Unspecified
Size        : 532527
License     : ASL 2.0
Signature   : RSA/SHA256, Sat 15 Oct 2016 01:26:45 AM JST, Key ID 6a2faea2352c64e5
Source RPM  : python-certbot-apache-0.9.3-1.el7.src.rpm
Build Date  : Sat 15 Oct 2016 01:24:13 AM JST
Build Host  : buildhw-10.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager    : Fedora Project
Vendor      : Fedora Project
URL         : https://pypi.python.org/pypi/certbot-apache
Summary     : The apache plugin for certbot
Description :
Plugin for certbot that allows for automatic configuration of apache
/usr/lib/python2.7/site-packages/certbot_apache
/usr/lib/python2.7/site-packages/certbot_apache-0.9.3-py2.7.egg-info
/usr/lib/python2.7/site-packages/certbot_apache-0.9.3-py2.7.egg-info/PKG-INFO
(...)
/usr/lib/python2.7/site-packages/certbot_apache/__init__.py
(...)

$ rpm -qil --scripts certbot
Name        : certbot
Version     : 0.9.3
Release     : 1.el7
Architecture: noarch
Install Date: Sat 14 Jan 2017 12:42:22 PM JST
Group       : Unspecified
Size        : 21757
License     : ASL 2.0
Signature   : RSA/SHA256, Sat 15 Oct 2016 01:26:53 AM JST, Key ID 6a2faea2352c64e5
Source RPM  : certbot-0.9.3-1.el7.src.rpm
Build Date  : Sat 15 Oct 2016 12:57:19 AM JST
Build Host  : buildvm-16.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager    : Fedora Project
Vendor      : Fedora Project
URL         : https://pypi.python.org/pypi/certbot
Summary     : A free, automated certificate authority client
Description :
certbot is a free, automated certificate authority that aims
to lower the barriers to entry for encrypting all HTTP traffic on the internet.
/etc/letsencrypt
/usr/bin/certbot
/usr/bin/letsencrypt
/usr/share/doc/certbot-0.9.3
/usr/share/doc/certbot-0.9.3/CHANGES.rst
/usr/share/doc/certbot-0.9.3/CONTRIBUTING.md
/usr/share/doc/certbot-0.9.3/README.rst
/usr/share/licenses/certbot-0.9.3
/usr/share/licenses/certbot-0.9.3/LICENSE.txt
/var/lib/letsencrypt
```

`python2-certbot-apache` は `certbot` に対するApache用プラグインのライブラリとしてパッケージングされており、実行コマンド本体は `certbot` パッケージに含まれていることが分かります。
`/usr/bin/certbot` は Python スクリプトで、 `/usr/bin/letsencrypt` は `/usr/bin/certbot` へのシンボリックリンクになっていました。（恐らく互換性維持のためと思われます）

```
$ file /usr/bin/certbot
/usr/bin/certbot: Python script, ASCII text executable

$ file /usr/bin/letsencrypt
/usr/bin/letsencrypt: symbolic link to `/usr/bin/certbot'
```

なお、設定ファイルはインストールだけでは特に生成されないようです。一応、以下に設定ファイルのサンプルが載っているのですが、今回は一度も触らなかったです・・・。

* https://certbot.eff.org/docs/using.html#configuration-file

# 証明書の取得

今回実験した環境では、すでに他の用途でApacheの設定ファイルをカスタマイズしていました。また、すでにインターネット環境にHTTPポートを開放しており、外部からのアクセスを受け付けている状態です。
そのため、設定ファイルへの副作用とWebサービスのダウンタイムを回避するため、Certbot の Apache プラグインではなく、Webroot プラグインを使うことにしました。

* https://certbot.eff.org/docs/using.html#webroot

Webroot プラグインは公開中のドキュメントルートに `.well-known/` ディレクトリを作成し、そこにドメイン認証用に Let's Encrypt がアクセスしてくるファイルを配置する仕組みです。
これにより、稼働中のApacheを停止すること無く、設定ファイルへの副作用も無く、証明書を取得できます。

まずは `--dry-run` でテストします。`--webroot-path or -w` オプションでインターネットに公開中のDocumentRootを指定し、対応するFQDN名を `-d FQDN名` で続けて指定します。なお、ここでは例として DocumentRoot = /var/www/html, FQDN名 = `www.example.com` と記載してます。

```
$ sudo certbot certonly --dry-run --webroot -w /var/www/html -d www.example.com
```

cursesライブラリを使った対話形式のセットアップが開始され、メールアドレス入力と利用規約への同意を行います。すると、以下のように表示されました。

```
(cursesのUI上)
Generating key (2048 bits): /var/log/letsencrypt/letsencrypt.log
Creating CSR: /etc/letsencrypt/keys/0000_key-certbot.pem

(ターミナル上)
IMPORTANT NOTES:
 - The dry run was successful.
 - If you lose your account credentials, you can recover through
   e-mails sent to (入力したメールアドレス)
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
```

この時点でのファイル構成です。

```
# find /etc/letsencrypt/
/etc/letsencrypt/
/etc/letsencrypt/accounts
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory/(xxx)
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory/(xxx)/regr.json
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory/(xxx)/private_key.json
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory/(xxx)/meta.json
/etc/letsencrypt/renewal
/etc/letsencrypt/keys
/etc/letsencrypt/keys/0000_key-certbot.pem
/etc/letsencrypt/csr
/etc/letsencrypt/csr/0000_csr-certbot.pem

# find /var/log/letsencrypt/
/var/log/letsencrypt/
/var/log/letsencrypt/letsencrypt.log.1
/var/log/letsencrypt/letsencrypt.log

$ find /var/www/html/.well-known/
.well-known/
(DocumentRoot の下に .well-known/ ディレクトリは作られていたが、中身は空。)
```

では、実際に取得してみます。 `--dry-run` オプションを削除し、またcursesのTUIによる確認を避けるため `--non-interactive` オプションを付けて実行します。

```
$ sudo certbot certonly --non-interactive --webroot -w /var/www/html -d www.example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
You should register before running non-interactively, or provide --agree-tos and --email <email_address> flags.

-> 指示のとおりにコマンドラインオプションを追加します。

$ sudo certbot certonly --non-interactive --agree-tos --email (メールアドレス) --webroot -w /var/www/html -d www.example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for (-d で指定したFQDN名)
Using the webroot path (-w で指定したDocumentRoot) for all unmatched domains.
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/(-d で指定したFQDN名)/fullchain.pem. Your
   cert will expire on 2017-04-14. To obtain a new or tweaked version
   of this certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you lose your account credentials, you can recover through
   e-mails sent to (--email で指定したメールアドレス).
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
```

この段階で DocumentRoot 以下の `.well-known/` ディレクトリは空っぽでした。処理が成功したら、ファイルをクリーンアップしてくれるのかも。

証明書の保存先は利用方法は以下のURLで案内されています。

* https://certbot.eff.org/docs/using.html#where-are-my-certificates

この時点での `/etc/letsencrypt` 以下を確認してみます。

```
# find /etc/letsencrypt/
/etc/letsencrypt/
/etc/letsencrypt/accounts

(--dry-run 実行時のディレクトリ)
/etc/letsencrypt/accounts/acme-staging.api.letsencrypt.org/directory/(xxx)/

(以下が実際の取得で作られたファイル)
/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/(yyy)
/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/(yyy)/regr.json
/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/(yyy)/private_key.json
/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/(yyy)/meta.json

/etc/letsencrypt/renewal
/etc/letsencrypt/renewal/(FQDN名)
/etc/letsencrypt/keys
/etc/letsencrypt/keys/0000_key-certbot.pem  (--dry-run 実行時のもの)
/etc/letsencrypt/keys/0001_key-certbot.pem
/etc/letsencrypt/csr
/etc/letsencrypt/csr/0000_csr-certbot.pem  (--dry-run 実行時のもの)
/etc/letsencrypt/csr/0001_csr-certbot.pem
/etc/letsencrypt/archive
/etc/letsencrypt/archive/(FQDN名)
/etc/letsencrypt/archive/(FQDN名)/cert1.pem
/etc/letsencrypt/archive/(FQDN名)/privkey1.pem
/etc/letsencrypt/archive/(FQDN名)/chain1.pem
/etc/letsencrypt/archive/(FQDN名)/fullchain1.pem
/etc/letsencrypt/live
/etc/letsencrypt/live/(FQDN名)
/etc/letsencrypt/live/(FQDN名)/cert.pem
/etc/letsencrypt/live/(FQDN名)/privkey.pem
/etc/letsencrypt/live/(FQDN名)/chain.pem
/etc/letsencrypt/live/(FQDN名)/fullchain.pem
```

なお `/etc/letsencrypt/live/(FQDN名)` 以下のファイルは、 `/etc/letsencrypt/archive/(FQDN名)` へのsymlink となっていました。

この段階で `/etc/letsencrypt` 以下をバックアップしておきました。

# Apache HTTPD 2.4.6 の設定

今回検証した CentOS7 の Apache HTTPD はバージョン 2.4.6 でした。そのため、 fullchain.pem (Apache >= 2.4.8) ではなく、 chani.pem + cert.pem (Apache < 2.4.8) の組み合わせを使います。

本記事では鍵ファイルと証明書, 証明書チェインのディレクティブのみ以下に紹介します。

```
SSLCertificateKeyFile   /etc/letsencrypt/live/(FQDN名)/privkey.pem
SSLCertificateFile      /etc/letsencrypt/live/(FQDN名)/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/(FQDN名)/chain.pem
```

CentOS7でのfirewalldの穴あけ：

```
$ sudo firewall-cmd --add-service=https --zone=public --permanent
$ sudo firewall-cmd --reload
```

# 証明書の更新

`certbot renew` で、取得した証明書全てについて有効期限をチェック & 有効期限まで30日未満なら更新してくれるようです。
特にオプション指定無しで、最後に取得したときのplugin & optionを使ってくれるようです。

* https://certbot.eff.org/docs/using.html#renewing-certificates

毎日cronなどで実行してもいいですし、30日未満で更新してくれますので、毎週位まで頻度を落としても平気かもしれません。

CentOS7になり、systemdのtimer方式で `certbot renew` を毎週行う設定が以下に紹介されています。

* http://dacci.hatenablog.com/entry/2016/05/01/CentOS_7_%E3%81%A7_Let's_Encrypt

上記URLからの引用です(強調・省略は本記事の著者による)：

```/etc/systemd/system/certbot.service
[Unit]
Description=Let's Encrypt certificate renewal

[Service]
Type=oneshot
ExecStart=/bin/certbot renew
ExecStartPost=/bin/systemctl reload httpd.service
```

```/etc/systemd/system/certbot.timer
[Unit]
Description=Let's Encrypt weekly certificate renewal

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target
```

```
# systemctl enable certbot.timer
# systemctl --type=timer --all
UNIT                         LOAD   ACTIVE   SUB     DESCRIPTION
certbot.timer                loaded inactive dead    Let's Encrypt monthly certificate renewal
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

```

以上で CentOS7 + RPM版 Apache HTTPD 2.4.6 に Let's Encrypt 導入できました！