Let’s Encrypt で取得した証明書の更新スクリプトを書いてみた

本記事は最終更新日より 1 年以上経過しております。
スポンサーリンク

 Let’s Encrypt の発行するサーバー証明書は有効期間が 90 日と短く、自動更新による運用を前提とされている。
 適当にコマンドをそのままシェルにしても大丈夫だけど、少し便利な物にしようと思って CentOS 7 向けスクリプトを書いてみた。

ソース

#!/bin/bash
# Renew a certificate of let's encrypt.Using --webroot.

Script=/root/certbot/certbot-auto
Days=30
# HostName=DocumentRoot \
HostNameList="\
example.com=/path/to/docroot \
hoge.example.com=/path/to/hoge_docroot \
foo.example.com=/path/to/foo_docroot \
"
Tag=renew-certs
IsRenewal=0

for i in $HostNameList; do
    HostName=$(echo $i | cut -d '=' -f 1)
    DocumentRoot=$(echo $i | cut -d '=' -f 2)
    TimeLimit=$(openssl x509 -checkend $(( $Days * 86400 )) -enddate -in /etc/letsencrypt/live/$HostName/cert.pem)
    if [ $? -ne 0 ]; then
        TimeLimit=$(echo $TimeLimit | sed -e "s/[\r\n]\+//g")
        # Logging about an Expire.
        logger -i -t $Tag -p user.notice "$HostName / ${TimeLimit/notAfter=/}"
        # Renew Certificate.
        $Script --renew-by-default certonly --webroot -d $HostName -w $DocumentRoot > /dev/null 2>&1
        # Logging Results.
        if [ $? -eq 0 ]; then
            logger -i -t $Tag -p user.info "$HostName / Certificate update process finished."
            IsRenewal=1
        else
            logger -i -t $Tag -p user.notice "$HostName / Error Occurred."
        fi
    fi
done

if [ $IsRenewal -eq 1 ]; then
    systemctl restart httpd
fi

動作

 まず前提として –webroot で証明書を更新出来る様にしておく。TCP/80 から /.well-known/acme-challenge/ 以下にアクセスを正常に出来るだとかそういう所。更新時なら TCP/443 からでも回せるらしいけど未検証。
 自分は色々と面倒だからと root で回すようにしているので、変数 Script で表す certbot-auto のパス指定は root 以下にしている。別に “~/certbot/certbot-auto” でも良いかなと。
 更新対象は「ホスト名=ドキュメントルート」の形式を取り、後に “=” で分割する事でそれぞれを使用し –webroot のオプションに必要な -d と -w に引き渡している。末尾に半角スペースとバックスラッシュを忘れずに。
 ソース冒頭の Days で指定された日数以内に証明書が無効となる場合でのみ更新処理が走るようになっている。cron で毎日でもいいし 2~3 日毎でもいいから実行させておく。そうすることで証明書の有効期限が全て横並びでなくとも、必要に迫られれば更新するし、なにかしらエラーが発生していても後日、また更新処理が出来る算段だ。
 更新処理自体は stdout, stderr を /dev/null しているので、代わりに Syslog 経由で処理内容を出力している。letsencrypt-auto 自体もログを /var/log/letsencrypt 以下に吐くのだから、詳細な内容はそれを参照すれば良い。
 更新するホスト何れか 1 つでも証明書が更新されているのなら、httpd を再起動させている。もし Postfix や Dovecot の証明書もゴニョって更新させているなら、httpd の後に postfix と dovecot と追加してあげるとよい。systemd は便利。

動作確認など

 Days=90 として強制的に更新処理を走らせた結果の /var/log/messages で、ホスト名は実在する自分の物だからそのまま貼り付ける。

Dec 13 17:37:37 xxxxx renew-certs[26837]: bucci.bp7.org / Mar 10 13:05:00 2016 GMT Certificate will expire
Dec 13 17:37:50 xxxxx renew-certs[26938]: bucci.bp7.org / Error Occurred.
Dec 13 17:37:50 xxxxx renew-certs[26946]: rss.bp7.org / Mar 11 14:49:00 2016 GMT Certificate will expire
Dec 13 17:38:03 xxxxx renew-certs[27049]: rss.bp7.org / Error Occurred.
Dec 13 17:38:03 xxxxx renew-certs[27057]: www.bp7.org / Mar 10 13:47:00 2016 GMT Certificate will expire
Dec 13 17:38:16 xxxxx renew-certs[27158]: www.bp7.org / Error Occurred.
Dec 13 17:38:16 xxxxx renew-certs[27166]: mx.bp7.org / Mar 11 07:38:00 2016 GMT Certificate will expire
Dec 13 17:38:28 xxxxx renew-certs[27268]: mx.bp7.org / Error Occurred.
Dec 13 17:38:28 xxxxx renew-certs[27276]: cloud.buccimoni.com / Mar 12 07:26:00 2016 GMT Certificate will expire
Dec 13 17:38:41 xxxxx renew-certs[27385]: cloud.buccimoni.com / Certificate update process finished.
Dec 13 17:38:41 xxxxx systemd: Stopping The Apache HTTP Server...
Dec 13 17:38:45 xxxxx systemd: Starting The Apache HTTP Server...
Dec 13 17:38:45 xxxxx systemd: Started The Apache HTTP Server.

 bp7.org のドメインは既に 1 ドメインあたりの取得制限に掛かっているからエラーが出る。エラー処理も OK。
 OwnCloud で使用している cloud.buccimoni.com のドメインは正常に更新処理が完了した。
 ひとつでも証明書が更新されているので Apache が再起動して新しい証明書が有効となっている。

cron に登録

 crontab -e でもいいし /etc/crontab でも好きな方にエントリさせておく。今回は後者にした。

  0  5 */2 *  * root /root/bin/renew-cert.sh

 これで 2 日おきの 05:00 にサーバー証明書のチェック及び期限まで 30 日以内であれば更新処理が走る用になる。

追記

2016/07/03 21:10

 letsencrypt-auto は古いスクリプトで、現在は certbot-auto に変更されている。その為、スクリプトのバイナリパスも変更を行った。
 引数は同じで大丈夫なので、バイナリパス以外は変更点無し。

スポンサーリンク