ACMEプロトコルをv1からv2にアップデートした

認証局の **Let's Encrypt** から以下のようなメールが届くようになりました。

```
Update your client software to continue using Let's Encrypt

... （一部省略） ...

On June 1, 2021, you will no longer be able to renew your certificates using
ACMEv1. You should upgrade to an ACMEv2 compatible client before June 1, or
certificate issuance will fail. Until then, we will also have occasional
ACMEv1 issuance and renewal brown-outs each month:
```

2021年6月1日をもって**ACMEv1**で証明書を更新することはできなくなるので、6月1日までに**ACMEv2**互換クライアントにアップグレードしろとのことです。可能性のあるサーバを総点検しました。

アナウンスはかなり前からあったので殆どの人は対応済みと思いますが、一応アップデート手順を残しておきます。なお、本稿で説明するACMEクライアントは**certbot**で、WebサーバはApacheを使っています。

# 現状確認
## APIエンドポイントの確認
現在運用中の[APIエンドポイント](https://letsencrypt.org/ja/docs/acme-protocol-updates/)は、アカウント情報が保存されたディレクトリで確認できます。

```
$ ll /etc/letsencrypt/accounts/
drwx------ 3 root root 23 Feb 15  2018 acme-staging-v01.api.letsencrypt.org
drwx------ 3 root root 23 May 21  2018 acme-v01.api.letsencrypt.org
```

ここに`acme-v02.api`があれば、すでにACMEv2を使っていると判断できます。

## ログの確認
証明書の更新を実際にリクエストしているAPIエンドポイントをログから確認します。

```
$ grep GET /var/log/letsencrypt/letsencrypt.log
2021-04-23 03:02:04,286:DEBUG:acme.client:Sending GET request to https://acme-v01.api.letsencrypt.org/directory.
2021-04-23 03:02:05,015:DEBUG:requests.packages.urllib3.connectionpool:"GET /directory HTTP/1.1" 200 658
2021-04-23 03:02:12,456:DEBUG:acme.client:Sending GET request to https://acme-v01.api.letsencrypt.org/acme/authz-v3/10251753909.
2021-04-23 03:02:12,607:DEBUG:requests.packages.urllib3.connectionpool:"GET /acme/authz-v3/10251753909 HTTP/1.1" 200 772
2021-04-23 03:02:13,841:DEBUG:acme.client:Sending GET request to https://acme-v01.api.letsencrypt.org/acme/issuer-cert.
2021-04-23 03:02:13,991:DEBUG:requests.packages.urllib3.connectionpool:"GET /acme/issuer-cert HTTP/1.1" 200 1129
```

ここに`acme-v02.api`があれば、すでにACMEv2で実績があるということになるので、アップデート不要です。
ログが無ければ、`certbot renew --dry-run`で、認証局のステージングサーバにリクエストを投げてみることです。詳細なログを残せます。**ドライランモード**では証明書を更新しないので何度でも試行できます。

## certbotのバージョン確認
certbotクライアントをバージョンアップする前に、現在のバージョンを確認しておきましょう。

```
$ certbot --version
certbot 0.23.0
```

# パターン別アップデート手順
サーバ環境ごとに対応が違ったのでパターン別に説明します。

## certbotプラグインのアップデートも必要なパターン
CentOS7でcertbotインストール時に、Apacheと連携するプラグイン`python2-certbot-apache`を入れていたパターンです。

```:環境
CertbotACMEClient/0.23.0 (certbot; CentOS Linux 7 (Core)) Authenticator/apache Installer/apache (renew; flags: n) Py/2.7.5

$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
```

```:アップデート
# yum -y update certbot
```

バージョンを確認するとエラーになりました。

```:エラー
$ certbot --version
An unexpected error occurred:
AttributeError: 'module' object has no attribute 'TLSSNI01'
Please see the logfile '/tmp/tmpp6BH7s/log' for more details.
```

certbotプラグインが古いのが原因なので、これもアップデートします。

```
yum -y update python2-certbot-apache
```

```
$ certbot --version
certbot 1.11.0
```

ドライランモードでシミュレーション実行し、ログを確認します。

```
certbot renew --dry-run
```

```:ログ
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
```

`v02.api`が現れました。

```
$ ll /etc/letsencrypt/accounts/
drwx------ 3 root root 23 Feb 15  2018 acme-staging-v01.api.letsencrypt.org
drwx------ 3 root root 23 May 21  2018 acme-v01.api.letsencrypt.org
drwx------ 3 root root 23 Apr  3 14:30 acme-staging-v02.api.letsencrypt.org
```

## Software Collections で動作するパターン
CentOS6標準のPython2.6ではcertbotが動かないので、Software Collections(SCL)環境下のPython2.7でcertbotを手動インストールしていたパターンです。

```
$ cat /etc/redhat-release 
CentOS release 6.10 (Final)

$ python -V
Python 2.6.6
```

SCLを有効にします。

```
$ scl enable python27 bash
$ python -V
Python 2.7.17
```

SCL環境下でcertbot-autoのバージョンを確認します。

```
$ certbot-auto --version
Your system is not supported by certbot-auto anymore.
Certbot will no longer receive updates.
Please visit https://certbot.eff.org/ to check for other alternatives.
certbot 1.10.1
```

ログを確認したところ、すでにACMEv2でリクエストしているサーバでした。対応不要です。

```
$ certbot-auto renew --dry-run --debug

$ grep GET /var/log/letsencrypt/letsencrypt.log
2021-01-26 21:05:46,981:DEBUG:acme.client:Sending GET request to https://acme-staging-v02.api.letsencrypt.org/directory.
2021-01-26 21:05:47,518:DEBUG:urllib3.connectionpool:https://acme-staging-v02.api.letsencrypt.org:443 "GET /directory HTTP/1.1" 200 724
2021-01-26 21:06:05,584:DEBUG:acme.client:Sending GET request to https://acme-staging-v02.api.letsencrypt.org/directory.
2021-01-26 21:06:06,111:DEBUG:urllib3.connectionpool:https://acme-staging-v02.api.letsencrypt.org:443 "GET /directory HTTP/1.1" 200 724
```

## 標準的なパターン
Ubuntu18.04／Python3.6 という比較的新しい環境のパターンです。

```
CertbotACMEClient/0.23.0 (certbot; Ubuntu 18.04.3 LTS) Authenticator/webroot Installer/None (renew; flags: n) Py/3.6.9
```

まずパッケージ一覧を更新し、certbotをインストールし直します。

```
apt -y update
apt -y install certbot
```

```
$ certbot --version
certbot 0.27.0
```

アップデートされました。
ここで試しに`certbot renew --dry-run --debug`するとエラーを確認できます。
どうやら環境定義ファイルの修正が必要なようです。

```ini:/etc/letsencrypt/renewal/example.com.conf
version = 0.23.0
archive_dir = /etc/letsencrypt/archive/example.com
cert = /etc/letsencrypt/live/example.com/cert.pem
privkey = /etc/letsencrypt/live/example.com/privkey.pem
chain = /etc/letsencrypt/live/example.com/chain.pem
fullchain = /etc/letsencrypt/live/example.com/fullchain.pem

# Options used in the renewal process
[renewalparams]
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
authenticator = webroot
[[webroot_map]]
example.com = /var/www/html
```

となっているのを、次のように書き替えます。

```ini:/etc/letsencrypt/renewal/example.com.conf
version = 0.27.0                                         <===== 修正
archive_dir = /etc/letsencrypt/archive/example.com
cert = /etc/letsencrypt/live/example.com/cert.pem
privkey = /etc/letsencrypt/live/example.com/privkey.pem
chain = /etc/letsencrypt/live/example.com/chain.pem
fullchain = /etc/letsencrypt/live/example.com/fullchain.pem

# Options used in the renewal process
[renewalparams]
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
authenticator = webroot
server = https://acme-v02.api.letsencrypt.org/directory  <===== 追加
[[webroot_map]]
example.com = /var/www/html
```

再度、`certbot renew --dry-run --debug`すると成功。

忘れた頃に更新失敗に気づくのも嫌なので念の為、`certbot renew --force-renew`で有効期限を無視して強制更新し、Apacheを再起動、ブラウザから証明書の有効期限が延長されたことを確認しておくと安心です。