2026/05/28(木)Value-DomainのDNSを操作するユーティリティを更新した

更新日:
投稿日:

value-domain-dns-utilを更新したので、その記録とか、その感想の怪文章とか。

更新内容

ワイルドカード証明を発行するときに失敗する問題の修正

これまでDNS更新のタイミングの関係で偶々成功していたが、何度もやってると失敗することが多かったので、失敗しないようにした。

変更前

  • vd-dcr.pl
    • APIからDNSレコードを取得→レコードにACME TXTレコードがなければ追加、あれば上書き→編集後のレコードをAPIに送る

変更後

  • vd-dcr-auth.pl
    • APIからDNSレコードを取得→レコードにACME TXTレコードを追加
  • vd-dcr-cleanup.pl
    • cleanup:PIからDNSレコードを取得→レコードから今回登録したACMEレコードを除去→編集後のレコードをAPIに送る

モジュール化

lib/VdDnsUtil.pllib/VdDnsUtil.pmに変更し、shebangを削除。

並びに今回の変更で追加したユーティリティも.pmとして実装。

バージョニングの追加

スクリプトを叩いたときに依存モジュールと本体のバージョンが出るようにした。

対処が必要なこと

vd-dcr.plの改修に関わる影響のため、vd-dcr.plを使っている人だけに影響がある。

Net::DNS::Digの追加

rootユーザーで以下を流し、Net::DNS::Digを入れる。既にある場合は不要。

cpan Net::DNS::Dig

Certbotの走行コマンドの変更

certbotを以下の書式で再走行させる。具体的には--manual-cleanup-hookが増えてるので、そこを足す。これによってrenewが上書きされ、次回以降、正しく動作するようになる。

 sudo certbot certonly --manual -n \
   --preferred-challenges dns \
   --agree-tos -m <your-email> \
   --manual-auth-hook "/path/to/vd-dcr-auth.pl <value-domain-api-key> <root-domain> <optional:ttl>" \
+  --manual-cleanup-hook "/path/to/vd-dcr-cleanup.pl <value-domain-api-key> <root-domain> <optional:ttl>" \
   -d <FQDN>

また、従来であればValue-DomainのAPIに更新リクエストを投げてから3秒しか待っていなかったところ、今回はDNSの伝播を待ち、更に5秒待つように直しているため、以前より実行時間が伸びているため、もし何かしらのタイミング処理がある場合は見直しが必要。

今回の対応をした理由

切欠としてはモジュール化の作業中に不具合が発覚したところに始まる。

これまで共通部品として.plを使っていたが、Perlの再利用性について考えていた時に.pmの概念を知ったので改修することにした。その動作確認をしていたところ、ワイルドカードドメインの更新がやたらと失敗することに気付き、本格的に調査を始めたのが発端だ。

そして以前は次の順序で処理していた。

  1. 初回走行時にルートドメインのTXTレコードを追加する
  2. 二回目走行時にこのレコードをワイルドカード用のTXTレコードで上書きする

しかし、本来これは正しくなかった。ワイルドカードドメインを指定する場合、プライマリとセカンダリのTXTレコードが両方とも登録されている必要があるようだった。

ではなぜ今まで動いていたのか。これはDNS更新のタイミングの関係で偶々成功していただけと思われる。そして、私がそのことに気付いていなかったわけだ。動作確認で何度も実行しているとやけに失敗するので、今回この不具合に対応することに決めた。

そして前述の更新内容に繋がるわけだが、これまでは--manual-auth-hook向けのスクリプトしか用意しておらず、その中で「既存ACMEレコードがあれば上書きする」という擬似的なクリーンアップ処理を行っていた。これが前述の不具合を引き起こす要因となっていたため、次の改修を施した。

  • auth側からクリーンアップ処理(既存ACMEレコードの上書き)を除去し、純粋に「TXTレコードを追加する」だけの処理に変更
  • 新たに--manual-cleanup-hook用のスクリプトを追加し、authで登録したレコードをDNSから削除する処理を、こちらに新設した

これにより、プライマリ・セカンダリ両方のTXTレコードが正しく揃った状態でLet's Encryptの検証に渡され、検証完了後にきちんと後始末されるようになった。

参考までにコケていたときは次のエラーが出ていた。

Certbot failed to authenticate some domains (authenticator: manual). The Certificate Authority reported these problems:
Domain: hogezzz.lycolia.info
Type: dns
Detail: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.hogezzz.lycolia.info - check that a DNS record exists for this domain

