読者です 読者をやめる 読者になる 読者になる

OpenBSD の chroot jail httpd で CGI を動かす(3) Perl 編

ついに3日目となりましたこの旅路。日記更新はずれこんでますがリアルではだいたい本当に3日かけてます。今は全部わかってから書いてるからすっとしてますが、手探り状態だった時はしんどくて…。

OpenBSDhttpdPerl CGI を動かす

はい。というわけで今日はこれです。でも、1日目2日目でだいたいやり方をつかんだので、実は簡単です。お題はもちろんデフォルトのインストールに入っていた、 /var/www/cgi-bin/printenv です。この内容は…、

# cat /var/www/cgi-bin/printenv
#!/usr/bin/perl
##
##  printenv -- demo CGI program which just prints its environment
##

print "Content-type: text/plain\n\n";
foreach $var (sort(keys(%ENV))) {
    $val = $ENV{$var};
    $val =~ s|\n|\\n|g;
    $val =~ s|"|\\"|g;
    print "${var}=\"${val}\"\n";
}

すげえ簡単。これがデフォで「全然動かない」んだから OpenBSD 憎いですよね。パーミッションが 000 になってるからこれを 777 にしたら動くのかって言えば全然動かないところが超クール。

というわけで動かします。

chroot jail 内に持ち込むもの確認

で、初日のルールに従って作業します。

  1. 検討
  2. ldd
  3. cp
検討

持ち込み品ですが、この場合、インタプリタである /usr/bin/perl が明らかです。これを機内持ち込みして良いかどうかの検討ですが、 Perl です。スクリプトを読ませるとかなりいろいろできちゃいますね。ちなみに httpd が乗っ取られた!と考えた時にこの perl も www で動くことになるんですが、 www が極力悪さをできないようにしてあげることが必要そうです。あとは root に setuid できるものは一切置かないと。

つまり、 ServerRoot 以下で www ユーザが Perl を使ってなにかできちゃうこと(特に書き込みかな)を制限・把握するということになるかと。

というわけで、基本 /var/www 以下のディレクトリは root の所有物とし、ディレクトリは 705 にし、 www はファイル作れなくする。各ファイルについては、 /var/www/htdocs 以下は 404 かなあ。 /var/www/cgi-bin/ 以下のインタプリタ実行ファイルだけは所有権を www にして 500 で。基本グループの運用はしない(だってユーザ僕ひとりのサーバだし)のでグループは 0 で。

# cd /var/
# chmod 705 www
# cd /var/www
# find . -type d | xargs chmod 705
# cd /var/www/htodcs
# find . -type f | xargs chmod 404

これくらいでよろしくお願いしますという感じです。結局のところ、この辺はやればやるほどセキュリティを下げるような行為にあたるわけで、何をどうコントロールするか自分で把握しておいてね、ということなんだと思います。こうしてログに残して管理し続けることが重要なんですね。

ldd

では第一の持ち込み品である /usr/bin/perl に ldd してみます。

# ldd /usr/bin/perl
/usr/bin/perl:
        Start            End              Type Open Ref GrpRef Name
        0000000000400000 0000000000802000 exe  1    0   0      /usr/bin/perl
        00000002097c8000 0000000209d03000 rlib 0    1   0      /usr/lib/libperl.so.11.0
        0000000206668000 0000000206a89000 rlib 0    1   0      /usr/lib/libm.so.5.0
        000000020b953000 000000020bd5c000 rlib 0    1   0      /usr/lib/libutil.so.11.0
        000000020a416000 000000020a8f5000 rlib 0    1   0      /usr/lib/libc.so.51.0
        0000000207500000 0000000207500000 rtld 0    1   0      /usr/libexec/ld.so

