65.9K
CodeProject 正在变化。 阅读更多。
Home

什么是:chroot – Linux 中的系统调用和实用程序

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019 年 3 月 30 日

CPOL

3分钟阅读

viewsIcon

9081

chroot() Linux 系统调用的解释和 chroot 实用程序的使用示例

chroot()于 1979 年被添加到 Version 7 Unix 中,用于文件系统隔离。

事实上,它是整个当前容器化理念的先驱,只是现在有命名空间和 cgroups,而早期使用 chroot 创建一个与主机隔离的环境,例如,可用于测试目的。

此外,chrootchange 和文件系统 root 的“缩写”。

Linux 文件系统树

Linux 中的目录树通常如下所示(另请参阅文件系统层次结构标准

$ tree -d -L 1 /
/
├── bin -> usr/bin
├── boot
├── data
├── dev
├── etc
├── home
├── lib -> usr/lib
├── lib64 -> usr/lib
├── lost+found
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin -> usr/bin
├── srv
├── sys
├── tmp
├── usr
└── var

chroot() 允许创建一个嵌套的文件系统树,这可以用下图演示

下面,我们将通过一些 C 代码示例更仔细地研究 chroot(),以及 chroot 实用程序及其在操作系统中的用法。

chroot() – Linux 系统调用

因此,chroot 旨在通过更改其根目录来限制对文件系统的访问。

即,而不是这样的目录结构

$ tree -d -L 1 /
/
├── bin -> usr/bin
├── boot
├── data
...
├── tmp
├── usr
└── var

一个进程将仅看到那些在顶级由传递给 chroot() 的参数限制的目录。

让我们创建一些要用于示例的目录

$ mkdir -p /tmp/chroot/{1,2,3,4}

让我们用 C 语言编写以下代码

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>

int main(void) {

    // check path before chroot()
    char t_cwd[PATH_MAX];
    getcwd(t_cwd, sizeof(t_cwd));
    printf("Current dir before chroot(): %s\n", t_cwd);

    // do chroot()
    chdir("/tmp/chroot/");
    if (chroot("/tmp/chroot/") != 0) {
        perror("chroot /tmp/chroot/");
        return 1;
    }

    // check path path after chroot()
    char a_cwd[PATH_MAX];
    getcwd(a_cwd, sizeof(a_cwd));
    printf("Current dir after chroot(): %s\n", a_cwd);

    // point dr struct to the "root"
    struct dirent *de;
    DIR *dr = opendir("/");  
  
    // run readdir() and list "root"'s content
    while ((de = readdir(dr)) != NULL)  
        printf("%s\n", de->d_name);  

    // try to open /etc/passwd from a "host" filesystem
    FILE *f;
    f = fopen("/etc/passwd", "r");

    if (f == NULL) {
        perror("/etc/passwd");
        return 1;
    } else {
        char buf[100];
        while (fgets(buf, sizeof(buf), f)) {
             printf("%s", buf);
        }
    }

    return 0;
}

这里将会

  • 在调用 chroot() 之前检查当前路径
  • 调用 chroot()
  • 再次检查当前路径
  • 获取“根”内容
  • 尝试打开存在于“真实”文件系统上的 /etc/passwd 文件

构建它

$ gcc chroot_example.c -o chroot_example

并运行以检查(使用 sudo,因为 chroot() 只能由 root 使用)

$ sudo ./chroot_example
Current dir before chroot(): /home/setevoy/Scripts/C
Current dir after chroot(): /
.
..
4
3
2
1
/etc/passwd: No such file or directory

chroot() 本身在内核的 open.c 文件中定义

SYSCALL_DEFINE1(chroot, const char __user *, filename)
{
  return ksys_chroot(filename);
}

并将返回 ksys_chroot()

int ksys_chroot(const char __user *filename)
{
  struct path path;
  int error;
  unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
retry:
  error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
  if (error)
    goto out;

  error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
  if (error)
    goto dput_and_out;

  error = -EPERM;
  if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
    goto dput_and_out;
  error = security_path_chroot(&path);
  if (error)
    goto dput_and_out;

  set_fs_root(current->fs, &path);
  error = 0;
dput_and_out:
  path_put(&path);
  if (retry_estale(error, lookup_flags)) {
    lookup_flags |= LOOKUP_REVAL;
    goto retry;
  }
out:
  return error;
}

它又会为进程调用 set_fs_root()

void set_fs_root(struct fs_struct *fs, const struct path *path)
{
  struct path old_root;

  path_get(path);
  spin_lock(&fs->lock);
  write_seqcount_begin(&fs->seq);
  old_root = fs->root;
  fs->root = *path;
  write_seqcount_end(&fs->seq);
  spin_unlock(&fs->lock);
  if (old_root.dentry)
    path_put(&old_root);
}

你可以在这里>>这里>>>找到很好的系统调用描述。


chroot – Linux 实用程序

要在 Linux 中创建一个隔离空间,你可以使用 chroot 实用程序

$which chroot
/usr/bin/chroot

$ file /usr/bin/chroot
/usr/bin/chroot: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), 
    dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, 
    BuildID[sha1]=f3861107940247a67dbbf6343fa5ff1c1c70305c, stripped

