chroot 入門 - ルートディレクトリを切り替えて環境を分離する

chroot 入門 - ルートディレクトリを切り替えて環境を分離する

この記事で解決できること

  • chrootプロセスから見えるルートディレクトリを切り替える 仕組みが分かる
  • 「chroot 環境でコマンドが動かない」を 依存ライブラリの観点で切り分け できる
  • live USB / レスキューモードからの システム復旧の型 が身につく
  • chroot が セキュリティ境界ではない 理由と、代わりに何を使うべきかが分かる

結論(実務の型)

  • chroot は ファイルシステムの見え方を切り替える だけ。プロセス・ネットワークは隔離しない
  • 動かす コマンド本体とその共有ライブラリ を新ルート内に揃える必要がある
  • システム復旧では /dev /proc /sysbind mount で持ち込んでから chroot する
  • 本物の隔離が必要なら chroot ではなく コンテナ(namespace) を使う

前提(対象環境)

  • OS: Ubuntu / Debian 系(RHEL 系もコマンドは同一)
  • chroot 実行には root 権限CAP_SYS_CHROOT)が必要
  • chroot コマンドは coreutils 同梱(追加インストール不要)

chroot とは何か?

結論: chroot は、あるプロセスとその子プロセスから見える「ルートディレクトリ /」を指定したディレクトリに付け替えるコマンド。そのプロセスは新ルートの外にあるファイルへアクセスできなくなる。

通常、すべてのプロセスはシステム全体のルート / を起点にファイルを辿る。chroot /mnt/newroot を実行すると、その配下のプロセスにとって /mnt/newroot が新しい / になる。

$ sudo chroot /mnt/newroot

この瞬間、chroot されたシェルから見える /etc/passwd は、実体としては /mnt/newroot/etc/passwd を指す。新ルートの外側(ホスト本来の /home など)は パスとして表現できなくなる。これが「環境を分離する」と表現される所以である。

chroot された環境は慣習的に chroot jail(牢獄) と呼ばれる。プロセスがその「牢獄」の外のファイルツリーを見られないことに由来する。

なぜ chroot を使うのか?

結論: 主な用途はシステム復旧・クリーンなビルド/テスト環境・特定環境への閉じ込めの3つ。「本番とは独立したファイルシステムでコマンドを実行したい」場面で使う。

代表的なユースケースは次のとおり。

用途 具体例
システム復旧 起動しなくなった OS を live USB から chroot して grub 再インストール・パスワード再設定
クリーン環境 パッケージビルドを汚れていない最小環境で実行(debootstrap で作成した root など)
隔離実行 公開 FTP/SFTP をホームディレクトリ配下に閉じ込める(sshdChrootDirectory
検証 別ディストリ・別バージョンのユーザーランドを既存カーネル上で動かす

chroot は カーネルを切り替えない。新ルート内に別バージョンのライブラリやコマンドを置けても、動いているカーネルはホストのものである。

どう chroot を実行するのか?

結論: 構文は chroot 新ルート [コマンド]。コマンドを省略するとそのユーザーのシェル($SHELL または /bin/sh)を対話起動する。実行には root 権限が要る。

基本構文:

$ sudo chroot NEWROOT [COMMAND [ARG...]]

コマンドを省略した場合:

# 新ルート内のシェルを対話起動する
$ sudo chroot /mnt/newroot

コマンドを明示する場合:

# 新ルート内の /bin/ls を実行して終了する
$ sudo chroot /mnt/newroot /bin/ls -l /

COMMAND に指定するパスは 新ルートから見た絶対パスchroot /mnt/newroot /bin/bash/mnt/newroot/bin/bash を実行する。ホスト側のパスではない点に注意。

chroot 環境に必要なものは?

結論: 動かしたいコマンドの実体に加え、それが依存する共有ライブラリ(.so)を新ルート内に揃える必要がある。揃っていないと「No such file or directory」で失敗する。

最小の落とし穴がこれである。chroot /mnt/newroot /bin/bash を実行しても、/mnt/newroot/bin/bash が存在しなければ当然動かない。さらに bash が依存する共有ライブラリ も新ルート内に必要になる。

依存ライブラリは ldd で確認する。

$ ldd /bin/bash
        linux-vdso.so.1 (0x00007fff...)
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f...)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
        /lib64/ld-linux-x86-64.so.2 (0x00007f...)

表示された .so ファイルを、新ルート内の 同じパス にコピーすれば bash が動く。手作業での最小 jail 構築例:

