Let’s Encryptの証明書自動更新のSlack通知を賢く行う

「Let’s Encrypt Slack」でググると、更新から通知までの一連の処理が一つのシェルスクリプトになっているゴリ押し法が出てくるので、もう少し管理を楽にする方法を模索した。

検証環境

  • Ubuntu 18.04.4 LTS (Bionic Beaver)
  • certbot 0.27.0-1~ubuntu18.04.1
  • MatterMost 5.20.1(Slack互換アプリとして検証)

apt の Certbot

apt の certbot パッケージには標準で crontabsystemd-timer のファイルが同梱されている。

$ apt-file list certbot
certbot: /etc/cron.d/certbot
certbot: /etc/letsencrypt/cli.ini
certbot: /etc/logrotate.d/certbot
certbot: /lib/systemd/system/certbot.service
certbot: /lib/systemd/system/certbot.timer
certbot: /usr/bin/certbot
certbot: /usr/bin/letsencrypt
certbot: /usr/share/doc/certbot/README.rst.gz
certbot: /usr/share/doc/certbot/changelog.Debian.gz
certbot: /usr/share/doc/certbot/copyright
certbot: /usr/share/man/man1/certbot.1.gz
certbot: /usr/share/man/man1/letsencrypt.1.gz
$ cat /etc/cron.d/certbot
# /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.
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
$ cat /lib/systemd/system/certbot.service
[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://letsencrypt.readthedocs.io/en/latest/
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true
$ cat /lib/systemd/system/certbot.timer
[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target

したがって個別にお手製の crontab を書いたり、パッケージ同梱のスクリプトを上書きして Slack 連携させるのは管理上あまり得策とは言えない。

解決法:Certbot の deploy hook を使う

certbot コマンドには --deploy-hook オプションがある。

--deploy-hook DEPLOY_HOOK
                      Command to be run in a shell once for each
                      successfully issued certificate. For this command, the
                      shell variable $RENEWED_LINEAGE will point to the
                      config live subdirectory (for example,
                      "/etc/letsencrypt/live/example.com") containing the
                      new certificates and keys; the shell variable
                      $RENEWED_DOMAINS will contain a space-delimited list
                      of renewed certificate domains (for example,
                      "example.com www.example.com" (default: None)

要は証明書ファイルが正しく更新できた時にコマンド DEPLOY_HOOK を実行するというもの。
ここに Slack webhook を叩く curl コマンドや、それを記述したシェルスクリプトのパスをおけば良い。
更新されたドメイン名は環境変数 $RENEWED_DOMAINS で与えられる(同一証明書で複数のドメインを認証した場合はスペース区切りで与えられるが、証明書が異なる場合は証明書ごとにコマンドが実行される)。

#!/bin/bash

SLACK_MESSAGE="New certificate created successfully: \`$RENEWED_DOMAINS\`"

SLACK_WEBHOOK_URL='<your_webhook_url>'
SLACK_CHANNEL='<your_slack_channel_id>'
SLACK_USERNAME="Let's Encrypt"
SLACK_ICON_URL='https://letsencrypt.org/images/le-logo-lockonly.png'

curl -s -o /dev/null -X POST --data-urlencode 'payload={"channel": "'"$SLACK_CHANNEL"'", "username": "'"$SLACK_USERNAME"'", "text": "'"$SLACK_MESSAGE"'", "icon_url": "'"$SLACK_ICON_URL"'"}' $SLACK_WEBHOOK_URL

この hook ファイルの保存先として、 /etc/letsencrypt/renewal-hooks/ というディレクトリが用意されている。
中にはそれぞれ deploy/ post/ pre/ というディレクトリがある(postpre については割愛する)ため、 /etc/letsencrypt/renewal-hooks/deploy/notify_slack として保存する。

/etc/letsencrypt/renewal-hooks/deploy/notify_slack

最後に、 certbot パッケージには /etc/letsencrypt/cli.ini という設定ファイルが存在する。
このファイルには certbot コマンドのオプション名と同じ属性名を与えることで、自動的にオプションが渡せるようになるものである。
このおかげで、 パッケージ標準の crontabsystemd-timer ファイルに手を加えずに済む。
/etc/letsencrypt/cli.ini 末尾に以下の一行を追加する(引用符で囲むと引用符もコマンドの一部として認識されてしまうので注意する)。

deploy-hook = /etc/letsencrypt/renewal-hooks/deploy/notify_slack

これで完成。

機能テスト

例えば nginx を使っている場合は以下のコマンドで強制的に証明書を更新できる。
ただし証明書更新の Rate Limit (同一ドメインは1週間に5回まで)には注意すること。

sudo certbot certonly -d <your_domain> --force-renewal --nginx

通知スクリプトだけ試したい場合は以下のコマンドで良い。

RENEWED_DOMAINS=<your_domain> /etc/letsencrypt/renewal-hooks/deploy/notify_slack

コメントを残す

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