Let's Encrypt の証明書が更新されない時の調査記録（renew に失敗編）

# はじめに
いまさら言うまでもなく、証明書の発行といえば Let's encrypt が便利ですね。
公式ドキュメントにしたがえば、たったの5ステップで導入できますし、自動更新の設定までいれてくれるのがありがたいです。

## 環境
* Ubuntu 18.04
* certbot 0.31.0

# Let's encrypt の自動更新が停まっていた
当協会でもいくつかのサービスは Let's encrypt で運用していて、これまで特に問題を感じたことはなかったのですが、
先日、期限20日前を告げるアラートメールが飛んできました。

## 期限間近のアラートメール
こんなメールが送られてきます。

> Hello,
> 
> Your certificate (or certificates) for the names listed below will expire in 20 days (on 04 Oct 19 20:34 +0000).  Please make sure to renew your certificate before then, or visitors to your website will encounter errors.
> 
> We recommend renewing certificates automatically when they have a third of their
> total lifetime left. For Let's Encrypt's current 90-day certificates, that means
> renewing 30 days before expiration. See
> https://letsencrypt.org/docs/integration-guide/ for details.
> 
> example.piano.or.jp

※example.piano.or.jp には、実際のドメインが指定されます。

# 調査したこと
結論を急ぐ方のために、今回の原因を先に記載します。（誰かの参考になれば幸いです）

## 結論（nginx master プロセスの消失）
あくまでも「今回の場合は」ということになりますが、nginx の master プロセスが消えていたことが原因でした。

```shell
hoge@example.com:$ ps aux | grep nginx
user   25851  0.0  0.0  13136   960 pts/2    S+   11:16   0:00 grep --color=auto nginx
nginx  27047  0.3  0.3 145452  7184 ?        S    Jul07 355:20 nginx: worker process
nginx  27048  0.0  0.2 144416  5644 ?        S    Jul07  15:01 nginx: worker process
nginx  27049  0.0  0.2 144000  5236 ?        S    Jul07   0:44 nginx: worker process
```

nginx の masterプロセスがなければ、`certbot renew` は失敗しますので、nginx を再起動させてあげてれば解決です。

※master プロセスが消失する原因については別途記載します。

### nginx master プロセスがない場合の復旧の仕方
ただし、nginx の masterプロセスがいない場合、`nginx -s restart` では再起動してくれません。

restart は 内部的に kill とstart をしてくれている（たぶん）ので、
`/run/nginx.pid` にプロセスIDが入っていない、または入っていても該当するプロセスがいない状態では当然ですね。

nginx workerプロセスをすべて停止してから、再度 nginx を起動してあげる必要があります。
※「罠」というほどでもありませんが、知らないとつまずくポイントかもしれません。

```shell
# nginx worker プロセスのkill. pid は上記のps結果にしたがう
kill 27047
kill 27048
kill 27049
# nginx 起動
sudo nginx
```


# 調査方法
先に結論を書いてしまいましたので、ここからは調査方法を書いてみます。
どうやって調査したらいいのか自信のない方の参考になれば幸いです。

## どこを疑うのか？
前提としては、これまでは何事もなく Let's encrypt の更新が出来ていましたので、今回疑うポイントは２つです。
※はじめて設定していて「うまく更新できない！」という場合は、もっとチェック項目がありますので別記事を探してみてください。

1. 更新用の設定があやまって変更されていないか？
2. renew コマンドは実行できるか？

私のチェックした順に記載しましたが、今思えば、エンドポイントに近い 2番を先に行った方がよかったと思います。

## 1. 更新用の設定をチェック
### cron の設定（/etc/cron.d/certbot）
まずは cron の設定をチェックしましょう。
ubuntu18.04 環境では、certbot 導入時に /etc/cron.d/certbot が作成されています。
といっても、誰かが勝手に変更するような設定でもないので、念のための確認というくらいでしょうか。


```bash 
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
```


### cron の実行履歴（/var/log/cron.log）
cron の設定自体が問題なさそうであれば、cron の実行ログを見て見ましょう。
デフォルトだと /var/log/syslog か /var/log/cron.log に出力されている筈[^1]です。