Domain: hogezzz.lycolia.info
Type: unauthorized
Detail: During secondary validation: Incorrect TXT record "tiHFPEdgZ24aejUpmRkkzNvzUoisQn6mDv3d3xqHBNo" found at _acme-challenge.hogezzz.lycolia.info

あとがき

二日で15コミット変更行数は1,397行追加、217行削除という結構大規模な改修を入れたので、どこかにバグが潜んでいる可能性は捨てきれない。

何せ以前開発していたvalue-domain-dns-cert-registerから、このvalue-domain-dns-utilに移行するときにLLMにガっとやらせてから、今回に至るまでコードのメンテはほとんどLLMに任せている。

当然LLMが書いたコードはレビューして明らかに変なところは手直ししているが、精々コード全体の3-4割で、過半数はLLMが書き、LLMが直せる場所は私が直さずLLMに直させている。テストコードに至っては99%くらいLLMに書かせており、正直正しいコードが掛かれているのかまで見切れていない。以前なら血眼になってテストコードを書いていたはずなのに不思議なものである。

まぁでも実コードを手直ししたら落ちたので、たぶん機能していると思う。あとvalue-domain-dns-cert-registerから継承されたテストコードについては一通り見ているので、全く見ていないわけでもない。

LLMにコードを書かせると作るのは楽だがレビューが大変なのが困りものだ。あと私はAnthropicをBANされている関係で地味にお金がかかるのも困りものである。今回の改修には20USDほどかかっており、安くはない。もう少し手でコードを書くべきだとは思う。考える力も徐々に薄れていっている気がしており、人間として危機感を感じることもある。

ボケ防止のためにはAIに頼らず、自分の手を動かすのも大切なことだと思う。こういう話では、よくそろばん論を持ち出して、そろばんを使わなくなったが人類は劣化していない、電卓があるみたいなことを言う人がいるが、実際問題珠算ができる人は非常に速い速度で暗算ができ、頭の回転も速いとされているので、電卓があるから優位と言われれば、それは違うと思う。例えば電卓を取り出す時間で計算が終わっていたり、物事を予測しやすかったりするわけだ。

ある能力が減った分、別の能力が増えるみたいな考えもあるとは思うが、一人の人間としてAIにべったり過ぎるのもやや危険ではないかと思う。第一災害時どうするんだと思うし、登山などでは電波の届かない山の中ではそもそも使えないとかもあるし、ボケるボケないも寿命に影響するみたいな話は聞くので、考える力は大切だと思う。私は管理職とか企画職みたいなところよりは、現場人間でありたい気もしているし…。

なんだかオチが迷走してきたが、とりあえずvalue-domain-dns-utilはワイルドカード証明書も含めて安定して動くようになったはずなので、必要に応じて更新してもらえれば安定性が増すかもしれない。もしバグを踏んだら報告してもらえれば対応するかもしれません。

しかしこのスクリプトは動作確認が非常に面倒なので、できればもう手を入れたくないところではある…。

2026/02/26(木)Value-DomainでcertbotのDNS-01 challengeをやるスクリプトを書き直した中で知ったこと

更新日:
投稿日:

前の記事value-domain-dns-utilを作り、かつてのvalue-domain-dns-cert-register (vddcr)を焼き直したことを書いたが、あれからいくらか直し、学びも得たので、その記録として残す。

なお、今回行った調査によりvalue-domain-dns-utilのvd-dcr.plでは、複数ドメイン指定(-d hoge.example.com -d fuga.example.com)や、ワイルドカードドメイン指定(-d example.com -d *.example.com)が動作することを確認している。

certbotがauth-hookに渡す環境変数の調査

主に複数ドメインを指定する場合に環境変数に何が入るのか調べた。これはワイルドカードドメインを指定する場合、必然的に複数指定になるため。

ドメインを一つだけ指定したとき

--manual-auth-hookに指定したスクリプトが一回だけ走る。

入力

sudo certbot certonly --manual -n --preferred-challenges dns --agree-tos -m postmaster@example.com --manual-auth-hook "/path/to/script" -d hoge.example.com

環境変数

変数名
CERTBOT_DOMAIN hoge.example.com
CERTBOT_VALIDATION xxxxx
CERTBOT_ALL_DOMAINS hoge.example.com

