Let's Encryptで常時SSL化してみた話(証明書の自動更新が失敗していた)
参照:Let's Encrypt総合ポータル
情報が少なくて結構大変だった。証明書の発行から自動更新まで全部マニュアル操作でやるような酔狂な輩は少ないのだろうな
母親の骨折入院に加え、同じ頃からワタシも原因不明の頭痛に悩まされ続けていた。やっと首や肩から来ているのではないかと感じ出し、整形で診てもらったところ頚椎椎間板ヘルニアと診断された。要するにギックリ腰が首で起きたようなモノのようだ。現在は筋肉の緊張をほぐす薬、炎症を押さえる薬とリハビリ(首の牽引、マッサージ)で、ようやく少し楽になってきた。が、まだ長時間デスクワーク(PC操作)をしていると痛みがひどくなり辛い状態だ。
Let's Encryptの証明書は有効期間が90日間(約3ヶ月)と短い。3ヵ月ごとに更新作業を行うのは面倒なので、失効前に証明書が自動更新されるようcronを仕込んでおいたのだが、残り期間が20日を切ったとのメール(以下)が届いていた。
Hello,
Your certificate (or certificates) for the names listed below will expire in 19 days (on 24 Apr 19 08:02 +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.
y-naito.ddo.jp
For any questions or support, please visit https://community.letsencrypt.org/. Unfortunately, we can't provide support by email.
〜略〜
Regards,
The Let's Encrypt Team
|
更新処理はcronで週一回実行しているのに、有効期限が20日を切ったということは、自動更新処理が失敗しているということになる。
1. 証明書の更新が失敗する原因
ログ(/var/log/letsencrypt/letsencrypt.log.*)を見たところ、原因は以下にありそうだ。
2019-03-26 04:01:46,572:INFO:certbot.auth_handler:Cleaning up challenges
2019-03-26 04:01:46,574:WARNING:certbot.renewal:Attempting to renew cert (y-naito.ddo.jp) from /etc/letsencrypt/renewal/y-naito.ddo.jp.conf produced an unexpected error:
Failed authorization procedure. y-naito.ddo.jp (http-01): urn:ietf:params:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://y-naito.ddo.jp/.well-known/acme-challenge/VKscynj4Tzgj69pQJU1sy1e4sy26HAyoJBMIDIA9a3M [180.45.255.213]:
"<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p". Skipping.
〜略〜
2019-03-26 04:01:46,587:ERROR:certbot.renewal:All renewal attempts failed. The following certs could not be renewed:
2019-03-26 04:01:46,588:ERROR:certbot.renewal: /etc/letsencrypt/live/y-naito.ddo.jp/fullchain.pem (failure)
|
".well-known/acme-challenge/" の下にあるべき認証ファイル "VKscynj4Tzgj69pQJU1sy1e4sy26HAyoJBMIDIA9a3M" が見つからないと言っている。".well-known/acme-challenge/"ディレクトリは作成してある(以前「3. マニュアル操作でドメイン証明書を取得」で手作業で作成した)が、認証ファイル名は最初に証明書を取得した時のものとは異なっており、当然ながらファイルの中身の認証コード(token)も異なる。また他の失敗しているログも見ると、認証ファイルの名前は毎回違うものになっていた。
色々と調べたところ、認証コード(token)はセッション毎に一度限りのワンタイムトークンであり、これにも有効期間(3〜7日程度らしい)がある。そのため更新の度に新しい認証コード(token)が発行され、通常は認証ファイルが自動的に新しいものに置き換えられる。しかし、ワタシはマニュアル操作で証明書の取得を行っており、手作業で認証ファイルを作成したので、更新時に自動的に置き換えてはくれないのだった。認証コードは、ドメインの正当な所有者が自分であることを確認するためのものなので、これが無ければ証明書を発行してもらえる筈もない。
認証コード(token)がワンタイムトークンだとか有効期間があるだとか、セキュリティの事などをちょっと考えれば全く当たり前の話ではあるが、一度発行された認証コードはウチのドメイン固有のもので、この先もずっと使い続けることができると思い込んでいた愚か者なのであった(これでもIT関係の仕事を生業にしているのだから、考えてみれば恐ろしい話ではある ^^;)。
2. 対処方法
と言うことで、考えられる対処方法としては以下の2つがある。
- もう一度マニュアル操作で証明書を取得する
最も簡単な対処方法ではあるが、これではまた3ヶ月後に同じ作業をしなければならないわけで、証明書の自動更新もできない。
- 証明書取得の際に認証ファイルが自動的に生成されるようにする
要するに新規に認証コード(token)が発行されたら、それを書き込んだ認証ファイルを".well-known/acme-challenge/"の下に自動的に作成して、証明書の発行処理を続けてくれれば良いわけだ(*1)。これは --manual-auth-hookオプションにちょっとしたスクリプトを指定するだけで実現できるようだ。
3. --manual-auth-hook オプションに指定するスクリプトの作成
今回作成したスクリプトはこんな感じの実に簡単なものだ。
#!/bin/sh
WEBROOT="/usr/local/apache2/htdocs"
CERTDIR="${WEBROOT}/.well-known/acme-challenge"
echo "${CERTBOT_VALIDATION}" >${CERTDIR}/${CERTBOT_TOKEN}
/usr/sbin/chown apache:www ${CERTDIR}/${CERTBOT_TOKEN}
exit 0
|
"WEBROOT"はApache2(ホームページ)のDocumentRootに指定してあるディレクトリで、この配下に".well-known/acme-challenge/"ディレクトリを作ってある。ここに認証コード(token)を書き込んだ認証ファイルを作っているだけだ。必要な情報は環境変数(以下)で渡されて来る。
- CERTBOT_DOMAIN : 検証するドメイン名
- CERTBOT_VALIDATION : 認証コード(token)
- CERTBOT_TOKEN : 認証ファイル名
このスクリプトを"/usr/local/bin/certbot-auth.sh"という名前で保存して実行権を付けておく。
4. マニュアル操作でドメイン証明書を取得
もう一度マニュアル操作でドメイン証明書を取得する。このとき certbot-auto に --manual-auth-hook オプションで上で作成したスクリプトを指定する。
$ /usr/local/bin/certbot-auto certonly --manual --manual-auth-hook /usr/local/bin/certbot-auth.sh -d y-naito.ddo.jp
|
以前は認証コードが画面に表示され、手作業で認証ファイルを作成したが、今度は自動的に作成されて証明書発行処理が進むはず(*2)。
"/etc/letsencrypt/renewal/y-naito.ddo.jp.conf"を確認し、以下のように "manual_auth_hook = /usr/local/bin/certbot-auth.sh" があれば、とりあえずOK。
$ cat /etc/letsencrypt/renewal/y-naito.ddo.jp.conf
# renew_before_expiry = 30 days
version = 0.30.0
archive_dir = /etc/letsencrypt/archive/y-naito.ddo.jp
cert = /etc/letsencrypt/live/y-naito.ddo.jp/cert.pem
privkey = /etc/letsencrypt/live/y-naito.ddo.jp/privkey.pem
chain = /etc/letsencrypt/live/y-naito.ddo.jp/chain.pem
fullchain = /etc/letsencrypt/live/y-naito.ddo.jp/fullchain.pem
# Options used in the renewal process
[renewalparams]
authenticator = manual
account = 8ennxxnnxnnxnnxxxnnxnxnnxnnnxnnn
manual_public_ip_logging_ok = True
server = https://acme-v02.api.letsencrypt.org/directory
manual_auth_hook = /usr/local/bin/certbot-auth.sh
|
新しい証明書が取得できたか確認する。
$ sudo ls -l /etc/letsencrypt/live/y-naito.ddo.jp
total 40
-rw-r--r-- 1 root wheel 692 1 20 19:55 README
lrwxr-xr-x 1 root wheel 38 4 5 14:57 cert.pem -> ../../archive/y-naito.ddo.jp/cert8.pem
lrwxr-xr-x 1 root wheel 39 4 5 14:57 chain.pem -> ../../archive/y-naito.ddo.jp/chain8.pem
lrwxr-xr-x 1 root wheel 43 4 5 14:57 fullchain.pem -> ../../archive/y-naito.ddo.jp/fullchain8.pem
lrwxr-xr-x 1 root wheel 41 4 5 14:57 privkey.pem -> ../../archive/y-naito.ddo.jp/privkey8.pem
|
大丈夫なようなので、Apacheを再起動、Webブラウザで自分のホームページを表示して証明書を確認する。証明書の有効期限は、ちゃんと3ヵ月後になっていた。
5. 証明書の更新を試す
現在の".well-known/acme-challenge/"ディレクトリ配下にある認証ファイルを表示し、もし古い認証ファイルがあったら、それらは必要ないので削除しておこう。
$ ls -lt .well-known/acme-challenge/
-rw-r--r-- 1 apache www 88 4 5 14:56 USnxoRLdNNV2zwDeHFTKi040_EvMjZpxPHEcy6eog7k
|
先ずは証明書の有効期限がたっぷり残っている状態での更新を試してみる。
$ sudo /usr/local/bin/certbot-auto renew
WARNING: unable to check for updates.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/y-naito.ddo.jp.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certs are not due for renewal yet:
/etc/letsencrypt/live/y-naito.ddo.jp/fullchain.pem expires on 2019-07-04 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
$ ls -lt .well-known/acme-challenge/
-rw-r--r-- 1 apache www 88 4 5 14:56 USnxoRLdNNV2zwDeHFTKi040_EvMjZpxPHEcy6eog7k
|
証明書の更新の必要がないのでskippedと表示され、認証ファイルにも変化がない。そこで次。前回は強制的に更新させたのだが、今回は--dry-runオプションを使ってみた。--dry-runオプションは実行動作の確認を行うのみなので、Let's Encryptのサーバに負荷をかけることもない。
$ sudo /usr/local/bin/certbot-auto renew --dry-run
WARNING: unable to check for updates.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/y-naito.ddo.jp.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator manual, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for y-naito.ddo.jp
Waiting for verification...
Cleaning up challenges
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/y-naito.ddo.jp/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/y-naito.ddo.jp/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
$ ls -lt .well-known/acme-challenge/
-rw-r--r-- 1 apache www 88 4 5 18:00 SiVHKzIJsJSf5ZY2St_4LhTmnb6ibSeLwplmkLkqOA4
-rw-r--r-- 1 apache www 88 4 5 14:56 USnxoRLdNNV2zwDeHFTKi040_EvMjZpxPHEcy6eog7k
|
問題なく更新できるようで、認証ファイルも新しいものが自動的に作成されている。これなら、自動更新もうまく行くのではなかろうか?
結果は、また3ヵ月後に・・・(^^;)
備考(*1):これを自動的にやってくれるのが --webrootプラグインなのだが、ここまで来たら、とことん --manualで突っ走ってみる。
備考(*2):認証コード(token)は一度限りのものなので、証明書が取得できたら認証ファイルを残しておく必要もない。そのため用済みになった認証ファイルは自動的に消してしまうこともできる。方法はマニュアル操作で証明書を取得する際に、以下のようなスクリプトを certbot-auto の --manual-cleanup-hook オプションに指定するだけ。
#!/bin/sh
WEBROOT="/usr/local/apache2/htdocs"
CERTDIR="${WEBROOT}/.well-known/acme-challenge"
rm -f ${CERTDIR}/${CERTBOT_TOKEN}
exit 0
|
|