ちゃんと動いたのが確認できたので、作業記録として公開してみる。
動機
- Lets’s Encryptでワイルドカード証明書を取得するにはDNS-01方式での認証が必要
- 更新のたびにDNS-01による認証が必要になる
- 認証に使うTXTレコードを毎回手動で登録、削除するのがめんどくさい
- 自分の手元では
tinydns
(ndjbdnsパッケージ)を動作させているが、自動更新に使える出来合いのフックスクリプトが見つからなかった。- ないなら自分でなんとかしよう
フックスクリプトの作成
ということで自作する。
大したことをやるわけではないので、お手軽にシェルスクリプトで実装する。
スクリプトで使用する変数
certbot
では--manual-auth-hook
と--manual-cleanup-hook
に独自のスクリプトを指定できる。
このとき、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レコード文字列を作成する
tinydns
のdata
ファイルに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と同じにしているので、一致するはず)
tinydns
のdata
ファイルからマッチする行を削除し、反映する
ってだけ。
証明書の取得(フックスクリプト指定)
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を使った例も見当たらなかったので手探りでしたが、思ったとおりの処理が実現できたし、いい勉強になりました。
コメント