Let's Encryptの証明書更新モードをStandaloneからWebrootに変更する

はじめに

先日、仕事で作成させていただいたWebサイトがSSL証明書の有効期限切れによりアクセス不可になっていました。

SSL証明書にはLet's Encryptを使用しており、cronに証明書更新コマンドを仕込んで自動更新されるようにしていたはずなのに...

実は、Let's Encryptには証明書更新モードというのがあり、そのモードによって自動更新に失敗するというケースがあります。

今回、改めてLet's Encryptについて勉強し直し、ようやく証明書更新モードについて理解することができたので、備忘の意味も含めて記事にしたいと思います。

証明書更新モードについて

まず、Let's Encryptの証明書モードとは何なのか、どんなモードがありどのような意味なのかを説明します。

Let's Encryptの証明書モードには以下の2種類があります。

  • Standalone
  • Webroot

Standalone

SSL証明書を発行または更新する際、Let's Encryptに含まれるWebサーバーの機能を使って証明書を発行または更新するモードです。

Let's EncryptのWebサーバーがポート80をバインドするため、自前で用意しているWebサーバーが稼働中だと以下のエラーが出て実行に失敗します。

Attempting to renew cert (www.sample.com) from /etc/letsencrypt/renewal/www.sample.com.conf produced an unexpected error: Problem binding to port 80: Could not bind to IPv4 or IPv6.. Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/www.sample.com/fullchain.pem (failure)

StandaloneモードでSSL証明書の発行または更新をする場合、事前に自前のWebサーバーを停止させておく必要があります。

Webroot

SSL証明書を発行または更新する際、対象ドメインの指定されたドキュメントルートに.well-knownディレクトリを作成することでドメインの有効性を確認するモードです。

ドキュメントルートが指定されていない、指定されたディレクトリがアクセス不可などの場合にエラーが出て実行に失敗します。

証明書更新モードの設定

現在の証明書更新モードは以下のファイルに記載されています。

$ cat /etc/letsencrypt/renewal/www.sample.com.conf

# Standaloneの場合
...
# Options used in the renewal process
[renewalparams]
authenticator = standalone
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
server = https://acme-v02.api.letsencrypt.org/directory

# Webrootの場合
...
# Options used in the renewal process
[renewalparams]
authenticator = webroot
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
server = https://acme-v02.api.letsencrypt.org/directory
webroot_path = /var/www/sample/current/public,
[[webroot_map]]
www.sample.com = /var/www/sample/current/public

証明書更新モードの変更

今回、SSL証明書の更新に失敗していたのは、証明書更新モードがStandaloneになっていたためです。Webサーバーは常時稼働しているので、cronによりSSL証明書の自動更新コマンドが実行されるときにも当然稼働しています。そうすると先述の通り、ポート80がバインドできないためエラーとなってしまいます。そこで、証明書更新モードを変更することにしました。

最初にcertbot-auto certonlyコマンドでSSL証明書をStandaloneで発行した場合、現在のモードはStandaloneになっているはずです。すでに発行済みの証明書の証明書更新モードを変更したい場合、もう一度certbot-auto certonlyコマンドを実行すれば変更できます。このとき、--webroot -w /path/to/document/rootオプションを付ける必要があります。

$ ./certbot-auto certonly --webroot -w /var/www/sample/current/public -d www.sample.com

Requesting to rerun ./certbot-auto with root privileges...
./certbot-auto has insecure permissions!
To learn how to fix them, visit https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None
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/www.sample.com.conf)

What would you like to do?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Keep the existing certificate for now
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

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/www.autovice.jp/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/www.autovice.jp/privkey.pem
   Your cert will expire on 2020-06-15. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - 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/donat

同じドメインですでにStandaloneで証明書を発行済みなので、証明書発行を続けるか聞かれます。

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/www.sample.com.conf)

What would you like to do?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Keep the existing certificate for now
2: Renew & replace the cert (limit ~5 per 7 days)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

更新した証明書で置き換えるので2を入力します。1またはcを入力するとキャンセルされます。

実行後、証明書更新モードがWebrootに変更されていればOKです。

$ cat /etc/letsencrypt/renewal/www.sample.com.conf

...
# Options used in the renewal process
[renewalparams]
authenticator = webroot
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
server = https://acme-v02.api.letsencrypt.org/directory
webroot_path = /var/www/sample/current/public,
[[webroot_map]]

おまけ

設定ファイルを手動更新

実は、certbot-auto certonlyコマンドを再実行しなくても、設定ファイルを手動更新しても証明書更新モードの変更はできます。設定ファイルに熟知している場合は手動更新するほうが手っ取り早いかもしれません。

証明書更新モードの指定

実は、証明書の更新時にも証明書更新モードの指定はできます。

$ certbot-auto renew --webroot -w /path/to/document/root

cronに設定する更新コマンドに証明書更新モードを指定すればStandaloneのままでも問題ありません。ただ、コマンドが長くなってしまうので個人的には証明書更新モードを変更しておいたほうが良いかなと思います。

その他のコマンド

  • certbot-auto certificates:SSL証明書の有効期限が確認できます。
  • certbot-auto renew --dry-run:実際に更新はせず、コマンドの有効性だけ確認できます。

まとめ

Let's Encryptの証明書更新モードをStandaloneからWebrootに変更する方法を解説しました。

Let's Encryptを導入して半年も経つというのに、今までちゃんと理解せずに使い続けていたため今回のような事象に陥ってしまいました。

何かを導入するときはちゃんと理解してから使うことが重要ですね。