```bash
Sep 16 00:00:01 123-45-123-456 CRON[19431]: (root) CMD ([19432] test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew)
Sep 16 00:00:01 123-45-123-456 CRON[19431]: (CRON) error (grandchild #19432 failed with exit status 1)
Sep 16 00:00:01 123-45-123-456 CRON[19431]: (root) END ([19432] test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew)
```

今回の場合は、下記のようにエラーが出ていました。
` error (grandchild #19432 failed with exit status 1) `

cron は走っているが、エラーで失敗していることが分かりました。
ただ、具体的なエラーは分からないので、renew の実行結果をテストしてみます。

## renewのテスト実行（certbot --dry-run renew）
`--dry-run` をつけることでテスト実行が出来ます。


```bash
# 実行結果（一部抜粋）
hoge@example.com:~$ sudo certbot --dry-run renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator nginx, Installer nginx
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for example.com
nginx: [error] invalid PID number "" in "/run/nginx.pid"
Cleaning up challenges
nginx: [error] invalid PID number "" in "/run/nginx.pid"
Encountered exception during recovery: 

1 renew failure(s), 0 parse failure(s)
hoge@example.com:~$ 
```

これで、具体的なエラーが確認できました。
` nginx: [error] invalid PID number "" in "/run/nginx.pid" `

/run/nginx.pid に記載されているPIDが不正、ということでした。
しかも、`""` ということなので、空になっています。

経験的に master プロセスが落ちていることが予測されるのですが、psを取ってみて確認します。

```shell
hoge@example.com:$ ps aux | grep nginx
user   25851  0.0  0.0  13136   960 pts/2    S+   11:16   0:00 grep --color=auto nginx
nginx  27047  0.3  0.3 145452  7184 ?        S    Jul07 355:20 nginx: worker process
nginx  27048  0.0  0.2 144416  5644 ?        S    Jul07  15:01 nginx: worker process
nginx  27049  0.0  0.2 144000  5236 ?        S    Jul07   0:44 nginx: worker process
```

worker と書かれたプロセスしかありませんので、nginx の master プロセスが落ちていることが分かりました。
ということは、nginx を再起動すれば pid がないことによるエラーは解消しそうです。
※master プロセスが無い場合の再起動は少し注意が必要です。[nginx master プロセスがない場合の復旧の仕方]() の手順を参考にしてください。

## renewの再テスト（certbot --dry-run renew）
もう一度 renew の dry-run を実行してみて、エラーが出なければ certbot renew 自体のエラーは解消です。

```shell

hoge@example.com:~$ sudo certbot --dry-run renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator nginx, Installer nginx
Renewing an existing certificate

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed with reload of nginx server; fullchain is
/etc/letsencrypt/live/example.com/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```
``Congratulations, all renewals succeeded.`` 
やりました。解消しました。



# おまけ（Let's encrypt の補足）

## ubuntu への導入（5ステップ）
たったこれだけで、自動更新の設定までしてくれます。

1. ssh ログイン
2. apt にリポジトリ追加
3. apt で certbot インストール
4. `sudo certbot --nginx`
5. あとはダイアログ形式で聞かれたことを入力するだけ  

ubuntu 以外の環境などは、公式ドキュメントをご参照ください。
⇒ [公式ドキュメント](https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx)

## Let's encrypt を採択できない場合もある
たんに利用状況の問題ですが。

決済を伴うサービスで、決済方法によっては Let's encrypt では賄えない場合もあります。
例えば、GMO-PG を利用する場合 Digicert か GlobalSign でないと決済サービスを利用できません。
（→ [よくあるご質問](https://faq.gmo-pg.com/service/detail.aspx?id=1231&a=102&isCrawler=1) ）

連携先サービスの都合により制限を受ける場合もあるので、商用サービスの場合には事前の確認が必要です。


[^1]: 出力先は、/etc/rsyslog.conf か /etc/rsyslog.d/50-default.conf に設定があります。