でたでた。これらを機内に持ち込んでいいか?。どうでしょうね。 ld.so はそもそもリンク用のライブラリだし、 libc libm libutil はプログラミングの教科書に出てくるような基本的なライブラリで、 libperl は Perl のなんでしょう(一応検索して確認した)。というわけで持ち込みは大丈夫そう。で、またこれを一個一個 ldd する…。言っておくけど、めんどいよ!

/usr/lib/libperl.so.11.0
# ls -l /usr/lib/libperl.so.11.0
-r--r--r--  1 root  bin  3346943 Jul 10  2009 /usr/lib/libperl.so.11.0
# ldd /usr/lib/libperl.so.11.0
/usr/lib/libperl.so.11.0:
        Start            End              Type Open Ref GrpRef Name
        0000000209a5e000 0000000209f99000 dlib 1    0   0      /usr/lib/libperl.so.11.0
# mkdir /var/www/usr
# mkdir /var/www/usr/lib
# cp /usr/lib/libperl.so.11.0 /var/www/usr/lib/
/usr/lib/libm.so.5.0
# ls -l /usr/lib/libm.so.5.0
-r--r--r--  1 root  bin  684882 Jul 10  2009 /usr/lib/libm.so.5.0
# ldd /usr/lib/libm.so.5.0
/usr/lib/libm.so.5.0:
        Start            End              Type Open Ref GrpRef Name
        0000000201f01000 0000000202322000 dlib 1    0   0      /usr/lib/libm.so.5.0
# cp /usr/lib/libm.so.5.0 /var/www/usr/lib
/usr/lib/libutil.so.11.0
# ls -l /usr/lib/libutil.so.11.0
-r--r--r--  1 root  bin  170775 Jul 10  2009 /usr/lib/libutil.so.11.0
# ldd /usr/lib/libutil.so.11.0
/usr/lib/libutil.so.11.0:
        Start            End              Type Open Ref GrpRef Name
        000000020e1ed000 000000020e5f6000 dlib 1    0   0      /usr/lib/libutil.so.11.0
# cp /usr/lib/libutil.so.11.0 /var/www/usr/lib
/usr/lib/libc.so.51.0
# ls -l /usr/lib/libc.so.51.0
-r--r--r--  1 root  bin  4582474 Jul 10  2009 /usr/lib/libc.so.51.0
# ldd /usr/lib/libc.so.51.0
/usr/lib/libc.so.51.0:
# cp /usr/lib/libc.so.51.0 /var/www/usr/lib
/usr/libexec/ld.so
# ls -l /usr/libexec/ld.so
-r-xr-xr-x  1 root  bin  49888 Jul 10  2009 /usr/libexec/ld.so
# ldd /usr/libexec/ld.so
/usr/libexec/ld.so:
# mkdir /var/www/usr/libexec
# cp /usr/libexec/ld.so /var/www/usr/libexec
/usr/bin/perl
# ls -l /usr/bin/perl
-rwxr-xr-x  2 root  wheel  9172 Jul 10  2009 /usr/bin/perl
# mkdir /var/www/usr/bin
# cp /usr/bin/perl /var/www/usr/bin/

とりあえず以上。やってることは全部一緒です。

動作確認

確認は、 /var/www に chroot して実行者を www にするということで

# chroot -u www /var/www /cgi-bin/printenv

このコマンド。うってみます。

# chroot -u www /var/www /cgi-bin/printenv
chroot: /cgi-bin/printenv: Permission denied

あ、やべパーミッション 000 のままだ。

# chown www:www /var/www/cgi-bin/printenv
# chmod 500 /var/www/cgi-bin/printenv
# chroot -u www /var/www /cgi-bin/printenv
Content-type: text/plain

HOME="/root"
LOGNAME="oogatta"
MAIL="/var/mail/oogatta"
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin"
SHELL="/bin/ksh"
(以下略)

きたきた!きました。結構感動。ブラウザからも…。表示できた!!うれしいです。理屈通りにやって物ごとがうまくいくのはプログラミングの中だけ!。感動です。

