如何在 Linux 上使用 chroot 命令

chroot 命令是一项强大的工具,它允许你创建一个受限的“监狱”环境,非常适合隔离你的开发、测试工作区,甚至增强系统的安全性。本文将向你展示如何以最直接的方式使用它。

什么是 chroot?

评估一个命令的价值时,功能和易用性同等重要。如果一个命令过于复杂或冗长,用户望而却步,那么它的功能也就无从谈起。chroot 命令似乎常被认为是难以使用,或者说设置过程繁琐,导致很多人没有充分利用它的潜力。

通过 chroot,你可以在一个独立的文件系统中运行程序(例如 Bash 交互式 shell)。这个文件系统与你常规的文件系统隔离,从而创造一个封闭的环境。在 chroot 环境中,所有资源都被限制在其中,除非你拥有 root 权限,否则无法访问其自身的根目录。这就是为什么这种环境被形象地称为 chroot “监狱”。请注意,这里的“监狱”概念不同于 FreeBSD 的 jail 命令,后者创建的 chroot 环境 安全性更高

实际上,使用 chroot 命令的方式非常直接。本文将逐步引导你完成整个过程,全程使用标准的 Linux 命令,且与发行版无关。虽然有些发行版(例如 Ubuntu 的 debootstrap)提供了专用工具来设置 chroot 环境,但我们这里将采用通用方法。

何时使用 chroot?

chroot 环境提供了类似于虚拟机的功能,但它是一种更轻量级的选择。它不需要安装和配置虚拟机管理程序(如 VirtualBoxVirt-Manager),也无需在隔离环境中安装新的内核。chroot 环境会共享你现有的内核。

从某种意义上说,chroot 环境更接近于容器(例如 LXC),而不是虚拟机。它们轻便、部署速度快,且能够自动化创建和启动。与容器类似,配置 chroot 环境的一种常用方法是安装足够支持所需操作的操作系统组件。要确定 chroot 环境“需要什么”,关键在于分析它的用途。

chroot 环境的一些常见用途包括:

  • 软件开发和产品验证:开发人员编写代码,产品验证(PV)团队进行测试。有时,PV 可能会发现开发人员的计算机上无法复现的问题。这是因为开发人员的机器上安装了各种普通用户和 PV 团队没有的工具和库。chroot 允许开发人员在自己的计算机上创建类似于普通用户环境,并在交付 PV 测试之前在此环境中进行验证。可以使用软件所需的最低限度的依赖项来配置 chroot 环境。
  • 降低开发风险:开发人员可以创建一个专用的开发环境,从而避免任何在该环境中发生的问题影响到他们实际的系统。
  • 运行过时软件:有时,你需要运行旧版本的软件。如果旧软件的需求与你的 Linux 版本冲突或不兼容,你可以使用 chroot 来隔离该软件的环境。
  • 系统恢复和文件系统升级:如果 Linux 安装无法启动,你可以使用 chroot 将损坏的文件系统挂载到 Live CD 上的挂载点。这样,你可以像正常从根目录 / 启动一样,在损坏的系统中工作并尝试修复它。使用类似的技术可以将 Linux 文件系统从 ext2 或 ext3 迁移到 ext4。
  • 隔离应用程序:在 chroot 环境中运行 FTP 服务器或其他互联网连接的应用程序可以限制外部攻击者可能造成的破坏。这是增强系统安全性的重要一步。

创建 chroot 环境

首先,我们需要一个目录作为 chroot 环境的根目录。为了方便,我们将创建一个变量来存储这个目录的路径。这里,我们设置变量 chr 来存储 “testroot” 目录的路径。这个目录即使不存在也没关系,我们稍后会创建它。如果目录已经存在,它应该是空的。

chr=/home/dave/testroot

如果目录不存在,我们需要创建它。可以使用以下命令, -p 选项确保同时创建任何缺失的父目录:

mkdir -p $chr

接下来,我们需要创建一些目录,用来存放 chroot 环境中所需的操作系统组件。我们将构建一个精简的 Linux 环境,使用 Bash 作为交互式 shell,并包含 touchrmls 命令。这将使我们可以使用所有的 Bash 内置命令以及 touchrmls 命令。也就是说,我们能够在环境中创建、列出和删除文件,并使用 Bash Shell。

使用 大括号展开,我们可以一步到位地创建多个目录:

