Let's Encryptの更新処理をCertbotに移行する（ついでに自動更新もさせる

# はじめに
Let's Encryptの証明書は便利ですが、90日毎に更新処理するのがちょっと手間です。更新処理を自動化して手間を掛けずにSSL証明書を利用します。

# 背景と目的
## 背景
2年くらい前からLet's Encryptを使っており、当時配布されていた専用のスクリプトで更新していました。
当時はスクリプトがLet's Encryptと通信することで証明書が払い出される仕組みだったと記憶しています。（大雑把）
その時、スクリプトがHTTPS（TCP/443）を使って通信を待ち受ける必要があったため、Apacheを一度停止して実行していました。
個人用のWebDavサーバでしたが、Apache止めたくないなとの想いがあり、更新を自動化していませんでした。

## 目的
1.　Let's Encrypt設定用のクライアントのCertbotが出たと知ったので、そちらに移行してみます。
https://certbot.eff.org/

2.　CertbotのApache Pluginを使うとうちの環境は更新処理の自動化ができそうな感じがあるのでやってみます。

# 手順
1.　Certbotのインストール
CentOS向けにはEPELのパッケージを利用しているようなので、EPELリポジトリを有効化してあげます。その後、CertbotのパッケージをインストールすればOKです。簡単。

```centos:Certbotのインストール
# yum install epel-release
# yum install certbot-apache
```

2.　既存スクリプトからCertbotへ
初回はCertbotのApache Plugin指定して実行します。
Let's Encryptを利用している環境であれば、既存の証明書設定を読み込んでくれますので、ガイドに沿って設定を見ていけば大丈夫です。簡単。

さらにLet's Encrypt用のhttpdの設定も自動でやってくれます。これでhttpdを止めずに更新処理ができるはずです。

```certbot:ドメイン設定
# certbot --apache

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator apache, Installer apache
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: xxxxxxxx.xxxx.jp
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1
Attempting to parse the version 0.22.2 renewal configuration file found at /etc/letsencrypt/renewal/xxxxxxxxx.xxxx.jp.conf with version 0.22.0 of Certbot. This might not work.
Cert not yet due for renewal

You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /etc/letsencrypt/renewal/xxxxxxxx.xxxx.jp.conf)
```

証明書を再度インストールするか更新するか選びます。使っている環境なので更新で…

```certbot:証明書の作成
What would you like to do?
-------------------------------------------------------------------------------
1: Attempt to reinstall this existing certificate
2: Renew & replace the cert (limit ~5 per 7 days)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for xxxxxxxx.xxxx.jp
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/httpd/conf.d/ssl.conf
```

HTTPリクエストをHTTPSへリダイレクトするか聞かれます。うちの環境はTCP/80は空けてないのでリダイレクト無しを指定しました。ここまでで証明書の更新も含めて設定完了です。
まだ、試していませんが、初めて利用する場合にも同様の手順で使えるようになりそうです。
そこはおいおいサーバを置き換えた時にでも…

```certbot:リダイレクト設定
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

-------------------------------------------------------------------------------
Your existing certificate has been successfully renewed, and the new certificate
has been installed.

The new certificate covers the following domains: https://xxxxxxxx.xxxx.jp

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=xxxxxxxx.xxxx.jp
-------------------------------------------------------------------------------
```

3.　CertbotのDry Run
Certbotを使って証明書の更新処理だけをできるか確かめていこうとしたら、トラブルが発生しました。
うちの環境特有なのかCertbotのDry Runがうまく動きません…
エラーメッセージで調べても、Dry Runではなく別の事象っぽい感じ…

```certbot:dry-runのエラー
# certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/xxxxxxxx.xxxx.jp.conf
-------------------------------------------------------------------------------
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator apache, Installer apache
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
`Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA.`
Attempting to renew cert (xxxxxxxx.xxxx.jp) from /etc/letsencrypt/renewal/xxxxxxxx.xxxx.jp.conf produced an unexpected error: Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA.. Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/xxxxxxxx.xxxxx.jp/fullchain.pem (failure)

```

設定作業時の更新はうまく行っているから、サポートしていないとは考えづらいので、試しに`--dry-run`を外して実行してみます。

```certbot:更新処理
# certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/xxxxxxxx.xxxx.jp.conf
-------------------------------------------------------------------------------
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator apache, Installer apache
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for xxxxxxxx.xxxx.jp
Waiting for verification...
Cleaning up challenges

-------------------------------------------------------------------------------
new certificate deployed with reload of apache server; fullchain is
/etc/letsencrypt/live/xxxxxxxx.xxxx.jp/fullchain.pem
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/xxxxxxxx.xxxx.jp/fullchain.pem (success)
-------------------------------------------------------------------------------
```

何事もなく通ったよ…
とりあえずは`--dry-run`になにか不具合でもあるんかなって考えて放っておきましょう…
アップデートして直ったら記事も修正しましょう…

4.　そして自動化へ
更新の自動化はおなじみCrontabでやります。
本当はDry Runでテストしてからリリースしたかったんですが、一発本番やりましょう…

```certbot:/etc/cron.daily/cert_renew
#! /bin/sh
certbot renew
```

ただし、このままだとlet's Encryptの初期設定に引っかかり、Expireが近くないとみなされます。
初期だと30日前から更新が可能ですが、ここの制限を取っ払って強制的に更新を掛けてテストします。
以下のように設定したドメイン名の設定ファイルがあります。

```certbot:/etc/letsencrypt/renewal/xxxxxxxx.xxxx.jp.conf（編集前）
# renew_before_expiry = 30 days
cert = /etc/letsencrypt/live/xxxxxxxxx.xxxx.jp/cert.pem
privkey = /etc/letsencrypt/live/xxxxxxxxx.xxxx.jp/privkey.pem
chain = /etc/letsencrypt/live/xxxxxxxxx.xxxx.jp/chain.pem
fullchain = /etc/letsencrypt/live/xxxxxxxxx.xxxx.jp/fullchain.pem
version = 0.22.2
archive_dir = /etc/letsencrypt/archive/xxxxxxxxx.xxxx.jp

# Options and defaults used in the renewal process
[renewalparams]
installer = apache
authenticator = apache
account = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
pref_challs = tls-sni-01,
```

ここの`renew_before_expiry`の行の下に`renew_before_expiry = 90 days`を追記します。
これでExpireの90日前から更新できるようになります。
なお、更新テストが終わったら、こちらの行は削除しておきましょう。
自分はいくつかテストしていたら、一週間に5回までの制限に引っかかり、更新処理が出来なくなりました。（記事の公開も遅れました）

ちなみにCertbotのサイトでは更新処理失敗時の対策の為、日に2回回したほうがいいよと説明があります。
おそらく負荷分散の為、ランダムなSleepを入れて実行時間をずらしているようです。

```certbot:cron設定例
0 0,12 * * * python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew 
```

引用元：https://certbot.eff.org/lets-encrypt/centosrhel7-apache

# まとめ/さいごに
ちょっとしたトラブルがありましたが、Let's Encryptの証明書更新処理を自動化できました。
単一サーバの証明書であれば、この設定で十分でしょう。たぶん。
商用で冗長汲んでいる場合には、やり方を別途検討する必要があります。
そちらはまた別途、実践したら記事を書いてみます。