CVE-2012-どさにっき 〜2012年2月下旬〜

by やまや
<< = >>

2012年2月22日(水)

Apache 2.4 正式リリース

_ 2.4.1 出た。…2.4.0 は?

_ しばらく前からリリースされそうな気配が見えてたので、うちのサーバも 2.3 の最終版で設定のテストをしておこうかと思ったんだけど、間に合わずリリースが先になってしまった。ずいぶん前から評価目的ではいろいろいじってたので、とくにとまどうことなく移行できる目算はついてはいるんだが。このところなんか時間が取れないな。仕事は(いつもどおり)あまりしてないのになんでだろ。まあ、本気でやろうと思えばこの程度の作業に使う時間ぐらいは確保できるので、その程度のやる気しかないってことだが。

ルートサーバをでっちあげよう

_ global blackout の件、なんか Anonymous は おれ知らねーとか言ってるし、落とすの無理っしょ、とかいう 記事に対して いい指摘だとか言ってる。わしも落とせないという見解に同意なので楽観視してるけど、まあ乗りかかった船なので ルートサーバの複製を自前ででっちあげる件についてちと検証してみた。

_ 結論から言うと、この前書いたとおりの設定で問題なし。以下は蛇足。

_ まず、ルートのゾーンファイルを手に入れる。これにはいくつか方法があって、この前書いたように HTTP でダウンロードしてもいいし、ルートサーバのうちいくつかは AXFR を許可してるので DNS 的にゾーン転送してもいい (*1)。ルートゾーンは DNSSEC の不在証明が NSEC3 ではなく NSEC なので zonewalking で取得するという荒技もある。あと、root だけでなく逆引きとかで使う .arpa も *.root-servers.net の上にあるので、このゾーンも拾っておく。

_ ゾーンファイルを取得する過程で毒を入れられてるといけないので、取得したものがホンモノかどうか調べる。DNSSEC は公開鍵暗号に基づいているけど、同じく公開鍵暗号による HTTPS や SSH、あるいは dnscurve のようなものと大きく異なるのは、DNSSEC は通信路ではなくデータそのものを保護しているということ。どういうことかというと、DNS 以外の手段で取得したデータでも(極端な話、口頭で読み上げられたとしても)、必要な情報さえ揃っていればそれが真正のものかどうか検証することが可能ということ。HTTPS では逆立ちしてもできない芸当(設計ポリシーの違いなので、それが優れてるとかどうとかいうことではない)。ということで、拾ってきたゾーンファイルを DNSSEC 的に検証してやる。 YAZVSというツールを拾ってきて、依存してる perl モジュールもろともインストール (*2)して validation してみる。

> dig +dnssec dnskey . | awk '$5==257' > root.key	# トラストアンカー
> yazvs.pl -a root.key root.zone
Crypto Validation of root 2012021900
----------------------------------------------------------------------
OK: Parsed 4206 RRs from root.zone
OK: 1 trusted KSKs found
OK: Apex DNSKEY RRset validated
OK: 0 expiring RRSIGs found
OK: 0 bad RRSIGs found
OK: 399 good RRSIGs found

Comparison to current zone
----------------------------------------------------------------------
OK: Received 4206 RRs from b.root-servers.net
OK: Current serial 2012021901
DIFF: KSK 0 added, 0 removed, 1 unchanged
DIFF: ZSK 0 added, 0 removed, 1 unchanged
DIFF: RRSIG 84 added, 84 removed, 315 unchanged
DIFF: DS 140 added, 140 removed, 0 unchanged

Validation for root 2012021900 PASSED, 0 problems
…ということで、実際に今使われているものとは若干違いがあるようだけど、ホンモノであることが確認された(たぶん署名の有効期限が違うとかその程度)。

_ 確認できたところでこいつを BIND9 のキャッシュサーバに仕込んでおしまい。

zone "." {
	type master;
	file "root.zone";
	notify no;
};
zone "arpa" {
	type master;
	file "arpa.zone";
	notify no;
};
notify yes だとゾーンをロードしたときにホンモノのルートサーバに NOTIFY を投げてしまうので、これは明示的に止めておいた方がよさげ。この環境でパケットキャプチャしながら1日生活してみたけど、ルートサーバにはクエリがまったく飛んでなかったのでたぶん大丈夫でしょう。もちろん DNSSEC の validation も問題なし。

_ 同じことを unbound でやるのは、ちょとムズい。いちおう unbound でもゾーン情報を食わせることができないわけでもないけれど、unbound のローカルデータの機能は聞かれたことを答えるだけで、聞かれた A といっしょに聞かれていない RRSIG を合わせて答えるとか、存在しない名前を聞かれたら NSEC を返すとかの芸当はできないので DNSSEC まわりで不都合が起きると思う。試してないけどね。DNSSEC を使わないならうまくいくんじゃないかな、たぶん。