让我们为我们的“监狱”(FreeBSD 的jail 是 UNIX 的 chroot 的高级后继者)创建一个目录,其中包含一个隔离的文件系统

$ cd /tmp/ $ mkdir changed_root

实际上,chroot 实用程序将调用相同的 chroot() 系统调用 - 让我们用 strace 检查一下

$ sudo strace -e trace=chroot chroot changed_root/
chroot("changed_root/")                 = 0
chroot: failed to run command ‘/bin/bash’: No such file or directory
+++ exited with 127 +++

错误 ‘/bin/bash’: No such file or directory 是由以下事实引起的:在这个新环境中没有 /bin 目录和 bash 可执行文件。

类似地,如果你尝试调用任何其他程序,将返回这样的错误:

[setevoy@setevoy-arch-work /tmp]  $ which ls
/usr/bin/ls

[setevoy@setevoy-arch-work /tmp]  $ sudo chroot changed_root /usr/bin/ls
chroot: failed to run command ‘/usr/bin/ls’: No such file or directory

让我们修复它 - 在我们的 /tmp/changed_root 中创建 /bin 目录,并将 bash 文件从“主机”复制到这个“容器”中

[setevoy@setevoy-arch-work /tmp]  $ mkdir changed_root/bin
[setevoy@setevoy-arch-work /tmp]  $ cp /bin/bash changed_root/bin
[setevoy@setevoy-arch-work /tmp]  $ file changed_root/bin/bash
changed_root/bin/bash: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), 
    dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, 
    BuildID[sha1]=357034d1736cd97d2c8f8347045250dbd0de998e, stripped

再试一次

[setevoy@setevoy-arch-work /tmp]  $ sudo chroot changed_root /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

好的。

但现在它是由缺少必要的库引起的 - chroot 无法告知这一点。

ldd 检查 bash 的依赖项

[setevoy@setevoy-arch-work /tmp]  $ ldd /bin/bash
linux-vdso.so.1 (0x00007ffe37f16000)
libreadline.so.8 => /usr/lib/libreadline.so.8 (0x00007f39b13d2000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f39b13cd000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f39b1209000)
libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007f39b119a000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f39b153f000)

在我们的新工作目录中创建另外两个目录 - /lib/lib64

[setevoy@setevoy-arch-work /tmp]  $ mkdir changed_root/usr/lib changed_root/lib64

并复制库文件

[setevoy@setevoy-arch-work /tmp]  $ cp /usr/lib/libreadline.so.8 changed_root/usr/lib/
[setevoy@setevoy-arch-work /tmp]  $ cp /usr/lib/libdl.so.2 changed_root/usr/lib/
[setevoy@setevoy-arch-work /tmp]  $ cp /usr/lib/libc.so.6 changed_root/usr/lib/
[setevoy@setevoy-arch-work /tmp]  $ cp /usr/lib/libncursesw.so.6 changed_root/usr/lib/
[setevoy@setevoy-arch-work /tmp]  $ cp /lib64/ld-linux-x86-64.so.2 changed_root/lib64

再次运行 chroot

[setevoy@setevoy-arch-work /tmp]  $ sudo chroot changed_root/
bash-5.0#

现在我们在这里运行 bash 并且所有内置函数都能正常工作

bash-5.0# pwd/

但显然 - 没有其他外部实用程序会在这里工作

bash-5.0# ls -l
bash: ls: command not found

这可以像我们对 bash 所做的那样修复

[setevoy@setevoy-arch-work /tmp]  $ which ls
/usr/bin/ls

[setevoy@setevoy-arch-work /tmp]  $ cp /usr/bin/ls changed_root/bin/
[setevoy@setevoy-arch-work /tmp]  $ ldd /usr/bin/ls
linux-vdso.so.1 (0x00007ffdebbf5000)
libcap.so.2 => /usr/lib/libcap.so.2 (0x00007fa5b147d000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fa5b12b9000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fa5b14d8000)

[setevoy@setevoy-arch-work /tmp]  $ cp /usr/lib/libcap.so.2 changed_root/usr/lib/

其他库已经复制,所以让我们再次运行 ls

bash-5.0# /bin/ls -l /
total 0
drwxr-xr-x 2 1000 1000  80 Mar 22 11:45 bin
drwxr-xr-x 2 1000 1000 120 Mar 22 11:37 lib
drwxr-xr-x 2 1000 1000  60 Mar 22 11:38 lib64
drwxr-xr-x 3 1000 1000  60 Mar 22 11:39 usr

另请参阅

类似帖子

什么是:chroot – Linux 中的系统调用和实用程序 - CodeProject - 代码之家
© . All rights reserved.