Fedora Core 3 (kernel-2.6.x, glibc-2.3.x) では前ページの Jail ユーティリティで環境を作ると正常に機能せずログイン不能だったため探求した方法だ。
PAM (Pluggable Authentication Modules) は Linux をはじめ様々な UN*X で使用されている認証フレームワーク。アプリケーションが PAM に対応していればだが、そのアプリケーションが呼び出されると同時に認証部分を引き受け、設定によって指定された「認証モジュール」を動的にロード。動作の可否だけに限らず、認証の方法 (例えばデータベースサーバとの連携) や、認証直後の御沙汰をコントロールすることもできる。そうしたモジュールの中に pam_chroot というものがあり、それを利用すれば、特別なユーティリティを使ったりアプリケーションを chroot 対応にコンパイルしなおすといったこと無しに chroot が実現できる。PAM に対応しているプログラムには、通常のログインでコールされる login はもちろん、su や ssh (scp, sftp 含む) なども含まれる。
ここでは、一部のユーザのログインと ssh 接続を chroot 化することを目的とし、手動で chroot 環境を構築する手順を解説する。内容には前ページと重複する部分がかなりあるが、 chroot の仕組みを理解する上で欠かせないので改めて記述している。
以下設定例の前提 | chroot_dir: jail されるユーザ: jail されるユーザのグループ: hanako の HOME: |
/home/jails/ hanako hanako /home/jails/home/hanako |
※ 前ページとの違い: ユーザ (hanako) には専用のグループ (hanako) を割り当てることにした。
chroot_dir に必要な基礎ディレクトリを以下のように作成する。パーミションは本来のディレクトリ属性に合わせる。個別のユーザホームディレクトリはこの段階ではまだ作らない。
/home/jails/┐ bin/ dev/ etc/┐ ld.so.conf.d/ security/ home/ lib/┐ tls/ usr/┐ bin/ lib/┐ locale/ libexec/┐ openssh/ sbin/ share/┐ locale/ tmp/ var/┐ tmp/
主な基本プログラム
bin/ | bash,sh,cat,chgrp,chmod,chown,cp,ln,ls,mv,pwd,rm,mkdir,rmdir, touch,more,grep,egrep,hostname |
usr/bin/ | groups,id,test,[,head,tail,less,dircolors |
※ オレンジ色のものはややオプショナル
まずは共有ライブラリのことは考えずに、本体だけをコピーする。基本的にはシンボリックリンク (例えば sh や egrep) はシンボリックリンクのままコピーし、コピー後に確認して、リンクが切れていたり chroot_dir 外へのリンクだったりした場合のみ実体としてコピーする。ただし ssh, scp, vi といった複雑なプログラムだけは、必要なライブラリや関連ファイルを追尾するのに strace を併用したほうがいいようなので、前ページで紹介した Jail 付属の addjailsw を使って、後で、ライブラリもひっくるめてコピーすることにする。コピーコマンドはこんな感じ:
root# cp -dp /bin/{bash,sh,cat,..以下省略..} /home/jails/bin
less は基本プログラムのうちでも少し特殊な存在で、 terminfo 定義ファイルの手当てが必要。
前行程でコピーしたプログラムに応じて、必要な共有ライブラリを ldd で調べ、 chroot_dir にコピーする。上記の必須プログラム類なら strace を使う必要はなかった。以下の調査/コピー手順は、今後の再構築の機会に備え、ldcopy というシェルスクリプトに組み立てたので、それを使っていただければ簡単に行える。しかし、仕組みを理解しておくべきだと思うので一応解説しておこう。
下記は、 ls に必要な共有ライブラリを調べる例。
root# ldd /bin/ls
linux-gate.so.1 => (0xb7f5e000)
librt.so.1 => /lib/tls/librt.so.1 (0x005e4000)
libacl.so.1 => /lib/libacl.so.1 (0x00abe000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00c76000)
libc.so.6 => /lib/tls/libc.so.6 (0x00967000)
libpthread.so.0 => /lib/tls/libpthread.so.0 (0x00b97000)
/lib/ld-linux.so.2 (0x0094d000)
libattr.so.1 => /lib/libattr.so.1 (0x00c86000)
ファイルとして存在する共有ライブラリの ldd 出力は `登録名 => ファイル実体 (アドレス)' という形式になる。「ファイル実体」を chroot ディレクトリ内にコピーすればいいわけだ。一番上の linux-gate.so.1 は「実体」が無い、つまりカーネルにスタティックコンパイルされたライブラリなわけで、「そんなファイルどこ探しても無いじゃないか!」と悩む必要はない。 ( ldd の出力フォーマットはディストリビューションやバージョンによって異なるようなので、詳しくは ldd の man ページなどを参照してほしい)
全部実体としてコピーすると無駄が起こるため、先にそのライブラリがシンボリックリンクかどうか調べるか、リンクはリンクとしてコピーしてみてから点検して、必要ならば実体をコピーする。これはかなり好みの影響する話だが。例を示す:
root# cp -dp /lib/tls/{librt.so.1,libc.so.6,libpthread.so.0} /home/jails/lib/tls
実はこれら 3 つはシンボリックリンクなので、リンクを辿ってコピーする必要がある:
root# cp -p /lib/tls/{librt.so.1,..省略..} /home/jails/lib/tls
ldcopy は上記のライブラリ検索とコピーを BASH スクリプト化したものだ。それ以外に特に込み入ったことはしていない。ライブラリをコピーする上で目的地に存在しない枝フォルダは自動的に作成されるので、前述のように lib と lib/tls くらいを作っておけば充分だ。使い方は --help または -h オプションを与えれば見られるが、簡単に説明しておこう。
引数の意味:
chroot 環境の基底ディレクトリを指定。基底ディレクトリが未作成の場合は即エラー exit する。 | |
--follow | ldcopy は、デフォルト挙動ではライブラリをありのままコピー (cp -dpf) し、その結果できたリンクが破損リンクとなった場合はその分だけ実体としてコピーしなおすようになっている。 --follow オプションを使用すると、コピーコマンドが `cp -pf' に切り替わり、いきなり実体をコピーする。 |
--test | コピー先ディレクトリの作成 (存在しなかった場合) やライブラリのコピーは行わず、処理を「寸止め」。発行されるはずのコマンドのプレビューと、コピー先ディレクトリにリンク切れシンボリックリンクがないかのチェックのみ行う。 |
/path/to/program | 調べたいプログラムを指定する。 |
ssh, scp といった複雑なプログラムは、必要な周辺ファイルを strace を使って調べたほうが確実。もっと手軽にやりたければ、 Jail 付属の addjailsw スクリプト (perl) を使ってもいい (やり方は前ページを参照)。 ssh のサブプログラムである /usr/libexec/openssh/sftp-server のコピーもお忘れなく。
glibc ライブラリの一部が、ここまで行程をこなしてもまだコピーされていないかも知れない。下記のものを確認して、無ければ追加コピーしておく。
root# cp -dp /lib/libnss* /home/jails/lib
less など、 ncurses ライブラリを使用するプログラムを chroot 環境へコピーした場合は terminfo 定義ファイルも要る:
root# mkdir /home/jails/usr/share/terminfo root# cp -a /usr/share/terminfo/* /home/jails/usr/share/terminfo
必要なファイルは概ね下表。 addjailsw を使った場合は既に自動的にコピーされたものもあるかも知れない。注意の必要なものは個別に解説することにする。オレンジ色のものはオプション的だが、あれば、環境変数がしっかりしたり ls の表示が色分けされたりと chroot 環境が充実する。
etc/ | bashrc, ld.so.conf, ld.so.cache, nsswitch.conf, passwd, group, shadow, services, localtime, profile, profile.d/*.sh, termcap, DIR_COLORS |
etc/ld.so.conf.d/ | * |
usr/lib/locale/ | locale-archive |
usr/share/locale/ | locale.alias |
dev/ | null, tty, urandom |
passwd, shadow, group はシステムからそのままコピーしてから編集する。システムサービス絡みのアカウント類は、どれを残すか少々悩む。筆者もとことん煮詰めたわけではないので断言はできないが、まあ、こんなところか。どんな環境、どんなプログラムを使うかによっても状況は変わってくるので、必要に応じて煮詰めていただきたい。
passwd | shadow | group | 補足説明 |
---|---|---|---|
root bin daemon adm nobody dbus vcsa nscd sshd rpc rpcuser nfsnobody apache xfs gdm htt canna |
root bin daemon adm nobody dbus vcsa nscd sshd rpc rpcuser nfsnobody apache xfs gdm htt canna |
root bin daemon sys adm tty disk mem kmem wheel lock nobody users dbus floppy vcsa nscd sshd rpc rpcuser nfsnobody apache xfs gdm htt canna |
|
コピーしてくるのではなく mknod で作成する。まず、念のため当該デバイスファイルのタイプを調べる:
root# file /dev/{null,tty,urandom}
/dev/null: character special (1/3)
/dev/tty: character special (5/0)
/dev/urandom: character special (1/9)
例えば上記 /dev/null ならばキャラクターデバイスのメジャー 1/マイナー 3 と分かるので、以下のようにしてデバイスファイルを作成する。パーミションも ls で調べて、それを -m オプションで必ず指定すること:
root# mknod -m 0666 /home/jails/dev/null c 1 3 root# mknod -m 0666 /home/jails/dev/tty c 5 0 root# mknod -m 0644 /home/jails/dev/urandom c 1 9
加えてもうひとつデバイスファイルが必要。ログを syslog へ渡すためのソケット /dev/log だ。ただしこれは作るのではなく syslogd に作らせる。それには、syslogd の起動時にオプション `-a /home/jails/dev/log' を渡す (Solaris では -p らしい)。 RedHat系では /etc/sysconfig/syslog の SYSLOGD_OPTIONS 変数に加えておけば、syslogd の init スクリプトが読み取ってくれる。既に他のサービス (例えば BIND) のために -a オプションが使われていても大丈夫。標準的な Linux カーネルでは 19 個まで指定できるようだ。例えばこんな指定になる:
SYSLOGD_OPTIONS="-a /var/named/dev/log -a /home/jails/dev/log"
指定後 syslogd を再起動すれば dev/log ソケットファイルができているはず。
まだ存在しないユーザならば、まずシステム上にユーザを作成する。
root# useradd -d /home/jails/home/hanako hanako root# passwd hanako
これでシステム本来の /etc/passwd, group, shadow に hanako の情報が入り、/home/jails/home/hanako(0700) ディレクトリが作られ、スケルトンファイルもコピーされたはず。
コピーされたスケルトンファイルのうち .bash_logout だけは、このスクリプトのためだけに余分なプログラムをコピーする必要が生じてしまうため、特に理由がなければリネームするか削除する。
まずシステムの /etc/passwd ファイルを下記のように修正。 Jail の時とは少し異なるので注意。 ID 999 は例に過ぎない。
hanako:x:999:999::/home/hanako:/bin/bash
そして、この行を /home/jails/etc/passwd ファイルにそのまま加える。つまり PAM による chroot の場合にはシステムの passwd 記述と chroot 下の passwd 記述は同一となる。
root# egrep '^hanako:' /etc/passwd >>/home/jails/etc/passwd
shadow と group は文字通りそのまま:
root# egrep '^hanako:' /etc/shadow >>/home/jails/etc/shadow root# egrep '^hanako:' /etc/group >>/home/jails/etc/group
いよいよ仕上げの行程だ。システムの (chroot内のではない) PAM 関係設定ファイルを調整する。問題の PAM モジュール pam_chroot.so は /lib/security ディレクトリにあるが、それ自体は chroot_dir にコピーする必要はない。
login, sshd, su に、
session required pam_chroot.so
という記述をそれぞれ加える。PAM は、サービス定義ファイルの記述順の通りにモジュールを処理していくので、行の並び順は重要な意味を持つ。 chroot モジュールは、可能な限り後ろ、できれば一番最後に入れるべきだ。というのも、 chroot が実行された後となっては、 chroot_dir 外である /etc/pam.d ディレクトリはもうそれ以上読めなくなってしまうから。ただし SELinux を有効にした場合には別のアプローチが必要かも知れない。
上記 /etc/pam.d/su の設定を行った場合には、追加の処置が必要だ。 pam_xauth は、そのユーザのホームディレクトリ直下にセッションクッキー .xauthxxxxx を生成しようとする。しかし、 /etc/passwd で hanako のユーザホームは `/home/nakako' と定義されており、 chroot 前にはそんなディレクトリは存在しないため作れない。これは、 /home/jails/home/hanako から /home/hanako へシンボリックリンクを張っておくことで解決できる。つまり: root# cd /home root# ln -s jails/home/hanako hanako する。リンクのオーナーは root で構わないが、気になる人は、 root# chown --no-dereference hanako.hanako hanako |
pam_chroot.so モジュールは、このファイルに記述されているユーザにのみ働く。ユーザ名は正規表現なので、 hanako とだけ書いた場合、chanako や hanakosan というユーザにもマッチしてしまう。始め (^) と終わり ($) を明示しているのはそのため。 Fedora Core 3 標準の pam_chroot ではこれ以外にコントロールのバリエーションはないが、 SourceForge.net で見つけた拡張版 pam_chroot では、グループによる指定や、ユーザ名に応じて chroot_dir を動的に割り当てたりもできるようだ。
# username_regex chroot_dir ^hanako$ /home/jails
以下の PAM モジュール設定ファイルだけは chroot_dir 内にもないとエラーが出るので、コピーしておく。
root# cp -p /etc/security/pam_env.conf /home/jails/etc/security root# cp -p /etc/security/console.perms /home/jails/etc/security
PAM による chroot を sshd で利用するには、 sshd サーバの設定も確かめておく必要がある。まず、sshd 自体は root 権限で動いていなければならないということ。そして /etc/ssh/sshd_config の下記項目も確認しておこう:
以上で PAM による chroot は完成だ。