tinydns on CentOS7でDNS-01使ったLet’s Encrypt証明書の自動更新してみた

ちゃんと動いたのが確認できたので、作業記録として公開してみる。

動機

  • Lets’s Encryptでワイルドカード証明書を取得するにはDNS-01方式での認証が必要
    • 更新のたびにDNS-01による認証が必要になる
    • 認証に使うTXTレコードを毎回手動で登録、削除するのがめんどくさい
  • 自分の手元ではtinydns(ndjbdnsパッケージ)を動作させているが、自動更新に使える出来合いのフックスクリプトが見つからなかった。
    • ないなら自分でなんとかしよう

フックスクリプトの作成

ということで自作する。
大したことをやるわけではないので、お手軽にシェルスクリプトで実装する。

スクリプトで使用する変数

certbotでは--manual-auth-hook--manual-cleanup-hookに独自のスクリプトを指定できる。

User Guide — Certbot 2.11.0.dev0 documentation

このとき、certbotから渡される以下の変数を使って必要な処理を書いていくことになる。

  • CERTBOT_DOMAIN: 認証対象のドメイン名。
  • CERTBOT_VALIDATION: 検証に使う文字列。これをTXTレコードに登録する。
  • CERTBOT_TOKEN: HTTP-01方式で使うものなので、今回は使わない。

また、必要なら処理結果として出力された文字列をCERTBOT_AUTH_OUTPUTを参照してログとして吐き出すこともできる。(今回は使わず)

auth-hookスクリプト

以下のシェルスクリプトを、certbot-tinydns.shと名付けてauth-hookスクリプトとして使う。

#!/bin/bash

DOMAIN="_acme-challenge.${CERTBOT_DOMAIN}"
VALIDATION=${CERTBOT_VALIDATION}

TINYDNS_DATA_FILE=/etc/ndjbdns/data
TINYDNS_DATA_CMD=/usr/bin/tinydns-data
TINYDNS_ADDR={tinydnsがListenしているIPアドレス}

# _acme-challengeホスト文字列を作成
TXT_RECORD="'${DOMAIN}:${VALIDATION}:300"

# tinydnsのdataファイルに追記し、cdbファイルを作成する
echo ${TXT_RECORD} >> ${TINYDNS_DATA_FILE}
$(cd $(dirname ${TINYDNS_DATA_FILE}) && ${TINYDNS_DATA_CMD})

# 10秒待って、名前解決できるか確認。
sleep 10
host -r -t txt ${DOMAIN} ${TINYDNS_ADDR} | grep ${VALIDATION} > /dev/null 2>&1
exit $?

動作としては

  • DNS-01認証に必要なTXTレコード文字列を作成する
  • tinydnsdataファイルにTXTレコードを追記し、反映する
  • ちゃんと名前が引けることの確認をして処理を終了

というだけ。
ホントはもう少し真面目にNG時の処理とか入れる必要があるんだろうけど、その場合はcertbotが異常終了するのでわかるかなと思って省略した。
tinydnsがListenしているIPアドレスも自動取得しようかと思ったけど、めんどくさかったのでベタ打ち。

cleanup-hookスクリプト

以下のシェルスクリプトを、certbot-cleanup.shと名付けてcleanup-hookスクリプトとして使う。

#!/bin/bash

DOMAIN="_acme-challenge.${CERTBOT_DOMAIN}"
VALIDATION=${CERTBOT_VALIDATION}

TINYDNS_DATA_FILE=/etc/ndjbdns/data
TINYDNS_DATA_CMD=/usr/bin/tinydns-data

# _acme-challengeホスト文字列を作成
TXT_RECORD="'${DOMAIN}:${VALIDATION}:300"

# tinydnsのdataファイルから該当レコードを削除しcdbファイルを作成する
sed -i -e "/${TXT_RECORD}/d" ${TINYDNS_DATA_FILE}
$(cd $(dirname ${TINYDNS_DATA_FILE}) && ${TINYDNS_DATA_CMD})

