Let’s encryptのドメイン認証の方法を証明書の更新にあわせてHTTP-01に変更してみた(おまけつき)。

By | 2019年2月4日 , Last update: 2021年2月19日

はじめに

Let’s encryptのドメイン認証の方法のうち、ACME TLS-SNI-01 validationによるドメイン認証ができなくなるので、HTTP-01によるドメイン認証に切り替えるために本Webサイトにおいて必要であった設定について前の記事で書きました。

実は、前の記事を書き始めた時点では使用中であったLet’s encryptの証明書の期限まで30日以上残っていたために更新作業を行うことができなかったことをコロッと忘れたまま記事を書き始めてしまい、途中でそれに気がついたものの、

時すでにお寿司🍣

(※画像は本記事の内容とは関係ありません。)

準備のために必要な作業や確認事項だけでも書いておけば何かの役には立つだろうと思ったので、記事としての切れ味はイマイチな感じをキープしたまま前の記事は公開されてしまったという訳です。(´・ω・`)

そしてこの度めでたく、証明書の有効期限まであと30日を切りましたので、ドメイン認証としてHTTP-01を使うことによって証明書を更新する初めての機会が訪れました。☺

そこで、cronで起動できるシェルスクリプト(letsencrypt.sh)を書き、その中からcertbotコマンドを起動するように設定してみました。

この記事では、このletsencrypt.shについて書いていきます。

さらに、おまけとしてletsencrypt.shを起動するタイミングについても考察します。

スポンサーリンク

letsencrypt.shの開発

いきなりスクリプト。

本Webサイトで使用しているドメイン(pandanote.info)の証明書の自動更新用のスクリプト(letsencrypt.sh)は以下の通りです。

letsencrypt.shをWebサーバの/var/adm/binの下に置いています。

なお、以下の組み合わせで動作確認を行っています。nginxを使用しているサーバでletsencrypt.shをcron経由で起動する際には/usr/sbinにPATHを明示的に通す必要がありますので、6行目でPATH環境変数を設定しています。

また、Fedora 33以降用のletsencrypt.shではcertbotコマンドのsubcommandにcertonlyを使用することにしたため、第1引数に証明書の更新を行うドメイン名のリストをカンマ区切りで設定するように変更しています。

  1. Fedora 29+Apache httpd
  2. Fedora 30+Apache httpd
  3. Fedora 31+Apache httpd
  4. Fedora 31+nginx
  5. Fedora 32+nginx
  6. Fedora 33+nginx

スポンサーリンク

/etc/nginx/nginx.confという名前のファイルが存在する場合に限り、certbotコマンド実行時のコマンドラインオプションに”--nginx”を追加しています。

#!/bin/sh
#
# See https://pandanote.info/?p=3842 for details.
#
PATH=/usr/bin:/bin:/usr/sbin
DRY_RUN=""
LASTCHECK_DIR=/var/run/letsencrypt
LASTCHECK_FILE=${LASTCHECK_DIR}/lastcheck.txt
LOG_DIR=/var/log/letsencrypt
SERVER_OPT=""
if [ "$1" = "-d" ]; then
DRY_RUN="--dry-run"
shift
fi
if [ -n "$1" ]; then
DOMAIN_LIST=`echo $1 | sed -e "s/,/ /"`
else
echo "Usage: $0 <domain list(comma-separated)>"
fi
if [ ! -d "${LASTCHECK_DIR}" ]; then
mkdir -p "${LASTCHECK_DIR}"
fi
if [ ! -d "${LOG_DIR}" ]; then
mkdir -p "${LOG_DIR}"
fi
if [ ! -f "${LASTCHECK_FILE}" ]; then
echo "0" > ${LASTCHECK_FILE}
fi
if [ -f /etc/nginx/nginx.conf ]; then
SERVER_OPT="--nginx"
fi
LASTCHECK_TIME=`cat ${LASTCHECK_FILE}`
CURRENT_TIME=`date +%s`
ELAPSED_TIME=`expr {LASTCHECK_TIME}`
LOG_FILE=${LOG_DIR}/letsencrypt_update_`date +%Y%m%d%H%M`.log
if [ ${ELAPSED_TIME} -le 5184000 ]; then
echo "Not yet due to update (elapsed time: Misplaced &{LOG_FILE}
exit 1
fi
for i in ${DOMAIN_LIST}
do
certbot certonly {SERVER_OPT} -n -d Misplaced &{LOG_FILE}
done
rm -f ${LASTCHECK_FILE}
date +%s > ${LASTCHECK_FILE}
cd ${LOG_DIR}
find . -type f -a -mtime +100 -a -name letsencrypt\* -exec rm -f '{}' \;
exit 0

証明書の更新を行うためだけであれば52行目だけを実行すれば十分ですが、Let’s encryptの証明書の更新のタイミングの都合上、そのタイミングを見計らうための前処理が必要です。

また、Apache httpdのreload処理については、本Webサイトが稼働しているサーバの都合上、httpdの子プロセスが頻繁に入れ替わっていることと、その際に新しい設定(証明書含む)が子プロセスに読み込まれているようですので、省略しています。

この前処理については次節で書きます。

Let’s encryptの証明書の「残り30日問題」等への対応

letsencrypt.shを自動的に起動させるための設定を考える上で欠かせないのが、証明書が更新できるタイミングです。

前の記事でもほんのちょっとだけ書きましたが、Let’s encryptの証明書は有効期限まで30日以内にならないと更新できません。

また、有効期限の19日前と9日前(時々20日前と10日前になることがあります。)に以下のようなタイトルのメールが送られてきて、軽く煽られます。

Let’s Encrypt certificate expiration notice for domain “pandanote.info”

 


スポンサーリンク

この種類のメールも可能であればできるだけ送られてこないように設定したいものです。

上記の問題を解決するために、有効期限の30日前から19日前までの間にcertbotコマンドを実行して証明書を更新する方法を考えることにしました。

これにはシェルスクリプト側での対応とcronの設定が必要で、以下のような役割分担とすることにしました。

  • シェルスクリプト側: 直近でcertbotコマンドを実行した時刻をファイルに保管しておき、その時刻からシェルスクリプトが起動された時刻までの経過時間が60日を超えている場合にはcertbotコマンドを実行する。それ以外の場合にはcertbotコマンドを実行せずにシェルスクリプトを終了する。
  • cron側: 上記のシェルスクリプトを平均10日より短く、かつわかりやすい周期で実行したいので、1週間に1回実行するように設定する。

letsencrypt.shの実行例

上記のletsencrypt.shの実行例は以下のような感じになります。なお、pandanote.infoに直接関係しない部分については一部省略しています。

Saving debug log to /var/log/letsencrypt/letsencrypt.log Non-interactive renewal: random delay of 315 seconds – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – Processing /etc/letsencrypt/renewal/pandanote.info.conf – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – Cert is due for renewal, auto-renewing… Plugins selected: Authenticator apache, Installer apache Renewing an existing certificate Performing the following challenges: http-01 challenge for pandanote.info Waiting for verification… Cleaning up challenges – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – new certificate deployed with reload of apache server; fullchain is /etc/letsencrypt/live/pandanote.info/fullchain.pem – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/pandanote.info/fullchain.pem (success) – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –

 
ドメイン認証の方法としてHTTP-01を使用できているようです。(`・ω・´)シャキーン

