certbot renewで証明書をメール送信

Let’s Encryptで作成した証明書をSSLアクセラレータに設置するため、certbot renew で証明書を更新したら、Zipにしてメール送信するようにしてみました。

Ubuntu 20.04.2 では apt で certbot をインストールすると、/etc/cron.d/certbot が設置され、1日2回 certbot renew が実行されますので、基本的には証明書を作成すれば他に何もしなくても自動更新されます。

# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc.  Renewal will only occur if expiration
# is within 30 days.
#
# Important Note!  This cronjob will NOT be executed if you are
# running systemd as your init system.  If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob.  For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew

最初に証明書を作成するときはコマンドラインから certbot を起動します。

# certbot certonly --manual -d 'www.taiyodo.ga' -d 'taiyodo.ga'

certonly は証明書の作成のみで、ApacheやNginxへのインストールは行いません。

http-01チャレンジが選択されますので、チャレンジ用のファイルをウェブサーバの所定の位置に指定されたファイルを作成して続行します。-d オプションで複数のドメインを指定し、マルチドメインの証明書(SANs)を作成しますので、チャレンジ用のファイルもその数分作成します。

なお、ワイルドカード証明書の場合は http-01 でなく dns-01 でのチャレンジが必要になり、DNSサーバのTXTレコードを編集する必要があります。

証明書が作成されると、 /etc/letsencrypt/renew/www.taiyodo.ga.conf が作成され、renew 対象になります。

renew 時には /etc/letsencrypt/renew/ にある各証明書ごとの設定ファイルを読み込み、更新します。

renew の際にメールを送るためには、①http-01の認証を自動化、②証明書が作成されたときにメール送信、の2つの機能を作る必要があります。

certbot のドキュメントは「certbot -h all」で見ることができます。トピックごとのドキュメントならば、renew の場合は「certbot -h renew」という具合です。

認証の自動化には「--manual-auth-hook」オプションでスクリプトを指定することにより、対話形式で行っていたチャレンジ用のファイルを作成する処理を行わせます。

具体的には、https://www.taiyodo.ga/.well-known/acme-challenge/$CERTBOT_TOKEN にアクセスしたら $CERTBOT_VALIDATION を出力するように、本番サーバのドキュメントルート以下の所定のディレクトリに $CERTBOT_TOKEN ファイルを作成します。

#!/bin/sh
case ${CERTBOT_DOMAIN} in
    www.taiyodo.ga*)
        DOCROOT="/home/taiyodo/www/www.taiyodo.ga"
        ;;
    other.taiyodo.ga)
        DOCROOT="/home/taiyodo/www/other.taiyodo.ga"
        ;;
    *)
        echo "No such domain: ${CERTBOT_DOMAIN}"
        exit 1
        ;;
esac
echo "$CERTBOT_VALIDATION" | ssh -i ./.ssh/id_rsa-taiyodo.ga mtystg@taiyodo.production.server "cat > $DOCROOT/.well-known/acme-challenge/$CERTBOT_TOKEN"

$CERTBOT_DOMAIN は認証するドメインで、ドメインごとにバーチャルホストを切っているのでドキュメントルートのディレクトリを変更しています。

認証のため作成したファイルは「--manual-cleanup-hook」で削除します。

#!/bin/sh
case ${CERTBOT_DOMAIN} in
    www.taiyodo.ga*)
        DOCROOT="/home/taiyodo/www/www.taiyodo.ga"
        ;;
    other.taiyodo.ga)
        DOCROOT="/home/taiyodo/www/other.taiyodo.ga"
        ;;
    *)
        echo "No such domain: ${CERTBOT_DOMAIN}"
        exit 1
        ;;
esac
ssh -i ./.ssh/id_rsa-taiyodo.ga mtystg@taiyodo.production.server "rm -f $DOCROOT/.well-known/acme-challenge/$CERTBOT_TOKEN"

証明書が作成されたときに、証明書をZipファイルにしてメール送信するには「–deploy-hook」にスクリプトを指定します。

#!/bin/sh
expire_date=$(openssl x509 -text -noout -in $RENEWED_LINEAGE/cert.pem | perl -ne 'use Time::Piece; if ($_ =~ /Not After : (.*)$/) {my $t = Time::Piece->strptime("$1","%b %d %H:%M:%S %Y %Z"); $t = localtime($t); $t += $t->tzoffset; print $t->strftime("%Y年%m月%d日 %H:%M:%S");}')

zip_file=cert-$(date '+%Y%m%d').zip
zip_path="/tmp/$zip_file"
cd "$RENEWED_LINEAGE" && zip -qP XXXXXXX "$zip_path" *

sed 's/$/\r/' <<EOF | sendmail -i -f mtystg@gmail.com mtystg@gmail.com
From: mtystg@gmail.com
To: mtystg@gmail.com
Subject: =?UTF-8?B?U1NM6Ki85piO5pu45pu05paw5L6d6aC8?=
Content-Type: multipart/mixed; boundary="0000000000008a725805c8d82df4"

--0000000000008a725805c8d82df4
Content-Type: text/plain; charset="UTF-8"

SSL証明書を送付しますので、更新をお願いします。

有効期限: $expire_date

次回は有効期限の20日前頃に送付予定です。


--0000000000008a725805c8d82df4
Content-Type: application/x-zip-compressed; name="$zip_file"
Content-Disposition: attachment; filename="$zip_file"
Content-Transfer-Encoding: base64

`base64 "$zip_path"`
--0000000000008a725805c8d82df4--
EOF

動作を確認するため、「--dry-run」オプションをつけて実行してみます。

# certbot renew --cert-name www.taiyodo.ga --manual-auth-hook=$HOME/certbot_hooks/manual_auth_hook.sh --manual-cleanup-hook=$HOME/certbot_hooks/manual_cleanup_hook.sh --deploy-hook=$HOME/certbot_hooks/deploy_hook.sh --dry-run

deploy_hook.sh は「--dry-run」では実行されない旨のメッセージが出力されます。認証のみのテストになります。

/etc/letsencrypt/renew/www.taiyodo.ga.conf にhookなどの設定を追加します。

renew_before_expiry = 20 days
manual_auth_hook = /home/root/certbot_hooks/manual_auth_hook.sh
manual_cleanup_hook = /home/root/certbot_hooks/manual_cleanup_hook.sh 
deploy_hook = /home/root/certbot_hooks/deploy_hook.sh 

これで次回更新されてメールが届けばOKになります。

なお、デフォルトでは renew_before_expiry は30日ですので、20日に変更しています。

2022-03-13 追記

/etc/letsencrypt/renew/ の conf ファイルに「deploy_hook」を設定しても起動しません。

https://github.com/certbot/certbot/issues/5660 に記載がありますが、オプションの –renew-hook を –deploy-hook には変更しましたが、conf のパラメータは必要性が薄いため変更されていません。よって、上記の conf では deploy_hook.sh は起動できませんでした。

renewal.py でもこのようになっていました。

# These are the items which get pulled out of a renewal configuration
# file's renewalparams and actually used in the client configuration
# during the renewal process. We have to record their types here because
# the renewal configuration process loses this information.
STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent",
                    "server", "account", "authenticator", "installer",
                    "renew_hook", "pre_hook", "post_hook", "http01_address",
                    "preferred_chain", "key_type", "elliptic_curve"]

ということで、以下のように renew_hook を使用するのが正しいです。

renew_hook = /home/root/certbot_hooks/deploy_hook.sh 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です