chroot
対 bind
ruid
と euid
Unix/Linux の wrapper 方式には次のような問題が含まれていた。
root
による SUID が含まれるために、リスクが高いhttpd
と wrapper の両方に発生し、整合性が必要なために、複雑・煩雑になる
これらの問題を全て一挙に解決できる可能性が生まれた。Bell-Labs からリリースされた Plan 9 である。
Bell-Labs は Unix を生み出したことでも知られている。その同じグループが Unix の問題点を克服するために Plan 9 を生み出した。Plan 9 は Unix の良い面を継承し発展させながら、Unix とは異なる革新的な技術に支えられている。その一つが per-process name space (プロセス毎の名前空間)である。
name space とはファイルの名前のなす空間であり、これがプロセス毎に異なっていても構わないようにしたのである。
(Unix など、これまでのOSでは、名前空間はどのプロセスから見ても同じである。もっとも Unix には 、その例外として、chroot の仕組みがあるが特権を必要とする。)
他のプロセスからは新たに生成された名前空間は見えない。つまり影響を与えない。そして、新しい名前空間の構成には特権が要らない!
さらに Plan 9 には名前空間を自由に構成するための道具が備わってる。仕切の無い「市場(いちば)」から、壁で区切られている「ショップ」方式で Web サーバーを構成できる可能性が開けたのである。
ここで言うサンドボックとはセキュリティ用語であり、悪意を含んだプログラムの次のような動作
「アクセス空間の限定」に関して、Pegasus では次の図のようになっている。
図で、real space とは、本来の名前空間である。httpd space とは httpd が見る名前空間であり、それは CGI プログラムが見る名前空間でもある。Plan 9 以外の OS では real space と httpd space は一致する。Pegasus では httpd space は、ユーザ毎に異なる。
例えばクライアントが alice
のドキュメントにアクセスしている場合には httpd
が見る名前空間は、alice
がホームページを公開するために必要とするファイルに限定されている。bob
についても同様である。
なお、ここで言う「ユーザ」とは Web ドキュメントを管理するシステムのユーザのことである。彼らが管理するドキュメントは、例えば筆者のサーバーであれば、
http://ar.aichi-u.ac.jp
(real host document)http://plan9.aichi-u.ac.jp
(IP based virtual host document)http://cpa.aichi-u.ac.jp
(name based virtual host document)http://ar.aichi-u.ac.jp/~arisawa
(personal document)
Pegasus が見る名前空間の基本構造は、real host, virtual host, user page を問わず共通である。
これによってサーバーコードが簡単になり、また CGI プログラムのポータビリティを可能にしている。
名前空間の基本構造を同じにできるのは、Pegasus の場合にはサンドボックス化を前提として設計されているからである。
この事情は Apache と対照的である。Apache は継ぎはぎ的な設計によって現在に至っている。real host,
virtual host, user page の構造はどれも互いに異なっている。この複雑さはシステム設定を難しくし、一方を立てれば他方が立たなくなるというジレンマを抱え込んでいる。
OS | 難易度 | メモ |
---|---|---|
Windows | できない? | 隠し仕様があるとか? |
Unix/Linux | 原理的には可能 実際的な困難性 |
chroot (特権必要) 環境準備が大変 |
Plan 9 | やさしい 必要なものが揃っている |
bind (特権不要) |
環境準備
mount --bind
は負担を軽減してくれる。しかし Plan 9 の bind
ほど器用ではない。
user alice
の CGI が実行される場合
プロセス | Apache | Pegasus |
---|---|---|
httpd | as http (可変)弱い権限 |
as web (固定)弱い権限 |
wrapper (suid を使う) |
as root 絶対的特権 |
|
CGI | as alice 強い権限 |
as web (固定)弱い権限 |
suexec
, cgiwrap
, sbox
注1: http
も web
も仮想ユーザ
注2: サービスは可能な限り弱い権限で行うのが原則 (破滅的な事故を防ぐ)
注3: alice
として実行される CGI は alice
の全てのファイルを破壊する威力を持っている
注4: Pegasus のシンプルな仕組みが設定ミスを防ぐ
web
として実行してもセキュリティ上の問題が発生しないのは、httpd
から見える空間をユーザ毎に閉じ込めているからである。Apache も閉じ込めを行えば同様に http
として CGI が実行可能なはずである。その方が注3で述べた問題が発生しない。
Unix/Linux では閉じ込めには特権が必要になり、wrapper は欠かせない。しかし実際問題として閉じ込めを困難にしているのは、閉じ込めた空間の環境準備である。
閉じ込めた空間には、CGI を実行する Perl や Python などのプログラムの他に、ライブラリなど(さらにダイナミックリンクライブラリなど)を含める必要がある。必要なものはスクリプト言語毎に異なる。Python に至っては /etc/passwd
が必要である! もっとも必要としているのは、その一部である。
Stein の SBOX の時代と異なり、現在ではディレクトリのハードリンクはサポートされていない。そのために、必要なファイルはユーザ毎にコピーをして準備しなくてはならない。これは現実的な方法ではない。
* | Windows | Unix/Linux | Plan 9 |
---|---|---|---|
管理者アカウント | administrator (変更可能) |
root |
glenda (変更可能) |
特権の範囲 | device file |
device file |
device 注2 |
run as (no password) |
任意のユーザ | 任意のユーザ | 任意の仮想ユーザ |
注2: ファイルサーバーにおいては、(記憶デバイスにファイルが作成されるので)特権は file に及ぶ。ただし特権を与える方法が Unix/Linux と異なる。ファイルサーバーのコンソールから特別に許可するのである。CPU サーバー(サービスを行っている) にはファイルに及ぶ特権プロセスは存在しない。
Plan 9 は CPU サーバーとファイルサーバーを分離することによって、root
のような特権プロセスを排除したのである。
chroot
対 bind
chroot
と Plan 9 の bind
の機能を比較する。
chroot | bind |
---|---|
名前空間の編成はできない | 必要に応じて動的に新しい名前空間を編成する |
名前空間の一部にプロセスを閉じ込める | 名前空間の一部にプロセスを閉じ込めることもできる |
管理者権限が必要 | 管理者権限は不要 |
他のプロセスは影響は受けない | 他のプロセスは影響は受けない 編成された名前空間は、他のプロセスからは見えない |
mount --bind dir1 dir2は(特権は必要であるが) Plan 9 の
bind
の機能の一部を実現している。これは Plan 9 ではbind dir1 dir2に相当する。これが実行されると
dir2
に dir1
と同じ内容が見える。(dir1
のサブディレクトリを含めて)dir2
は空のディレクトリである。空でない場合には、その内容が消去されるが(注1)、Plan 9 の bind
ではそのようなことはない。
dir2
が空で無い場合にはエラーにすべきであろう。
Plan 9 の bind はさらに多才である。
bind -a dir1 dir2とすれば、
dir2
と dir1
がミックスされるが、同じ名前があれば、dir2
が採用される。bind -b dir1 dir2の場合には、
dir2
と dir1
に同じ名前があれば、dir1
が採用される。a
は after、b
は before の意味である。その他のオプションも存在するが、ここでは採り上げない。bind dir2 / && cd /とすると
dir2
の中に閉じ込められる。
言語プロセッサレベルの対策としては、Perl の taint モードが存在するが、完全にガードを固めるのは難しいであろう。ユーザは自分の好みの言語プロセッサを使いたいであろう。そして、好みは多様である。従って言語プロセッサに依存しない対策が欲しい。願わくば機械語レベルでの対応である。
Apache について言えば、サーバーレベルで
mod_chroot
mod_unixd
mod_privilege
mod_chroot
はどうやら廃止の方向らしい。現在では代わりに mod_unixd
がある。興味深いのは、これを使う時に chroot
のオプションがある点である。これは Apache が特権を抱え込んで動く事を意味する。
クライアントからのリクエストを特権モードを抱え込んだままで処理するのはあまりにも危険である。Apache は太りすぎであり、どこに深刻なバグが含まれているかわからない。Apache が採り得る方法は、クライアントのリクエストを受け取る前に特権を必要とする chroot
を行い、その後で特権モードを解除するぐらいであろう。この場合、ユーザ毎の閉じ込めはできないが、我慢するしかないであろう。実際に、Apache の仕様では、chroot
を行うディレクトリは1つに固定されている。(もっとも Apache2.4.9 は、これすらバグがあって働いていない)
mod_privilege
は、suexec
の代わりのはずであるが、これはまだ実験的な位置づけである。mod_privilege
の存在は、Apache は将来 wrapper 方式から脱却しようとしていることを示唆している。
SBOX は全体として Unix 系の wrapper としては良くできている。もっとも wrapper と CGI の置き場所との関係は cgiwrap の方が良い。(SBOX は wrapper と CGI を共に cgi-bin
に置かせるので、ややこしくなる)
筆者は Pegasus が完成してから SBOX の存在を知ったのであるが、両者を比較してみるとディレクトリの基本構造に関する考え方が驚く程よく似ている。
$home/www/bin $home/www/cgi-bin $home/www/etc $home/www/lib $home/www/html $home/www/tmp $home/www/…
$home
と書いたのは Pegasus と比較するためで、Unix では $HOME
を意味する。サンドボックス化した場合には httpd
は $home/www
以下しか見えないので、この中に CGI の実行に必要な全てのファイルを押し込む必要がある。bin
は実行ファイル、etc
は /etc
の一部である。html
が閉じ込められた空間でのドキュメントルートである。
$home/www/bin $home/www/etc $home/www/docここに
doc
が閉じ込められた名前空間でのドキュメントルートであり、SBOX の html
に相当する。bin
は SBOX と同様に実行ファイルの置き場所である(ただしユーザが追加したいもののみを置けばよい)。etc
の位置づけは全くことなる。Plan 9 には /etc
は存在しない。Pegasus の etc
は httpd
のための管理データの置き場所である。etc
に置けるようになっている。つまりドキュメントの管理者も追加的に行えるようになっている。etc
は CGI からは見えないようになっている。bin
や etc
は必要に応じて作成する。cgi-bin
を持たないのは、筆者はああいうのは嫌いだからである。プログラムが複雑になるばかりで、それに見合うメリットは無い。(逆にディメリットはいろいろ考えられる)
CGI の実行に必要な他のファイル、例えば
$home/www/lib $home/www/tmp $home/www/…は自動的に(動的に)構成される。
tmp
は CGI が作業するためのディレクトリで、Stein は注意深くユーザ毎に割り付けている。他のユーザと作業スペースを共有するとセキュリティ上の問題が発生するからである。
クライアントはサーバーの(通常は80番窓口に)処理を依頼する。込み合った場合には、前の処理が終わらないうちに、次の処理依頼が来る。その結果、ユーザ alice
の処理が複数の CGI によって同時並行的に行われる事がありえる。SBOX のように作業スペースを共有していると、CGI が乗っ取られた場合にセキュリティ上の問題が発生する。
幸いな事に Plan 9 では RAM ディスクを自由に生成できる。Pegasus では作業スペースとして RAM ディスクが使われている。従って初めから準備する必要はなく、しかも、その内容は他のプロセスからは見えない。
いや、それ以上に多くのメリットを持っている。
RAM ディスクは(それを作成した)プロセスの消滅と共に消える。CGI が生成した作業ファイルを、わざわざ CGI 自ら消しに行かなくても、自動的に消えてくれるのである。不要な作業ファイルの削除は Unix/Linux では頭の痛い問題であるが、Plan 9 ではプログラムのエラーによって CGI がクラッシュした場合にも、RAM ディスクで作業をしていれば自動的に消える!
open()
システムコールのオプションに ORCLOSE
(remove on close) が存在し、これを使えばファイルがクローズされると自動的にファイルが消去される。
Apache に代表される Web サーバーのサンドボックス化に関して、この10年間、Unix/Linux では遅々として進歩が無かったのであるが、対照的に仮想マシンに関しては大きな進歩があった。
ruid
と euid
alice
の UID(user ID) を 1000 とすると、alice
が実行したプログラムは(通常は)ruid
とは read UID、euid
とは effective UID のことであり、C で書かれたプログラムの中で使用可能な名前である。
管理者(root) が実行したプログラムでは(通常は)
(ruid,euid) = (0,0)である。(0 は
root
の UID である。)
この意味について解説する。
状態 | 意味 | 補足 |
---|---|---|
euid=0 |
管理者特権を発揮しながら動く | アクセス権の設定を無視 |
ruid=0 |
管理者特権をリザーブしている | euid を 0 にできる |
(ruid,euid) = (0,1000)のプロセスは外見的には
alice
が実行したプログラムのように振る舞うが(つまり alice
のアクセス権に従う)(ruid,euid) = (0,0)のプロセスに化けることが可能である。
alice
がこれを実行すると(ruid,euid) = (1000, X)となる。ここに
X
はこのプログラムの所有者 uid
である。root
所有の実行可能プログラムの setuid 属性は特別に危険!
tip+
をリリースしたことがある。これは FreeBSD に添付されていた tip
を改良したものである。改良の主な目標は日本語を扱えるようにすることにあった。tip
のソースコードを吟味すると、酷いものであった。ruid
=0 のままで、ユーザに(ユーザが書いた)プログラムの実行を許していたのである。