certbotコマンドはコマンドラインから手動で起動されていないと判断した場合には、ちょっと時間をおいてから証明書の更新をリクエストするようです。上記の例でもcertbotの実行開始直後に315秒待ってから証明書の更新をリクエストしています。

cronの設定

以下のようなファイルを作成し、/etc/cron.dの下に置きました。毎週金曜日の夜に起動する設定としています。

28 23 * * 6 root /var/adm/bin/letsencrypt.sh <domain name(comma-separated)> 2>&1 1>/dev/null

まとめ

60日おきにシェルスクリプトを起動しようと思って「偶数月(または奇数月)の特定の日の特定の時刻」に起動するように設定すると、2月の末日の前後では直前のシェルスクリプトの起動からの経過時間が60日に満たないことがあるために、所望の結果が得られない(=証明書の更新ができない)ことになります。証明書の更新ができないと次回のシェルスクリプトの起動は60日後になります。すると、その間に使用中の証明書の有効期限が到来してしまい、有効な証明書がない期間ができてしまいます。これではシェルスクリプトを作った意味がなくなってしまいます。

また、「毎月の特定の日の特定の時刻」に起動するように設定してしまうと、証明書の更新ができないタイミングであることがわかっているのにcertbotコマンドを実行することになり、本Webサイトを運用しているサーバに無用な負荷をかけてしまいますので、それもできれば避けたいところです。

というわけで少々複雑ではありますが、上記のような仕組みを作ってみました。

これでワンランク上の証明書更新ライフを楽しむことができると思います。(`・ω・´)

この記事は以上です。