ドメインを複数指定したとき

指定した回数分、--manual-auth-hookに指定したスクリプトが走る。

入力

sudo certbot certonly --manual -n --preferred-challenges dns --agree-tos -m postmaster@example.com --manual-auth-hook "/path/to/script" -d hoge.example.com -d fuga.example.com

環境変数

走行一回目

変数名
CERTBOT_DOMAIN hoge.example.com
CERTBOT_VALIDATION xxxxx
CERTBOT_ALL_DOMAINS hoge.example.com,fuga.example.com

走行二回目

変数名
CERTBOT_DOMAIN hoge.example.com
CERTBOT_VALIDATION yyyyy
CERTBOT_ALL_DOMAINS hoge.example.com,fuga.example.com

ワイルドカードドメインを指定したとき

ドメインを複数指定したときと似た挙動をするが、CERTBOT_DOMAINは二回とも同じものが入り、CERTBOT_ALL_DOMAINSも同じドメインが二個入り、CERTBOT_VALIDATIONだけ異なる値になる。

入力

sudo certbot certonly --manual -n --preferred-challenges dns --agree-tos -m postmaster@example.com --manual-auth-hook "/path/to/script" -d example.com -d *.example.com

環境変数

走行一回目

変数名
CERTBOT_DOMAIN example.com
CERTBOT_VALIDATION xxxxx
CERTBOT_ALL_DOMAINS example.com,example.com

走行二回目

変数名
CERTBOT_DOMAIN example.com
CERTBOT_VALIDATION yyyyy
CERTBOT_ALL_DOMAINS example.com,example.com

TXTレコードに永続性は不要で、証明書を作るときにだけあればよい

私は長らくDNSレコードにtxt _acme-challenge.bts "gfj9Xq...Rg85nM"のような値が存在し続けることに意味があると考えていたが、実際にはそうではなかった。

Challenge Types - Let's EncryptのDNS-01 challengeの仕様には次のようにある。

原文

After Let’s Encrypt gives your ACME client a token, your client will create a TXT record derived from that token and your account key, and put that record at _acme-challenge.<YOUR_DOMAIN>. Then Let’s Encrypt will query the DNS system for that record. If it finds a match, you can proceed to issue a certificate!

日本語訳

Let’s EncryptがACMEクライアントにトークンを渡すと、クライアントはそのトークンとアカウントキーから派生したTXTレコードを作成し、_acme-challenge.<YOUR_DOMAIN>に格納します。その後、Let's EncryptはDNSシステムにそのレコードを照会します。一致するレコードが見つかった場合、証明書の発行に進むことができます。

つまり、DNS-01 challengeにおけるTXTレコードはドメインの所有権を確認するための一時的な検証値に過ぎず、証明書の発行が完了すれば不要になる。

この仕様を知ったことで、ワイルドカードドメイン指定時の懸念が解消された。ワイルドカードでは_acme-challenge.example.comに対して--manual-auth-hookに指定したスクリプトが二度走り、そのままでは二回目で一回目のTXTレコードが上書きされるが、永続する必要がないなら問題にならない。

以前はワイルドカードドメインに対してTXTレコードを二個維持する必要があると考え、「_acme-challengeのTXTレコードがDNS上に二行以上あればワイルドカード用とみなして全削除してから追加、二行未満なら単純に追加」という判別ロジックを検討していた。

しかしこの方法では、前回のレコードが一行だけ残っている場合に破綻する。ワイルドカード指定では--manual-auth-hookに指定したスクリプトが二回走るので、次のようなことが起きる。

  1. 一回目:既存レコードは一行(前回の残り)なので「二行未満→追加」が選ばれ、計二行になる
  2. 二回目:既存レコードが二行になったため「二行以上→全削除して追加」が選ばれ、一回目で登録したばかりのレコードごと消えてしまう

このように既存レコード数という外部状態に依存した判別では、初期状態の違いによって正しく動かないケースが生じ、運用でカバーせざるを得なかった。

だが、TXTレコードに永続性は不要で、証明書を作るときにだけあればよいということが分かり、結果としてレコードを永続させる必要がないと分かったことで、このような判別ロジック自体が不要になった。

これによって例外処理をするスクリプトの作成も、それを動かすための運用も考慮しなくてよくなり、vd-dcr.plはシンプルな状態に保てるということが分かったので、とても良かったし、何よりcertbotへの理解が深まったのもよかった。

