最近Gentooのrsyncサーバが乗っ取られた。 これはrsync 2.5.6にバッファオーバフローを起こさせて落とした、というやつ だったが、これは rsync のコーディングの問題というよりも、 デーモンモードで動かすときのデフォルトの思想に問題がある。
デーモンモードで動かすとき、つまり、rsync --daemon
するときは、rsyncd.conf
に定義された配布ディレクトリ
に chroot() して、配布ディレクトリ以外のファイルを破壊しないようにする、
という配慮がデフォルトでONになっている。これが災いした。
rsyncdはポート873で待ち受けするため、起動時にはroot(UID=0)でなければ ならない。さらに、その後でクライアントの接続を許し、リクエストに応じた 配布ディレクトリにchroot()するためには、クライアントのやりとりが始まって からしばらくはUID=0でいつづけなければならず、この間にバッファオーバフロー を狙われてそれが成功したら文句無しでrootを奪われてしまうことになる。
結局のところ、ネットワークサービスデーモンで、クライアントの接続をう けたあとでchroot()するようなプログラムは、どれもこれもその危険性を内在し ていることになる。「このプログラムは自動的にchroot()します」と書いてあっ たとした場合、chrootという言葉で無条件に安全だと判断してはいけない。
安全なのは、
を先に済ませてから即座にUID=0から脱却し別の一般ユーザIDにsetuidした のちに
もの。逆に、安全でないのは、
ものである。まさに rsync --daemon の
デフォルトがこれで、これを回避するためには rsyncd.conf
で
use chroot = no
としておけば良い。ただし、これでも rsyncd の場合は不十分で、 ポート873に接続を受けて
最初のリクエストを受けるときはrootのまま動いている
という甘さがある。今回の事件が契機になって、rsyncがバッファオーバーフ ローを起こす可能性はほとんどなくなってはいるであろうが、 「ルートディレクトリでルートユーザのまま動いている」という古風な 状態が存在することには変わりない。
rsyncdに限らず、「1024未満の特権ポートを使うけれどもrootである必要の ないデーモンプログラム」はこのような順番で動かすべきだ。
これを汎用的に行なえるのが chroot コマンドと tcpserver の組み合わせで ある。tcpserverでは最初にroot権限でポートを確保したら即座に別UIDに移行 できるオプションがあるのでこれを利用する。まとめると、以下のような シェルスクリプトになる。
#!/bin/sh exec env - \ PATH=/usr/local/bin:/usr/bin:/usr/sbin:/bin \ chroot /somewhere/to/chroot \ envuidgid nobody \ tcpserver -vR -c40 -U 0 rsync rsync --daemon --no-detach 2>&1
chroot空間 /somewhere/to/chroot
内の usr/local/bin など
に rsync, envuidgid(daemontools) や tcpserver(ucspi-tcp) がインストール
してあるものとする。--no-detach は daemontoolsのsupervise経由で起動する
ためにつけてみた。
このようなコマンドの組み合わせで起動すると、ポート873で待ち受けを行な うプロセスは
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND nobody 65459 0.0 0.1 916 504 p5 I 3:36PM 0:00.02 tcpserver -vR -p -c40 -U 0 rsync rsync --daemon --no-detach
のように nobody 権限になる。よろしい。