mkdir -p $chr/{bin,lib,lib64}

现在,我们将目录更改为我们新的根目录:

cd $chr

下面,我们将需要的二进制文件从常规的 /bin 目录复制到 chroot 环境的 /bin 目录。 -v (详细)选项可以让我们在复制的过程中看到每个操作的详情:

cp -v /bin/{bash,touch,ls,rm} $chr

已复制的文件如下:

这些二进制文件都有依赖关系。我们需要找到它们的依赖文件,并将它们也复制到 chroot 环境中,否则 bash、touch、rm 和 ls 将无法运行。我们需要依次对每个命令执行此操作。首先,我们处理 Bash。 ldd 命令会为我们 列出依赖项

ldd /bin/bash

依赖项会在终端窗口中列出:

我们需要将这些依赖文件复制到新的环境中。 如果手动从列表中逐个选择依赖文件并复制,那将非常耗时且容易出错。

幸运的是,我们可以将其半自动化。我们再次列出依赖项,这次我们将把它格式化成一个列表,然后遍历这个列表复制文件。

这里,我们使用 ldd 列出依赖项,并将结果通过管道传递给 egrepegrep 命令等同于使用带有 -E(扩展正则表达式)选项的 grep-o(仅匹配)选项将输出限制为匹配行的部分。 我们正在寻找以数字结尾的匹配库文件 [0-9]

list="$(ldd /bin/bash | egrep -o '/lib.*.[0-9]')"

我们可以使用 echo 命令检查列表的内容:

echo $list

有了列表之后,我们可以使用以下循环逐个复制文件。 我们使用变量 i 遍历列表。 对于列表中的每个成员,我们将文件复制到 chroot 环境的根目录,也就是保存在变量 $chr 中的路径。

-v (详细)选项使 cp 命令在执行每个副本时声明它。 --parents 选项确保在 chroot 环境中创建任何缺失的父目录。

for i in $list; do cp -v --parents "$i" "${chr}"; done

这是输出:

我们将使用相同的技术捕获其他每个命令的依赖关系。 我们将使用循环技术来执行实际的复制。 好消息是,我们只需对收集依赖项的命令稍作编辑。

通过几次按向上箭头键可以从命令历史记录中检索命令,然后进行编辑。 循环复制命令根本不需要更改。

这里,我们使用向上箭头键找到命令,并将其编辑为 touch 而不是 bash

list="$(ldd /bin/touch | egrep -o '/lib.*.[0-9]')"

现在,我们可以重复与之前完全相同的循环命令:

for i in $list; do cp -v --parents "$i" "${chr}"; done

touch 命令的依赖文件已复制:

接下来,我们编辑 ls 的列表命令行:

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

同样,我们将使用相同的循环命令。它不关心列表中的文件是什么, 它只是盲目地遍历列表并复制文件。

for i in $list; do cp -v --parents "$i" "${chr}"; done

ls 命令的依赖项已被复制:

最后一次编辑 list 命令行,使其适用于 rm:

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

最后一次使用循环复制命令:

for i in $list; do cp -v --parents "$i" "${chr}"; done

rm 命令的最后依赖项已被复制到 chroot 环境中。 我们终于准备好使用 chroot 命令了。 此命令设置 chroot 环境的根目录,并指定要作为 shell 运行的应用程序。

sudo chroot $chr /bin/bash

我们的 chroot 环境现在处于活动状态。终端窗口提示符已更改,交互式 shell 正由我们环境中的 bash shell 处理。

我们可以尝试我们带入环境的命令。

ls
ls /home/dave/Documents

当我们在环境中使用 ls 命令时,它会像我们预期的那样工作。 当我们尝试访问环境之外的目录时,命令失败。

我们可以使用 touch 创建文件,使用 ls 列出文件,使用 rm 删除文件。

touch sample_file.txt
ls
rm sample_file.txt
ls

当然,我们也可以使用 Bash shell 提供的内置命令。 如果你在命令行中键入 help,Bash 会为你列出它们。

help

使用 exit 离开 chroot 环境:

exit

如果你想删除 chroot 环境,可以简单地删除该目录:

rm -r testroot/

这将递归删除 chroot 环境中的文件和目录。

自动化带来的便捷

如果 chroot 环境对你来说很有用,但是设置过程略显繁琐,请记住,你可以通过使用别名、函数和脚本来消除重复任务的压力和风险。