PAMによるchroot

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 はもちろん、sussh (scp, sftp 含む) なども含まれる。

ここでは、一部のユーザのログインと ssh 接続を chroot 化することを目的とし、手動で 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

オレンジ色のものはややオプショナル

まずは共有ライブラリのことは考えずに、本体だけをコピーする。基本的にはシンボリックリンク (例えば shegrep) はシンボリックリンクのままコピーし、コピー後に確認して、リンクが切れていたり 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 スクリプト

ldcopy は上記のライブラリ検索とコピーを BASH スクリプト化したものだ。それ以外に特に込み入ったことはしていない。ライブラリをコピーする上で目的地に存在しない枝フォルダは自動的に作成されるので、前述のように liblib/tls くらいを作っておけば充分だ。使い方は --help または -h オプションを与えれば見られるが、簡単に説明しておこう。

コマンド書式:
ldcopy --dir=/chroot/dir [--follow] [--test] /path/to/program
(引数の順序はこの通りである必要はない)

引数の意味:

--dir=/chroot/dir chroot 環境の基底ディレクトリを指定。基底ディレクトリが未作成の場合は即エラー exit する。
--follow ldcopy は、デフォルト挙動ではライブラリをありのままコピー (cp -dpf) し、その結果できたリンクが破損リンクとなった場合はその分だけ実体としてコピーしなおすようになっている。 --follow オプションを使用すると、コピーコマンドが `cp -pf' に切り替わり、いきなり実体をコピーする。
--test コピー先ディレクトリの作成 (存在しなかった場合) やライブラリのコピーは行わず、処理を「寸止め」。発行されるはずのコマンドのプレビューと、コピー先ディレクトリにリンク切れシンボリックリンクがないかのチェックのみ行う。
/path/to/program 調べたいプログラムを指定する。

特別なプログラムと関連ファイルのコピー

複雑なプログラムはaddjailswでコピー

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

etc/ 下のファイル

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
  • apache ユーザ関連は chroot ディレクトリ内に WEB コンテンツファイルが存在する場合に必要。そうしないと、特に CGI で動的に生成されるファイルがある時、ファイル属性の UID, GID ナンバーが名前へと解決されなくて格好悪い。
  • shadow ファイルの root の行は、パスワードのセクションを `*' で潰しておこう。 chroot したユーザに su を使わせる必要はないだろうから。
  • chroot されたユーザに X を一使わせることはまずないので xfs, gdm は要らないかも知れない。
  • 日本語を入力する機会がなければ htt(IIIMF), canna も必要ないだろう。

dev/ 下のデバイスファイル

コピーしてくるのではなく 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/syslogSYSLOGD_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 だけは、このスクリプトのためだけに余分なプログラムをコピーする必要が生じてしまうため、特に理由がなければリネームするか削除する。

passwdファイルの修正とコピー

まずシステムの /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

PAMの設定

いよいよ仕上げの行程だ。システムの (chroot内のではない) PAM 関係設定ファイルを調整する。問題の PAM モジュール pam_chroot.so/lib/security ディレクトリにあるが、それ自体は chroot_dir にコピーする必要はない。

PAMサービス定義ファイル

login, sshd, su に、

session required pam_chroot.so

という記述をそれぞれ加える。PAM は、サービス定義ファイルの記述順の通りにモジュールを処理していくので、行の並び順は重要な意味を持つ。 chroot モジュールは、可能な限り後ろ、できれば一番最後に入れるべきだ。というのも、 chroot が実行された後となっては、 chroot_dir 外である /etc/pam.d ディレクトリはもうそれ以上読めなくなってしまうから。ただし SELinux を有効にした場合には別のアプローチが必要かも知れない。

/etc/pam.d/login
一番最後、あるいは `pam_selinux.so open' の直前がいいらしい。
/etc/pam.d/sshd
一番最後に入れる。
/etc/pam.d/su
他のユーザから su する機会があるならこれも編集。 pam_xauth.so の記述された行より後ろにしないと、X 上のターミナルから su した時に環境変数が不足して挙動がおかしくなる。
上記 /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

/etc/security/chroot.conf

pam_chroot.so モジュールは、このファイルに記述されているユーザにのみ働く。ユーザ名は正規表現なので、 hanako とだけ書いた場合、chanako や hanakosan というユーザにもマッチしてしまう。始め (^) と終わり ($) を明示しているのはそのため。 Fedora Core 3 標準の pam_chroot ではこれ以外にコントロールのバリエーションはないが、 SourceForge.net で見つけた拡張版 pam_chroot では、グループによる指定や、ユーザ名に応じて chroot_dir を動的に割り当てたりもできるようだ。

# username_regex   chroot_dir
^hanako$           /home/jails

chroot_dir内にどうしても必要なファイル

以下の 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

sshdサーバの設定変更

PAM による chroot を sshd で利用するには、 sshd サーバの設定も確かめておく必要がある。まず、sshd 自体は root 権限で動いていなければならないということ。そして /etc/ssh/sshd_config の下記項目も確認しておこう:

以上で PAM による chroot は完成だ。