投稿日:
私は2020年の末あたりからLet's EncryptのDNS-01 challengeをValue-Domainで達成するためのスクリプトであるvalue-domain-dns-cert-register (vddcr)の開発を行ってきていたのだが、メンテが辛くなってきたので、今回Shellscript(Bash)Perlでvalue-domain-dns-utilとして作り直した。その経緯と、開発中の学び、そして最後にどうやって作っていったかについて書いていく。
背景
一年半ほど前からTypeScriptに限界を感じてきたことや、当時の誤った単体テストへの偏執により、vddcrはメンテしたくない状態になってしまっていた。
そこで昨夏、つまり2025年の8月に式年遷宮、つまり全体的な書き直しを行ったのだが、個々の処理をする部品を作ったまではいいものの、それらを繋ぎ合わせるところで飽きてしまい、放置していた。vddcrはこの時点で事実上の放棄状態だった。
しかし、自宅サーバーの運用をしていく中で別の目的でValue-DomainのDNS APIと関わらなければならない必要が出てきたため、今回、vddcrの置き換えも兼ねて新しく作り直すことにしたのだ。
具体的には複数ドメインに対するDDNSをバルクでやりたい要求が生まれたが、Value-DomainにあるDDNS APIは一度に1ドメインしか処理できない上にレートリミットが60秒に1回と極めてキツく、複数ドメインに対して行うには余りにも遅延が大きかった。
例えば10ドメイン更新しようとすると10分もかかるわけで、これでは使い物にならないというので、DNS APIを直に叩いて一気に書き換える必要性が出てきたのだ。どうせ作るならvddcrが担っていたDNS challengeの機能も包含できる形にしたかったので、Value-DomainのDNS APIを便利に叩くためのツールキットとしてvalue-domain-dns-utilを作ることにした。
つまりvddcrの純粋な後継ツールというよりは、vddcrの役目もこなせるツールキットを開発し、vddcrと同等の機能を有するツールを実装サンプルとして同梱したわけだ。
Shellscriptにした理由
ものの数時間でPerlに直したので過去の話となった。移植性の関係でbashはイマイチだった。
ぶっちゃけそんなに大した理由はなくて、単に目の前にターミナルとテキストエディタがあってパッと組めたからという部分が大きい。実際ベースはパッと作れた。まぁ作って半年も放置していたのが…。ただまぁPerlの方が保守性は高いだろうし、移植性もあるだろうとは思うから、気が向いたら書き直すかもしれない。
ちなみに別言語に移植することの検討はvddcrでも行っていたが、最終的には候補になかったShellscript(Bash)での実装に落ち着いた。そもそもこの課題の存在を忘れていたので、検討すらしていなかったのである。
value-domain-dns-utilについて
今回作ったvalue-domain-dns-utilは、vddcrの単純な代替ではなく、Value-DomainのDNS APIを叩くのに便利なShellscriptの関数群だ。DNS challenge以外にも用が生まれたので、このような汎用的な形になっている。
DNS APIから現在のレコードを取得する関数や、取得したレコードから先頭一致のパターンマッチでレコードを取得する関数、レコードリストへの追加や置換をする関数、そして編集したレコードをDNS APIに送り戻す関数を備えており、DNSに関する一連の編集処理ができるように便利に作っている。
結果として、Value-DomainのDNS APIを叩く汎用機能があれば、それを流用してLet's EncryptのDNS-01 challengeもできるし、当然複数ドメインに対するDDNSをバルクでやることも可能になる。
学び
開発中に発見も多くあったので記していく。なんかこういうのまとめた記事かページ作りたいね。
echoでドルマーク始まりのシングルクォート文字列を作ると、その中の改行コードが展開される
例えば以下のようになる。これは一行で改行展開を書けて変数に入れられるので、インデントされているコードなどで物理的にへし折りたくないときなどに有用だし、文字列としてのコードが見えるのでわかりやすい部分もあると思う。
入力
#!/bin/bash
# ドルマーク始まりのシングルクォート文字列は改行が展開される
echo $'1:aaa\nbbb'
# つまりこれと同じ
echo "2:ccc
ddd"
# ダブルクォートの中の\nは展開されない
echo "3:eee\nfff"
# ドルマーク始まりのダブルクォートも展開されない
echo $"4:ggg\nhhh"
# 一行で改行展開を書けて変数に入れられるので便利
hoge=$(echo $'5:iii\njjj')
echo "$hoge"
出力
1:aaa
bbb
2:ccc
ddd
3:eee\nfff
4:ggg\nhhh
5:iii
jjj
変数の最後の一文字を削る
これは後方一致除去(${parameter%word})と呼ばれるものらしく、詳しくは調べていないが"hoge"に対してfugaを繋げ、"hogefuga"としたい場合に便利だ。
例えば以下のように書ける。
入力
#!/bin/bash
hoge='"hoge"'
fuga='fuga'
result="${hoge%?}$fuga"'"'
echo $result
出力
"hogefuga"
sedで改行を改行コードに置換(\n→\\n)
これは見つけた当時sedの記事にも書いているが、以下のようになる。
入力
cat <<'EOF' | sed ':a;N;$!ba;s/\n/\\n/g'
aaa
bbb
ccc
ddd
EOF
出力
aaa\nbbb\nccc\nddd
[]ではGlob展開できないが、[[]]では出来る
今のところこういった形式でGlobを書くことはないのだが、Sonnet 4.6にテストコードを書かせている中で発見したので書き留めておく。
入力
#!/bin/bash
hoge='aaabbbccc'
[ $hoge = *"bbb"* ]
result1=$?
[[ $hoge = *"bbb"* ]]
result2=$?
echo $result1
echo $result2
出力
1
0
あとがき
LLMとの作業分担
今回の開発でもLLMを活用したのでその辺り。
今回の開発では、8ada152~93d58ccのコミットを行った。最も古いコミットである8ada152ではテストコードを除き、私がフルスクラッチで書き、テストコードはClaude Code(Sonnet 4.6)に書かせた。
次の029a951では、私が書いたコードをClaude Code(Sonnet 4.6)に全体的に直してもらって私がレビュー・微修正するという開発形態をとった。それ以降のコミットはコメントなどの微修正なので、再び私が手動で書いている。
こうした分担により、設計の根底にある信条のようなものには私の色が濃く出ているが、実装上の問題点はClaude Codeによって大きく改善されている。特に029a951では、typoや単純な実装ミスの修正に加えて、bashの文字列処理に不慣れなことで生まれたエスケープシーケンスの山が整理された。jqの-rオプションの有無がまちまちだった部分も統一され、可読性がだいぶ向上したと思う。
とはいえ、Claude Codeの言うことを鵜呑みにはしていない。こちらから明示的に指示して直させた箇所もある。例えばテストコードのappend_recordやreplace_recordのアサートは元々Globで書かれていたのだが、より厳密にするために全文マッチに直させた。別にレコードの順序がどうなっていようとValue-Domain APIは気にしないどころか勝手にソートされるので実用上の意味はないのだが、こういうのは人としての拘りである。
昨今は過去の記事でもちょいちょい言及している通り、LLMを使った開発をそこそこしていて徐々に馴染めてきているので、その点も含めてよい経験になった。
抽象化した今回の設計と、過去の歪な設計を振り返って
抽象化して再利用性を高めた今回のツールキット方式と、単体テスト大正義とばかりに変な作りになってしまった前回の方式の振り返り。
過去のvddcrでは単体テストへの誤った偏執によりモジュールが具象的な単位で分割され、非常にメンテナンス性が悪く、再利用性もないものになっていたが、今回はちゃんと抽象化して、責務ごとに機能が切れたと思っている。このことによって再利用性が高く、使いやすいものが作れたと考えている。大した規模ではないとはいえ、部品を組み合わせて処理を組むには程よくいい感じだろう。
先ほどのことや、単純に機能を削ったこと、TypeScriptの複雑すぎる型管理や非同期処理から解放されたこともあり、コードもだいぶ見通しがすっきりした。ツールではなく、ツールキットという形で作ったことも奏功し、Value-DomainのDNS API周りのスクリプトは大分作りやすくなったと思う。将来的にバルクのDDNSスクリプトを書くのも楽になるはずだし、重い腰を上げて作れてよかった。
式年遷宮で起きる変遷について
式年遷宮をする以上、元の状態には戻れないという話があり、それが何故起きるのかみたいなところが分ったような、分からないような、そんな話。
しかし世の中には式年遷宮で全く違う姿になるものがある。今回作ったのはまさにそんな感じで、なんというか作者の気持ちがわかった気がする。
分かりづらい例えだが、FF14の外部ツールであるConcept Matrix(CMTool)がAnamnesisに変わったときは、全く別物になった気さえしたし、こういった式年遷宮によって元あったことは一応できるにはできるけど、複雑になったり、なんかかゆいところに手が届かなくなったり…みたいなのは他でもちょいちょいあると思っていて、今回vddcrをvalue-domain-dns-utilにしたのもまさにそうだと思う。
という背景があるのもあり、完全互換を目指したvd-dcr.shを同梱することで、この不便さを少しはなくそうとした。いやまぁ自分のためにやってるので、自分のためのことではあるのだが…。とはいえ、以前のと比べるとプラットフォーム別の互換性は落ちていると思うので、現時点だと動かない環境は増えている気がする。
あとがき2:結局Perlに変えた
動かそうとしてた環境の一つのシェルがashで、bashが使えないことに気が付いたのでSonnet 4.6に頼んでサッとPerlに変えてもらった。
関連記事
確認時のApache2のバージョンはApache/2.4.58 (Ubuntu)
Apache2のインストール
sudo apt -y install apache2
公開ディレクトリを触りやすくする
公開ディレクトリの所有者はデフォルトだとroot:rootで、そのままでは扱いづらいため自分自身でも手軽に扱えるように権限を調整する。
# 公開ディレクトリを見たりいじる為に自分をwww-dataに入れる
sudo usermod -aG www-data <自分のユーザー名>
# 公開ディレクトリを扱いやすくするためwww-data:www-dataに変える
sudo chown -R www-data:www-data /var/www
# 公開ディレクトリを扱いやすくするためにグループに全権限を付与する
sudo chmod -R g+rwx /var/www
mod_rewriteやmod_cgiを使えるようにする
CGIの実行に必要なPerlなどのランタイムは必要に応じて別途入れておく。PHPは一般的にCGIとして実行しないため、次項の方法で動かせるようにする。
sudo a2enmod rewrite
sudo a2enmod cgi
sudo service apache2 restart
PHPを使えるようにする
バージョンの部分は適宜書き換える。PHP本体をインストールしていない場合は別途インストールしておく(依存解決で勝手にインストールされるかもしれないが)
sudo apt install -y libapache2-mod-php8.3
sudo a2enmod php8.3
sudo service apache2 restart
完全には解消していないが、とりあえず日本語が打てるようになったので記す。
確認環境
Ubuntu 24.04.3 LTS
起きていた事象
Ubuntu Serverとして導入した環境をUbuntu Desktop(GNOME)に置き換え、Xubuntu Desktopにマイグレーションしたところ、Ubuntu Desktopでは問題なかった日本語入力ができなくなった。
状況
IbusとMozcが自動起動せず、手動で起動させても日本語が打てず、systemctl --user show-environmentを叩いたときに以下の内容が出る状況だった。
HOME=/home/hoge
LANG=ja_JP.UTF-8
LOGNAME=hoge
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
SHELL=/usr/bin/zsh
USER=hoge
XDG_RUNTIME_DIR=/run/user/1000
GTK_MODULES=gail:atk-bridge
QT_ACCESSIBILITY=1
XDG_DATA_DIRS=/usr/local/share/:/usr/share/:/var/lib/snapd/desktop
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
DISPLAY=:10.0
GSM_SKIP_SSH_AGENT_WORKAROUND=true
SSH_AUTH_SOCK=/tmp/ssh-80h84ldKtdNU/agent.2702
XAUTHLOCALHOSTNAME=
参考までに正常に動作する環境では以下の出力となる。こちらはUbuntu Desktop→Xubuntu Desktopと辿ってきており、Server由来要素がない環境だ。
HOME=/home/hoge
LANG=ja_JP.UTF-8
LOGNAME=hoge
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
SHELL=/usr/bin/zsh
USER=hoge
XDG_RUNTIME_DIR=/run/user/1000
GTK_MODULES=gail:atk-bridge
QT_ACCESSIBILITY=1
XDG_DATA_DIRS=/usr/share/xfce4:/usr/share/xubuntu:/usr/share/gnome:/usr/local/share:/usr/share:/var/lib/snapd/desktop
CLUTTER_BACKEND=x11
CLUTTER_IM_MODULE=ibus
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
DEBUGINFOD_URLS=$'https://debuginfod.ubuntu.com '
DESKTOP_SESSION=xubuntu
DISPLAY=:0
GDMSESSION=xubuntu
GDM_LANG=ja_JP
GPG_AGENT_INFO=/run/user/1000/gnupg/S.gpg-agent:0:1
GSM_SKIP_SSH_AGENT_WORKAROUND=true
GTK_IM_MODULE=ibus
GTK_OVERLAY_SCROLLING=0
IM_CONFIG_PHASE=1
LANGUAGE=ja_JP
PWD=/home/hoge
QT_IM_MODULE=ibus
QT_QPA_PLATFORMTHEME=gtk2
SHLVL=0
SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh
XAUTHLOCALHOSTNAME=
XAUTHORITY=/home/hoge/.Xauthority
XDG_CONFIG_DIRS=/etc/xdg/xdg-xubuntu:/etc/xdg
XDG_CURRENT_DESKTOP=XFCE
XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/hoge
XDG_SESSION_CLASS=user
XDG_SESSION_DESKTOP=xubuntu
XDG_SESSION_TYPE=x11
XMODIFIERS=@im=ibus
_=/usr/bin/dbus-update-activation-environment
対処法
/etc/environmentに以下の内容を追記し、OS起動ごとにWhinsker MenuからIbusの設定を叩いて起動させることにした。自動起動の実現は結構面倒そうだったので試していない。
GTK_IM_MODULE=ibus
QT_IM_MODULE=ibus
XMODIFIERS=@im=ibus
あとがき
本来あるべき姿で起動させたいが方法が全くわかっていないのが厳しい…。何より環境変数が圧倒的に足りていないので、色々不都合が起きてそうだ。
まぁ当該端末をデスクトップユースで使うことは滅多にないのでいいっちゃいいのだが…。
投稿日:
なんか昔のやり方が使えなくなってたので調べたメモ。
確認環境
| Env | Ver |
|---|---|
| OS | Ubuntu 24.04.3 LTS |
| Docker | version 28.1.1, build 4eba377 |
| Docker Compose | v2.35.1 |
| Mastodonのバージョン | v4.5.0-alpha.2 |
| Mastodonのコミットハッシュ | 06803422da3794538cd9cd5c7ccd61a0694ef921 |
手順
DEVELOPMENT.md#dockerの手順通りにやると行ける。
docker compose -f .devcontainer/compose.yaml up -d
docker compose -f .devcontainer/compose.yaml exec app bin/setup
docker compose -f .devcontainer/compose.yaml exec app bin/dev
コンテナへのアタッチ方法
# 方法1
docker compose -f .devcontainer/compose.yaml exec app zsh
# 方法2
cd .devcontainer
docker compose exec app zsh
VSCodeがあるならDockerやRemoteほげほげ系の拡張を使い、devcontainer-appにAttach Shellしてもよい。
検証ユーザーの作成
- 次のコマンドでユーザーを作る
./bin/tootctl accounts create hoge --email hoge@example.com --confirmed - ユーザーの承認を行い、必要に応じてパスワードを使いやすいものに変更する
rails console user = Account.find_by(username: 'hoge').user user.approve! # PW変更ここから user.password = 'password' user.skip_confirmation! # PW変更ここまで user.save! quit
フロントエンドのビルドとサーバー起動
bin/rails assets:precompile
bin/dev
トラブルシューティング
localhostではアクセスできるが、マシンのホスト名やhoge.testのようなローカルドメインでアクセスできない
.env.developmentにALTERNATE_DOMAINS=hoge.testの行を追加することで他のドメインでもアクセスできる。
この設定があるとMastodonを別環境で動かしててリモートからドメインアクセスする場合に便利。
ActiveRecord::PendingMigrationErrorなど、ActiveRecord関係のエラーが出る
コンテナの中でrails db:setupを叩くことで解決する。
Unable to load application: NameError: uninitialized constant LetterOpenerWebというエラーが出る
リポジトリルートにあるdocker-compose.ymlを使うと発生するので、.devcontainer/compose.yamlを使う事で解決する。
あとがき
検証用にバニラなMastodon環境が欲しくて作ってみたが昔と変わっていて地味にハマった…。