ちなみに、同様のことは検索するとチラホラ出てきて、

Quick Perl setup within Apache Chroot of OpenBSD

ここなんか参考になります。

ここを見るとまだ他に jail 内にコピーしてるものがあります。要するに、今回は Perl のモジュールを何も使ってないサンプルですが、使うとそのモジュールごとにおなじことをやらんといかん!ということみたいですね。

# systrace -A perl <scriptname.cgi>

も役立つと書いてありますね。やってみると…。うわあ、さっきのサンプルでも、

# systrace -A chroot -u www /var/www /cgi-bin/printenv
# cat /root/.systrace/cgi_bin_printenv
Policy: /cgi-bin/printenv, Emulation: native
        native-issetugid: permit
        native-mprotect: prot eq "PROT_READ" then permit
        native-mmap: prot eq "PROT_READ|PROT_WRITE" then permit
        native-__sysctl: permit
        native-fsread: filename eq "/<non-existent filename>: /var/run/ld.so.hints" then permit
        native-close: permit
        native-fsread: filename eq "/<non-existent filename>: /usr/libdata/perl5/amd64-openbsd/5.10.0/CORE" then permit
        native-fsread: filename eq "/usr/lib" then permit
        native-fstat: permit
        native-fcntl: cmd eq "F_SETFD" then permit
        native-getdirentries: permit
        native-fsread: filename eq "/usr/lib/libutil.so.11.0" then permit
        native-read: permit
        native-mmap: prot eq "PROT_NONE" then permit
        native-mmap: prot eq "PROT_READ|PROT_EXEC" then permit
        native-mmap: prot eq "PROT_READ" then permit
        native-fsread: filename eq "/usr/lib/libm.so.5.0" then permit
        native-fsread: filename eq "/usr/lib/libc.so.51.0" then permit
        native-fsread: filename eq "/usr/lib/libperl.so.11.0" then permit
        native-munmap: permit
        native-sigprocmask: permit
        native-mprotect: prot eq "PROT_READ|PROT_WRITE" then permit
        native-sigaction: permit
        native-getpid: permit
        native-fsread: filename eq "/etc/malloc.conf" then permit
        native-mprotect: prot eq "PROT_NONE" then permit
        native-getuid: permit
        native-geteuid: permit
        native-getgid: permit
        native-getegid: permit
        native-fsread: filename eq "/dev/arandom" then permit
        native-gettimeofday: permit
        native-ioctl: permit
        native-lseek: permit
        native-fsread: filename eq "/cgi-bin/printenv" then permit
        native-write: permit
        native-exit: permit

うわー、あるよー…。 non-existent filename とか…。/var/run/ld.so.hints は ldconfig で作られるやつですよね。そりゃ無いな。これはなんとかしないと速度に関わるのかな。 /usr/libdata/perl5/amd64-openbsd/5.10.0/ は Perl モジュールパスだ!。これは移さないとだめだ。上記サイトではまるごと移してるなあ。まるごとはちょっと気が引けますね。でも CORE くらいは必須か。明日やろう。

/etc/malloc.conf と /dev/arandom に触りにいけてるのはなんでかなあ。例によってカーネルがやってんのか?わからない。わからないこともある。

とりあえず、使うモジュールを一個増やしたら、 ldd と systrace で探検していく必要がありそうですね。めんどいね!。でも、これが「セキュアな環境を作る」ということなんだと思いました。 OpenBSD 製じゃない機内持ち込み品が増えれば増えるほど、運用負荷も高くなるわけだし。 OpenBSD 製のファイルってよほどのことがないとアップデートかけることないけど、そうじゃないファイルは「いつでもアップデートできる」という姿勢で作られているわけだし。パッケージでインストールした場合もちゃんと jail 内に複製してあげないといけない。テオ様僕がんばるよ。

というわけで、 Perl 編は案外さらっと終了です。次はいよいよ Tokyo Promenade だ!