Let's Encrypt で webroot 利用時に更新(renew)がこけた時の対処方法

Let's Encrypt で webroot を利用した際、証明書の更新(renew)が上手くいかないケースに遭遇したのですが、ググっても日本語の情報が皆無でしたので、久しぶりの投稿です。

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

## webroot を利用した証明書の発行

もはや何番煎じか分かりませんが、古い情報が錯綜している感じがするので、念の為書いておきます。

```bash
#-- certbot-auto最新版のインストール
$ cd /usr/bin/
$ wget https://dl.eff.org/certbot-auto
$ chmod 700 ./certbot-auto

#-- certbot-autoのバージョン(2019年4月1日時点：0.32.0)
#-- certbot-autoを動作させるのに必要なPython等がインストールされていなかった場合、
#-- このタイミングで自動的にインストールされるようになっています。
$ certbot-auto --version

#-- 証明書の発行
$ certbot-auto certonly \
 --agree-tos \
 --webroot \
 -w /path/to/document/root \
 -d example.com \
 -d www.example.com \
 -m info@example.com --no-eff-email

#-- 発行した証明書を削除して綺麗にしたい時は
# $ certbot-auto delete -d example.com

#-- .pemファイルができている事を確認
$ ll /etc/letsencrypt/live/example.com/
```

各ファイルの意味は…

1. cert.pem：証明書
2. chain.pem：中間証明書
3. fullchain.pem：上記 1 と 2 とを連結したファイル
4. privkey.pem：秘密鍵

です。

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

## Apache の VirtualHost 設定

せっかくですので、こちらもついでに書いておきます。

```apache
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /path/to/document/root

    #-- HTTPSへの自動リダイレクト(NE = No Escape)
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
</VirtualHost>

<Virtualhost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /path/to/document/root

    SSLEngine On

    #-- Apache2.4.8以降は、fullchain.pemを指定すれば2行で書けます
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
```

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

## 証明書が更新(renew)できるか試してみる(dry run)

Let's Encrypt の証明書の有効期間は 90 日(3 ヶ月) なので、cron を利用して自動更新させるのが楽です。

webroot を利用している場合、`certbot-auto renew` とするだけの簡単作業…の筈なのですが…

```bash
#-- とりあえず dry run してみる
$ certbot-auto renew --dry-run
```

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

## 更新(renew)でエラーが発生した時の対処方法

dry run がすんなり通れば問題ないのですが、私の環境では、以下のようなエラーが発生する場合がありました。**やっとこの記事の本題です。**

※Google 等から検索してくる人のために、エラー文を長めにコピペしておきます。

```
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: Missing command line flag or config entry for this setting:
Select the webroot for example.com:
Choices: ['Enter a new webroot', '/path/to/document/root']

(You can set this with the --webroot-path flag). Skipping.
All renewal attempts failed. The following certs could not be renewed:
```

あるいは

```
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: Missing command line flag or config entry for this setting:
Input the webroot for example.com:. Skipping.
All renewal attempts failed. The following certs could not be renewed:
```

このエラーには再現性がなく、全く同じ環境なのにドメインによってエラーが出なかったり、同じドメインなのに delete してから再発行するとエラーが出るようになったり…といった状況でした。

エラー内容に従い、renew 時に読まれる設定ファイル `/etc/letsencrypt/renewal/example.com.conf` の中身を確認してみると、エラーが発生する際はなぜか…

```
[[webroot_map]]
```

…と、`[[webroot_map]]` エントリーが空になっていたり、あるいは…

```
[[webroot_map]]
example.com = /path/to/document/root
```

…と、「www付き」の方の設定が欠けていました。この場合…

```
[[webroot_map]]
example.com = /path/to/document/root
www.example.com = /path/to/document/root
```

…のように修正してやれば、正常に動作するようになります。

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

## 原因は不明

上記のような作業をしなくとも、`[[webroot_map]]` エントリーが正しく記述される場合もあります。なぜ空になる事があるのか、原因は不明です。

もし原因をご存知の方がいらっしゃいましたら、コメントで教えて頂ければ幸いです。

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

## 証明書の自動更新を設定する

dry run が上手くいけば

```bash
certbot-auto renew --post-hook "Apache を再起動させるコマンド"
```

を cron で定期的に実行するよう設定してやれば OK です。