MyDNS×Certbot(DNS-01)でSSL証明書を全自動更新

■ はじめに:なぜDNS-01認証なのか
通常、Certbotでよく使われるのは80番ポートを利用した http-01 認証(ACMEチャレンジ)だが、本環境ではセキュリティ対策として 80番ポートおよび443番ポートへのアクセスを国内限定に絞っている。

Let’s Encryptの認証サーバーは海外からもアクセスしてくるため、この制限下では通常のWeb認証が通らない。
そこで、ポート開放状況に左右されずに証明書を発行・更新できる DNS-01チャレンジ を採用した。

また、ハニーポット(迷い込み用サイト)運用において、あえて正規ドメイン(sky.0t0.jp)用の証明書をハニーポット側にセットすることで、アクセス者に「ドメイン不一致の警告」を見せ、意図的に隙があるように演出している。
この正規証明書の更新プロセスを自動化した際の記録。

■ 1. 構築環境と課題
・対象ドメイン: sky.0t0.jp, deepsky.0t0.jp

・手法: Certbotの manual モード + MyDNS DirectEdit(PHPスクリプト)

・直面した課題: certbot renew を実行すると、設定ファイル (.conf) に dns と記述していても、なぜかhttp-01 認証(Web 経由のチャレンジ)を試行してしまい、更新に失敗する。

■ 2. なぜ設定ファイルの記述だけでは不十分なのか
検証の結果、Certbotの仕様による以下の挙動が原因であることが判明した。

・設定ファイルの限界: /etc/letsencrypt/renewal/xxx.conf 内に preferred_challenges = dns と書いても、コマンドライン引数で明示的に上書きしない限り http-01 認証が実行されてしまう。
Certbot 1.12.0 では、renew 実行時に認証方式が明示されていない場合、manual 認証であっても http-01 が選択される挙動が確認された。
renewal 設定ファイル内の preferred_challenges = dns だけでは、この挙動を抑止できないケースがある。

・解決策: challenge 選択の曖昧さを回避するため、実行コマンド側に直接オプションを付与する必要がある。

■ 3. 運用のポイント:certbot.timer に頼らない自動化
certbot.timer はディストリビューション標準の ExecStart 設定で certbot renew を実行するため、
認証方式を上書きするオプションを注入できない。
そのため、上述した「履歴優先」の仕様によって http-01 に戻ってしまい、DNS-01認証が動かない。

【対策】
systemd の override で ExecStart を書き換える方法もあるが、今回は運用の単純さを優先し、cron による明示実行を採用した。

・certbot.timer は放置(または停止)。

・cron を使い、自作のシェルスクリプトからオプションを強制指定して実行する。

■ 4. 最終的な自動更新設定

  1. 定期実行シェルスクリプト (/root/cert_renewal.sh)

#!/bin/bash

--preferred-challenges dns を明示して認証方式を固定し、静かに(quiet)実行する

/usr/bin/certbot renew --preferred-challenges dns --quiet

  1. Cron登録(週1回)

毎週月曜の深夜2時に実行

0 2 * * 1 /root/cert_renewal.sh

■ 5. 結果
--dry-run テストにて、sky.0t0.jp および deepsky.0t0.jp 両方の検証パスを確認。
ハニーポットにはこの正規証明書を充て、「正規のドメインだが証明書エラーが出る」という狙い通りの環境を、手放しで維持できるようになった。

■ まとめ(覚書)
・認証の固定: 自動更新時はコマンド引数で --preferred-challenges dns を指定し、Certbotの履歴優先仕様を上書きする。

・フックの活用: MyDNSのDirectEditスクリプトを manual-auth-hook に設定することで、DNSレコードの操作も自動化。