_ ゾーン情報はコンテンツサーバに置くものだ、キャッシュサーバが持つなんてけしからん!という原理主義者的には、上のような設定をしたコンテンツサーバの BIND に対して、以下のような設定をしたキャッシュサーバから参照させればよい。

-- bind
zone "." {
	type forward;
	forwarders { 127.0.0.1; };
};

-- unbound
stub-zone:
	name: "."
	stub-addr: 127.0.0.1
もちろん arpa ゾーンも同じように設定する。この方法なら unbound でも DNSSEC が問題なく動く。

_ キャッシュから参照させるコンテンツサーバとして BIND ではなく NSD を使う場合。できないはずはないけど、こいつをルートサーバに仕立てあげるには --enable-root-server つきで configure からやりなおす必要がある。めんどくさいので確認してない。それさえやっておけば、あとはふつーのゾーンと同じように root と arpa のゾーンをつっこむだけでいいはず。ちなみにルートサーバをやるためにわざわざコンパイル時に指定が必要なのは、特別な処理が必要だからというわけではなく、単にバカよけのためらしい(ソース確認済み)。ルートサーバをでっちあげようというのは、まあ、そういう行為ってことだ。

_ 注意点。署名有効期間が意外と短いので、放っておくと DNSSEC が期限切れでコケる。なのでわりと頻繁にゾーンファイルの入れ替え作業をおこなう必要あり。まあ、こんなでっちあげルートサーバを常用することはないと思うけど、今回みたいにルートサーバがコケたときのために事前に備えておく、なんてことをするときには、準備が早すぎるといざ本番で役に立たなくなってしまう。必要とされる直前に取得しないといけない。


(*1): bind9 にはゾーン転送用の単体ツールが存在しない。NSD の nsd-xfer がお手軽。bind8 の named-xfer は DNSSEC を知らないので、転送されたゾーンファイルには TYPExx な謎レコードが満載だろうけど使えないことはないはず(未確認)。どうしても bind9 だけでやりたければ zone "." { type slave; ... }; な named.conf を書いて named に取得させる。あるいは dig axfr . の出力を加工するか。
(*2): 厳密に言えばこのスクリプトに毒が入ってないかも調べる必要があるけど、そのへんは思考停止する。

2012年2月23日(木)

ルートサーバをでっちあげなくてもよかった

_ 教えていただきました。L.root-servers.net が 公式にゾーン転送サービスをやってるようです。なので、ルートその他のゾーンが欲しいときにはどこぞのルートサーバに直接ゾーン転送のリクエストを投げるより、xfr.lax.dns.icann.org、xfr.cjr.dns.icann.org にリクエストしましょう、と。へー、知らなかった。

_ で、試してみたら、AXFR 以外にも NS やら A やらのふつーの DNS クエリにもちゃんと応答してくれる。つまり、まさにルートサーバの複製そのものだった。ってことは、万が一ルートサーバが全滅した場合はルートのかわりにこいつらに聞きにいくようキャッシュサーバを 設定すればいいってことだな。自前でルートサーバをでっちあげる必要なんかなかった。


2012年2月26日(日)

Apache 2.4 Require

_ そんなわけで、自宅サーバを Apache 2.4.1 に入れ替えた。2.3 のときから動作検証はしてたので、それほどハマることもなく。まあ、細かいところまで詳細を把握してるわけではないんだけどね。

_ 新しく追加されたモジュールやら、新しくデフォルトになった event mpm やら、2.4 についてはネタはそれなりにあるんだけど、地味にアクセス制御まわりについて。

_ Require というディレクティブは、従来は Basic/Digest 認証で Require valid-user とか書く程度にしか使われてなかったけど、2.4 では認証以外にもアクセス制限全般で使われる万能ディレクティブになった。アクセス制限全般で使われるということは、接続元のクライアントで制限するのもとーぜん Require を使う。

_ ローカルからの接続のみを許可するにはこんな感じ。

Require ip 127.0.0.1 ::1
ほかに Require local みたいな書き方もできて、これはホスト自身の IP アドレスからの接続を許可する。

_ 特定のクライアントからの接続を蹴る。

<RequireAll>
    Require all granted
    Require not ip 192.0.2.1
</RequireAll>
デフォではいずれかの Require にマッチすれば許可されてしまうので、<RequireAll> を使ってすべての条件にマッチしないとダメと指示する必要がある。この認証コンテナを使うと、192.0.2.0/25 からはアクセス許可、192.0.2.128/25 からは認証にパスしたユーザのみ、それ以外は拒否、なんて複雑な条件が書ける。apache 2.2 まででは不可能だった。
<RequireAny>
    Require ip 192.0.2.0/25
    <RequireAll>
	Require ip 192.0.2.128/25
	Require valid-user
    </RequireAll>