$ NEWROOT=/mnt/jail
$ sudo mkdir -p $NEWROOT/{bin,lib,lib64}
$ sudo cp /bin/bash $NEWROOT/bin/
# ldd の出力を見て必要な .so を同じ階層へコピー
$ sudo cp /lib/x86_64-linux-gnu/{libtinfo.so.6,libc.so.6} $NEWROOT/lib/
$ sudo cp /lib64/ld-linux-x86-64.so.2 $NEWROOT/lib64/
$ sudo chroot $NEWROOT /bin/bash

実用的な chroot 環境は手作業ではなく debootstrap(Debian/Ubuntu)や dnf --installroot(RHEL 系)で 依存込みの完全なユーザーランドを一括展開 して作るのが普通。手動コピーは仕組みの理解用と捉えるとよい。

システム復旧での chroot 実践

結論: 起動しない OS の修復では、対象ディスクをマウントし、/dev /proc /sys を bind mount で持ち込んでから chroot する。これでホスト稼働中の OS をあたかも通常起動したかのように操作できる。

live USB やレスキューモードから本来の OS に入って修復する手順。/dev/sda1 をルートパーティションとする。

# 1. 対象のルートパーティションをマウント
$ sudo mount /dev/sda1 /mnt

# 2. 仮想ファイルシステムを bind mount で持ち込む
$ sudo mount --bind /dev  /mnt/dev
$ sudo mount --bind /proc /mnt/proc
$ sudo mount --bind /sys  /mnt/sys

# 3. chroot で本来の OS に入る
$ sudo chroot /mnt

# 4. ここからは通常の OS 操作(例: GRUB 再インストール)
# grub-install /dev/sda && update-grub

/dev /proc /sys を持ち込まないと、grub-install やパッケージ操作など カーネルやデバイス情報を参照するコマンドが失敗 する。復旧 chroot ではこの bind mount がほぼ必須。

作業後は chroot を抜けてからアンマウント する。順序を間違えると target is busy になる。

# chroot を抜ける
# exit

$ sudo umount /mnt/sys /mnt/proc /mnt/dev
$ sudo umount /mnt

/dev/pts(疑似端末)や /run が必要なケースもある。arch-chroot(Arch Linux 提供だが他環境でも利用可)はこれらの bind mount を自動でやってくれる便利ラッパー。

chroot はセキュリティ境界になるのか?

結論: ならない。chroot はファイルシステムの見え方を変えるだけで、root 権限を持つプロセスは標準的な手法で chroot の外へ脱出できる。隔離目的には namespace ベースのコンテナを使うべき。

chroot を「サンドボックス」と誤解すると危険である。chroot 環境内で root 権限を持つプロセス は、二重 chroot などの古典的テクニックで容易に脱獄(chroot escape)できる。これは設計上の制約であり、バグではない。

chroot が隔離 しない もの:

  • プロセス空間(ps で外のプロセスが見え、kill できる)
  • ネットワーク(同じネットワークスタックを共有)
  • ユーザー / 権限(chroot 内の root はホストの root と同一)
  • カーネル(共有)

非特権ユーザー向けの安全な閉じ込めとしては、sshdChrootDirectory(SFTP 専用閉じ込め)や、systemd サービスの RootDirectory= + PrivateDevices= などの組み合わせが現実的な選択肢になる。

よくあるエラーと対処

結論: 失敗の大半は「依存ライブラリ不足」「bind mount 漏れ」「root 権限不足」の3つ。エラーメッセージから原因を切り分ける。

chroot: failed to run command '/bin/bash': No such file or directory

bash 本体は存在するのにこのエラーが出る場合、依存する共有ライブラリ(ローダ含む)が新ルート内に無い ことがほとんど。ldd /bin/bash の出力を新ルートへ揃える(依存セクション参照)。

$ sudo chroot /mnt/jail ldd /bin/bash   # 解決済みかは外から ldd で確認できないため
$ ldd /bin/bash                          # ホスト側で必要 .so を洗い出す

chroot: cannot change root directory to '/mnt': Operation not permitted

root 権限が不足している。sudo を付ける。コンテナ内などで CAP_SYS_CHROOT が剥奪されている場合も同じエラーになる。

chroot 後に「command not found」が多発する

PATH が新ルート内の構成と合っていない、または必要なコマンド群が未配置。最小環境では ls すら無いことがある。debootstrap 等で完全なユーザーランドを展開する。

umount で target is busy

chroot シェルを抜ける前にアンマウントしようとしている。先に chroot を exit し、その後 /sys /proc /dev /mnt の順でアンマウントする。

コピペ用: 復旧 chroot の安全テンプレ

# 入る
sudo mount /dev/sda1 /mnt
sudo mount --bind /dev  /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys  /mnt/sys
sudo chroot /mnt

# 作業後、chroot を exit してから
sudo umount /mnt/sys /mnt/proc /mnt/dev
sudo umount /mnt

次に読む