exit 0

ここも大したことはやってなくて

  • 登録したTXTレコードの文字列を作成する(auth-hookと同じにしているので、一致するはず)
  • tinydnsdataファイルからマッチする行を削除し、反映する

ってだけ。

証明書の取得(フックスクリプト指定)

certbotでの証明書取得のときに上記のフックスクリプトを指定して実行する。

# certbot certonly --manual --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory --manual-public-ip-logging-ok -d \*.infrastudy.com -d infrastudy.com -m {自分のメールアドレス} --manual-auth-hook=./certbot-tinydns.sh --manual-cleanup-hook=./certbot-cleanup.sh

これがうまくいくと、Let’s Encryptの更新情報定義ファイルにhookスクリプトが登録されるので、更新のときに同じように自動処理が実行される。

# cat /etc/letsencrypt/renewal/infrastudy.com.conf
# renew_before_expiry = 30 days
version = 0.31.0
archive_dir = /etc/letsencrypt/archive/infrastudy.com
cert = /etc/letsencrypt/live/infrastudy.com/cert.pem
privkey = /etc/letsencrypt/live/infrastudy.com/privkey.pem
chain = /etc/letsencrypt/live/infrastudy.com/chain.pem
fullchain = /etc/letsencrypt/live/infrastudy.com/fullchain.pem

# Options used in the renewal process
[renewalparams]
authenticator = manual
account = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
manual_public_ip_logging_ok = True
manual_auth_hook = /root/certbot-tinydns.sh ←ここ
server = https://acme-v02.api.letsencrypt.org/directory
manual_cleanup_hook = /root/certbot-cleanup.sh ←ここ
pref_challs = dns-01,

Certbotの更新処理登録

CentOS7ってことで、systemdにそのへんをおまかせすることにした。

更新処理を実行するservice定義の登録

/etc/systemd/system/certbot.serviceを以下の内容で作成する。

このserviceが起動されると

  • certbot renewの実行
  • 新しい証明書を有効にするためにhttpdの再起動

をやってくれる。

[Unit]
Description=Let's Encrypt certificate renewal

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew
ExecStartPost=/usr/bin/systemctl reload httpd.service

更新serviceを実行するtimerの登録

/etc/systemd/system/certbot.timerを以下の内容で作成する。

こいつは週1で実行される。うちの環境の記録を見ると毎週月曜日に実行されてるね。

[Unit]
Description=Let's Encrypt weekly certificate renewal

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target

こいつを以下のコマンドで有効にする。

# systemctl enable certbot.timer

あとは放置しとくと勝手にserviceが実行されて更新処理が走る。実際に今回の更新でもちゃんと実行してるのが確認できた。

# systemctl status certbot.timer
● certbot.timer - Let's Encrypt weekly certificate renewal
   Loaded: loaded (/etc/systemd/system/certbot.timer; enabled; vendor preset: disabled)
   Active: active (waiting) since Fri 2019-02-08 13:25:51 JST; 2 months 30 days ago

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
# systemctl status certbot.service
● certbot.service - Let's Encrypt certificate renewal
   Loaded: loaded (/etc/systemd/system/certbot.service; static; vendor preset: disabled)
   Active: inactive (dead) since Mon 2019-05-06 00:00:02 JST; 4 days ago
  Process: 16232 ExecStartPost=/usr/bin/systemctl reload httpd.service (code=exited, status=0/SUCCESS)
  Process: 16197 ExecStart=/usr/bin/certbot renew (code=exited, status=0/SUCCESS)
 Main PID: 16197 (code=exited, status=0/SUCCESS)

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.

まとめ

certbotでのワイルドカード証明書の更新が自動化できて楽になりました。tinydnsを使った例も見当たらなかったので手探りでしたが、思ったとおりの処理が実現できたし、いい勉強になりました。

コメント

タイトルとURLをコピーしました