どさにっきキャッシュレス 〜2019年3月中旬〜

by やまや
<< = >>

2019年3月11日(月)

KnotDNS

_ 昨年の IW2018 の資料が公開されたようで

_ 自分は Knot DNS の話をしたですよ。DNSSEC やりたいけどあんなの運用まわらん、とか、Let's Encrypt で DNS 認証させたいけど自動化できなくてめんどくさい、とか言ってる人いればぜひ Knot を使ってみてくださいませ。自動化できるよ。まあ、BIND でできないわけじゃないけど。いきない Knot に乗り換えるのが怖くても、資料にあるように、BIND やら NSD やら使い慣れたものを前段、後段に置いて三段構成にすれば、Knot をほとんど意識せず、これまでとほとんど変わらない感覚で自動化できるよ。

_ 資料に書こうか迷ったけど結局やめておいたネタ。

_ 資料では Let's Encrypt の dns-01 認証による証明書取得を自動化する方法について触れてるけど、そうはいってもうちのゾーンは商用の権威 DNS ホスティングに預けちゃってるもん、せっかく DNS の運用を手放して人まかせにできたのに、という場合でも、そのサービスでサブドメインの NS レコードを書けるのであれば Let's Encrypt の証明書取得は自動化できる。

_acme-challenge.example.com.	IN	NS	hoge.exmaple.com.
のように _acme-challenge だけ別サーバに委譲しちゃえばよい。委譲されたホストで dynamic update を受けつけるように Knot を設定しておけば(BIND でもいいけど)、certbot + rfc2136 plugin (lego でもいいけど)で Let's Encrypt な証明書の取得を自動化できる。

_ サブドメインだけとはいえ DNS を動かすなら運用を手放した意味ないじゃん、ということにはならない。なぜなら、_acme-challenge へのクエリなんて証明書取得の際に数回やってくるだけで、それ以外のときは無視してもまったく問題ないから。

_ つまり、この dynamic update な Knot は常時動かしておくのではなく、certbot の pre-hook/post-hook に起動/停止スクリプトを仕込んでおけばいいのだ。証明書取得にかかる数十秒の間しか DNS サーバが動かないのでふだんはリソース消費ゼロで、それ用のサーバを用意するなんてもったいないことをしなくても他用途のサーバで片手間にやらせておけば十分。動いてないときに親ゾーン側の _acme-challenge の NS が消えるわけじゃないので lame になるけど、誰もクエリを投げてこないのでこれも気にしなくてよし。

_ 試さずにたぶんできるはず、と言ってるわけではなく、動作実績もある。そこの環境では _acme-challenge の委譲先には AAAA レコードがあるだけで A がなく、そして certbot の pre/post-hook には Knot の起動/停止だけでなく IPv6 アドレスの付与/削除も仕込んである。ふだんは DNS の問い合わせに応答しないどころか ping6 にすら答えないが、証明書更新作業をおこなう数十秒の間だけ v6 only な DNS サーバとして動く(let's encrypt 側も v6 に対応してるので v4 いらない)。これでまったく問題なく Let's Encrypt の証明書を取得できている。

Hello, world 記号ゴルフ

_ 先日のものの環境依存性(できるだけ)排除版。POSIX な sh ならどれでも動き、どの OS でもたいてい動く。96バイト。

_(){ _=$@;}
_ /???;! _ /???/???/?${_##*/??}
${_##*/} \#-$?:-@[-_ \\$?$?$#,_-{<<_
#+::=$ _=@:*!
_

_ こないだは解説しなかったけどちょっと解説。

_ _(){ _=$@;} は引数を変数に代入するだけの関数。次の行で /??? を引数にこの関数を呼んでるけど、ふつーに _=/??? のように代入すると $_ の値はそのまま "/???" という文字列になってしまう。関数を通すことで、/??? という文字列そのものではなく、ワイルドカードにマッチしたパスを展開した文字列、つまり "/bin /dev ... /usr /var" を得ることができる。

_ bash だと関数の実行後さらに、特殊変数 $_ への値の代入がおこなわれる(関数で代入した値を上書きする)。$_ は直前のコマンドの最後の引数をあらわし、つまり今回の場合は "/var" になる。bash では $_ に値がセットされれば関数でなくてもかまわないので、関数を定義せず、"_" という関数呼び出しを ":" という何もしないコマンドに置き替えてもちゃんと動く。

_ ${_##*/??} は $_ から */?? というパターンに一致する部分を先頭から最長マッチで取り除いた文字列。$_ の中身は "/bin /dev ... /var" (bash 以外)または "/var" (bash) だから、*/?? を削除するとどちらの場合も "r" の1文字が残る。もし /web やら /www やらのようなディレクトリが存在する環境で実行すると、/var ではなく /web (/www) がの末尾1文字になってしまってうまく動かない。

_ この "r" を使って /???/???/?r という引数で再度関数を呼ぶ。複数のファイルにマッチし、内容も環境によって異なるけど、最後にマッチするのはどの環境でもたいてい /usr/bin/tr か /usr/man/tr のどちらかのはず。次の行で ${_##*/} としているので tr だけが残る。これもマッチしたパスのリストの最後が tr でないと動かない。

_ さらに2回目の関数呼び出しは ! が前置されているので、終了ステータスすなわち $? が 1 になる。また、引数なしで実行される前提なので、$# は 0。よって、"\\$?$?$#" は \110 であり、これは octal で "H" の ascii コードをあらわす。

_ 結果として、最後に実行されるコマンドは、以下のように tr にヒアドキュメントを食わせるだけのものになる。

tr '#-1:-@[-_' '\110,_-{' <<_
#+::=$ _=@:*!
_
実行すると、Hello, world! になる。この引数をうまいことやるともっと縮みそうだけどどうだろう???

_ 今回は /??? から r の1文字を拾い、そこから /???/???/?r で tr を作るという二段構成でやっているが、 先日のもっと短いけど Linux でしか動かないものは、/????/$$/../*-* というワイルドカードで /proc/<pid>/../sysrq-trigger にマッチさせ、そこに含まれる tr という文字列を直接拾っている。また、bash を前提としてるので関数を定義していない。


<< = >>
やまや