余談だがCertbotの振る舞いはRFC 8555に定められており、Automatic Certificate Management Environment (ACME)、つまり自動証明書管理環境とされているようだ。有志による日本語訳も存在し、日本語で読むこともできる

CRONTABは書かなくていい

以下のコマンドで証明書を発行した場合、CRONTABにもsudoを抜いた同じコマンドを書く必要がありそうだが、実はしなくていい。

sudo certbot certonly --manual -n \
  --preferred-challenges dns \
  --agree-tos -m postmaster@example.com \
  --manual-auth-hook "/path/to/vd-dcr.pl XXXXX example.com" \
  -d hoge.example.com

何故かというとタイマーとサービスが/usr/lib/systemd/system/certbot.timer/usr/lib/systemd/system/certbot.serviceとして登録されており、私の環境では毎日0時と12時に実行されるようになっていた。

サービスの生死は以下で確認できる。

systemctl status certbot.timer
systemctl status certbot.service

このサービスが何をしているかというと/etc/letsencrypt/renewal/*.confの設定ファイルをフェッチして証明書の更新をしてくれるようになっている。

つまりCRONTABを書く必要は、基本的にないのである。

なお/etc/letsencrypt/renewal/*.confの設定ファイルは証明書の発行コマンドを叩く度に増えるが、削除したい場合は、以下のコマンドで証明書など、諸々の関連ファイルごと削除できる。

# 例:sudo certbot delete --cert-name hoge.example.com
sudo certbot delete --cert-name <target-domain>

DNS-01 challengeの運用負荷を減らす、新しいDNS-PERSIST-01という方式の登場について

DNS-01 challengeについて調べる中で、DNS-PERSIST-01という新しいACMEチャレンジタイプの存在を知った。これはDNS-01の代替ではなく、DNS-01と併存する別の選択肢として提案されているものだ。

これはまだドラフトだが、今後導入される予定のDNSを利用した証明書の発行方式で、Let's Encryptでは2026年Q2に本番導入が予定されている。

DNS-PERSIST-01の利点は一度DNSに検証文字列を書けば、以降はDNSレコードを触らなくてよいというものである。DNSのAPIを都度叩かなくてよいため、運用面で非常に便利だといえる。当然、セキュリティ面では劣化がある。

欠点はACMEアカウント鍵が漏洩すると、第三者に証明書を発行されるリスクがあることだ。DNS-01 challengeでは更新の都度、ランダムな検証文字列を設定して検証するのでこの問題は起きない。

要するにDNSレコードを弄らない代わりに鍵を使う方式と、DNSレコードを弄る代わりに盗まれる物がない方式の二つになるということだ。

DNS-PERSIST-01でもセキュリティのために、ACMEアカウント鍵に有効期限を設けられるようだが、それだと本末転倒にも思う。勿論、そういうケースが役に立つ場合もあるだろうが、運用は大変そうだ。

2026/02/22(日)OpenWrtからValue-Domainに複数サブドメインのDDNSを行うツールを作った

Value-DomainのダイナミックDNSエンドポイントは60秒以内に叩くとエラーが返ってくるので、これを回避するためのDDNSの仕組みを作った話。

やったこと

まず、Value-DomainのDNS APIに対し、既存のaレコードをバルクで差し替えるためのツールとしてvd-ddns_v4.plを作った。

そしてhotplug.dにPPPoEインターフェースのIPが変わったときに、このスクリプトを蹴る処理を書いた。

この記事の前提構成

OpenWrtにIPv4用のPPPoEインターフェースがある。

やったこと

  1. 今回利用するのに必要なPerl周りをセットアップする。ストレージの空きが3.2MBほど必要
  2. value-domain-dns-util/root配下とか適当な場所に放り込む
    • opkg install openssh-sftp-serverでSFTPを導入しておくとファイル移動に便利
  3. vi /etc/hotplug.d/iface/40-pppoeとかして、OpenWrtのインターフェースが変化したときのHookを作る

     #!/bin/sh
     # デバイスが存在しなければ終了
     [ -n "$DEVICE" ] || exit 0
     # リンクアップでなければ終了
     [ "$ACTION" = ifup ] || exit 0
     # インターフェース名がPPPoEのものでなければ終了
     [ "$INTERFACE" = wanppp ] || exit 0
     # pppoe-wanpppのIPv4アドレスを取得
     pppoeaddr=$(ip -4 addr show pppoe-wanppp | head -2 | tail -1 | awk '{print $2}')
     # ログに吐く
     logger -t "DDNS - PPPoE IP" $pppoeaddr
     # DDNSもどきを叩く
     /root/vd-dns-util/vd-ddns_v4.pl 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' example.com $pppoeaddr hoge fuga piyo
     # 結果をログに吐きたいが何故か動いていない
     if [ $? -eq 0 ]; then
     logger -t "DDNS - UPDATE SUCCEED"
     else
     logger -t "DDNS - UPDATE FAIL"
     fi
    
  4. PPPoEインターフェスをRestart

  5. Value-Domainのコンパネで指定したドメインのaレコードが更新されていることを確認

備考

一般的にDNSレコードの浸透時間は更新前に浸透していたTTLに依存し、Value DomainのAPI経由で普段から操作している場合は120秒以下にならないため、最大120秒のダウンタイムが発生するが、理論上は公式のDDNS機能と大差ないはずと思われる。

aレコードのみの対応にしているのは私の環境だとIPv6は変動しないが、v4はそれなりの頻度で変わるためだ。

運用しているMastodonのv4が変わったのに気づかないまま疎通できないインスタンスが出てくることがしばしばあり、手動で対応するのが手間なのと、毎回一日くらい気づくのに遅れるので今回自動化に踏み切った。前々からやり勝ったのだが、中々腰が重く進んでいなかった。

そもそもこの手のものは監視システムで検知できて然るべきなので、おいおい監視システムの構築もしていきたいところだ。

2025/08/13(水)さくらのレンタルサーバーにLet's EncryptのDNS-01 チャレンジの証明書を持ち込む

さくらのレンタルサーバーにLet's EncryptのDNS-01 チャレンジの証明書を持ち込む方法。自動更新できないので実用性はないが、一時的に使う場合に有用。

前提条件

DNS-01 チャレンジの証明書を既に作っている。

やり方

  1. 発行した証明書をhomeに移す
    sudo cp /etc/letsencrypt/live/<サイトディレクトリ>/*.pem .
    sudo chown $USER:$USER privkey.pem fullchain.pem
    
  2. さくらのコンパネから設定するドメインのSSL設定を開く
  3. SSL証明書の種類を選択→独自SSL
  4. 秘密鍵にprivkey.pemをアップロードする
  5. SSL証明書インストールでfullchain.pemの中身を張り付けて登録する

2024/02/17(土)なりすましメール防御対策を取ろう

ここ最近なりすましメールが目立つのでなりすましメール防御対策を取りましょうという話。

なりすましメールが増えている一例

ここ数日の間にイラストレーターのIxy先生やMastodonインスタンス管理人のにょき氏といった一定の知名度を持つ人物を中心になりすましメールの被害にあっているようだ。

ここまで目立つのは余り見聞きしなかったので恐らくここ昨今のGMailとかの騒動を見たところで、なりすましメールを作れることに気づいた人物が愉快犯的に行為をしているのだろう。

取りうる防御策

個人レベルでできる対策としてはDNSレコードやメールサーバーの設定にSPF, DKIM, DMARCを設定することだ。これらの内容についてはGoogleによるDMARC を使用してなりすましと迷惑メールを防止するが詳しい。

さくらのレンタルサーバーを利用している場合は以下が参考になる。

さくらのレンタルサーバーを利用して外部DNSを利用している場合は、私が以前書いたValue-DomainのドメインをさくらのレンタルサーバーのメールでSPF, DKIM, DMARC対応させるが参考になるだろう。

SPD, DKIM, DMARCの三点を設定することで相手のメールサーバーがこれらに対応している場合になりすましメールを迷惑メールとして分類したり、メールの受信を拒否できるとされているため、設定することでなりすましメールを防御できる可能性が高まる。

メール送信に使っていないドメインに関しても以下のようなDMARCを設定しておくことで、悪意のある第三者によるなりすましを防げるだろう。

txt _dmarc v=DMARC1; p=reject; aspf=r; adkim=r

またメールサーバーを運用されている各位におかれては、SPD, DKIM, DMARCの三点を識別し、適切に受信メールをフィルタリングできる仕組みを導入して頂けると犯罪予告や迷惑メールによる被害を減らせる可能性があるので、是非とも導入を検討いただきたい。