</RequireAny>
<RequireAny> がデフォなので、いちばん外側のコンテナは不要だけど、まあ念のため。

_ 従来の Allow/Deny によるアクセス制限も可能だけど、mod_access_compat といういかにも過去からの互換性のためだけに残してあるよという名前のモジュールが必要になる。

_ とってもダサい <Limit>は、依然使えるけれども、使わなくてもいい方法が提供された。これまでこんなふうに書いていた。

<Limit GET POST OPTIONS>
    Allow from all
</Limit>
<LimitExcept GET POST OPTIONS>
    Deny from all
</Limit>
2.4 では Require を使ってこう書ける。
Require method GET POST OPTIONS
ただし、この制限にひっかかると 403 で蹴ってしまうようだ。405 Method not allowed を返してほしいんだけど…。AllowMethods という専用ディレクティブも新設されていて、これを使うとちゃんと 405 になるようだ(要 mod_allowmethods)。
AllowMethods GET POST OPTIONS
もちろん、<Limit> も引き続き使える。Require はアクセスの可否を決めるものだけど、<Limit> はアクセス制限以外にも使えるので、まったく不要になったわけではない。

_ 特定のリクエストヘッダがあったらアクセスを蹴る、なんてのにも Require が使える。まず、昔からの方法。

SetEnvIf User-Agent hoge block
Deny from env=block
Deny を Require で置きかえる。
SetEnvIf User-Agent hoge block
<RequireAll>
    Require all granted
    Require not env block
</RequireAll>
実は SetEnvIf もいらない。
Require expr %{HTTP_USER_AGENT} !~ /hoge/
さらにこんなふうにも書ける。
<If "%{HTTP_USER_AGENT} =~ /hoge/">
    Require all denied
</If>

_ apache 2.4 では 条件式が書けるようになったというのもひじょーに大きな変更。apache 2.2 までは SetEnvIf とか RewriteCond とかぐらいしか条件判断に使えるものがなくて、そのせいで複雑怪奇な mod_rewrite がムダに使われていたんだけれど、2.4 ではコアの機能として条件式が書けるのでこのあたりがだいぶすっきりする。rewrite がどうしても必要な場合でも、RewriteCond でこの新しい条件式が使えるようになったのでだいぶわかりやすく書けると思う。メソッド制限の例を <If> で書くとこんな感じになる。

<If "! %{REQUEST_METHOD} in {'GET', 'HEAD', 'POST', 'OPTIONS'}">
    Require all denied
</If>
<Limit> や Require method や AllowMethods では HEAD は GET に包含されるけど、この条件式ではそこまではされないので、明示的に HEAD を含める必要がある。もちろん Require 以外の設定も書けるので、403 ではなく 405 で蹴りたければかわりに Redirect 405 / とすればよい。


2012年2月29日(水)

なんでもかんでも PNG 画像にしちゃうスクリプト

_ というのを作った。もちろん、その PNG から元のデータを取り出すスクリプトも。 ダウンロード。このスクリプト自身を PNG に変換した例→ ←ちっこいな。
この画像ファイルから

% convert hoge2png.tar.gz.png ppm:- | dd bs=1 skip=33 count=1521 > hoge2png.tar.gz
として中身を直接取り出すことも可能(要 ImageMagick)。

_ 変換するのはデータそのものだけ。ファイル名だとかタイムスタンプだとかのメタ情報は完全に捨てるので、必要なら tar とかのアーカイブにしてから変換する。エラーチェックはするけどエラー訂正はしないので、画像を加工するとデコードできなくなる。

_ こんなことをして何がうれしいのかというと、 どんなファイルでも picasa にアップロードできるようになるってことだ。最近クラウドストレージサービスが大流行だけど、たいていどれも(無料では)容量制限がある。が、google+ あるいは picasaweb はいちおう写真専用だけれども2000ピクセルまでであれば無制限にアップロードできる。で、調べてみたら JPEG だけでなく PNG もアップロードできる仕様だった。PNG は可逆変換(←これ重要)なので、画像1枚につき 2000px x 2000px x 24bpp でだいたい 11MB までのファイルを無制限に保存できちゃうことになる。

_ が、あまり使い勝手はよくない。まぁ、思いついてから1時間もかからず作ったいいかげんなものだし。2000ピクセル制限に入りきらない巨大なファイルをてきとーに分割/結合してくれたり、picasa にアップロードした画像の管理とかするツールもあるといいんだけど、自分はもう飽きたのでやらない。誰かこのアイデアを発展してかっちょええツール作ってくれるとうれしい。ってゆーか、きっとすでに誰かが作ってそうな気がするけど見つかんなかった。


<< = >>
やまや