在CentOS服务器上做chroot环境隔离,核心就是把进程"关进小黑屋",让它只能看到指定目录下的文件系统,再通过bind挂载把必要的系统文件"映射"进去,同时严格限制挂载范围防止越权访问。这套方案在多租户托管、SFTP隔离、不可信代码沙箱等场景下非常实用,但很多人只做了chroot就觉得安全了,忽略了bind挂载带来的风险,结果隔离形同虚设。

下面我从原理、配置、限制、加固四个层面,把这套东西彻底讲透。

一、chroot到底在干什么

chroot命令的本质是修改进程的根目录。正常情况下,Linux进程看到的"/"是整个系统的根文件系统。执行chroot之后,进程的"/"变成了你指定的那个目录,它往上走也走不出去,相当于被困在一个子目录里。但这里有个关键前提:chroot并不是真正的安全隔离,它只是文件系统层面的视图切换,root用户在chroot里依然可以做很多危险操作,比如重新挂载、创建设备节点等。

所以chroot必须配合其他手段才能形成有效隔离,比如降权运行、限制系统调用、配合bind挂载只暴露必要文件。单独用chroot当安全措施,基本等于没锁门。

二、CentOS上搭建chroot环境的完整步骤

假设我们要为一个SFTP用户创建隔离环境,路径定在/home/chroot_sftp。第一步,创建目录结构:

mkdir -p /home/chroot_sftp/{bin,lib,lib64,etc,dev,usr/bin,usr/lib,usr/lib64,tmp,home/sftpuser}

第二步,复制必要的系统文件。chroot环境里需要有基本的命令和库文件,否则用户进去什么都干不了:

cp /bin/bash /home/chroot_sftp/bin/
cp /bin/ls /home/chroot_sftp/bin/
cp /bin/cp /home/chroot_sftp/bin/
cp /lib64/ld-linux-x86-64.so.2 /home/chroot_sftp/lib64/
cp /lib64/libc.so.6 /home/chroot_sftp/lib64/
cp /lib64/libdl.so.2 /home/chroot_sftp/lib64/
cp /lib64/libtinfo.so.6 /home/chroot_sftp/lib64/
cp /lib64/libutil.so.1 /home/chroot_sftp/lib64/
cp /lib64/libpthread.so.0 /home/chroot_sftp/lib64/
cp /lib64/libcrypto.so.1.1 /home/chroot_sftp/lib64/
cp /lib64/libssl.so.1.1 /home/chroot_sftp/lib64/

第三步,创建必要的设备节点和配置文件:

mknod /home/chroot_sftp/dev/null c 1 3
mknod /home/chroot_sftp/dev/zero c 1 5
mknod /home/chroot_sftp/dev/random c 1 8
mknod /home/chroot_sftp/dev/urandom c 1 9
cp /etc/passwd /home/chroot_sftp/etc/
cp /etc/group /home/chroot_sftp/etc/
cp /etc/resolv.conf /home/chroot_sftp/etc/
cp /etc/nsswitch.conf /home/chroot_sftp/etc/
cp /etc/host.conf /home/chroot_sftp/etc/

第四步,设置权限,确保只有root能写,普通用户只能读:

chown -R root:root /home/chroot_sftp
chmod 755 /home/chroot_sftp/bin
chmod 755 /home/chroot_sftp/lib64
chmod 755 /home/chroot_sftp/etc
chmod 755 /home/chroot_sftp/dev
chmod 755 /home/chroot_sftp/usr

第五步,配置OpenSSH的sftp子系统指向这个chroot目录。编辑/etc/ssh/sshd_config:

Match User sftpuser
    ChrootDirectory /home/chroot_sftp
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
    PermitTunnel no

重启sshd服务:systemctl restart sshd。到这里基本的chroot隔离就搭好了。

三、bind挂载的作用和正确姿势

光做chroot有个大问题:用户进了隔离环境之后,想用ls、bash这些命令,你得把对应的二进制文件和动态库全部复制进去。但这样做有两个麻烦:一是文件多且容易遗漏依赖,二是系统更新后你得手动同步,否则版本不匹配会报错。

bind挂载就是解决这个问题的。它把宿主机上的真实目录"映射"到chroot目录里,用户看到的是一个真实的目录,但实际上被限制在chroot的视图内。比如:

mount --bind /bin /home/chroot_sftp/bin
mount --bind /lib64 /home/chroot_sftp/lib64
mount --bind /usr/bin /home/chroot_sftp/usr/bin
mount --bind /usr/lib64 /home/chroot_sftp/usr/lib64

这样做的好处是:宿主机更新了bash或openssl,chroot里自动生效,不用手动同步。但风险也很明显——如果你把/proc、/sys、/dev这些敏感目录也bind进去了,用户就能看到系统进程信息、内核参数、甚至操作设备,隔离直接被突破。

四、bind挂载的限制策略——这才是重点

很多教程只教你怎么bind,不教你怎么限制。下面是我总结的几条硬规则:

第一,绝对不要bind /proc和/sys。这两个是内核信息的暴露口,用户可以通过/proc/1/cmdline看到所有进程的启动命令,通过/proc/cpuinfo看到硬件信息,通过/sys/class/net看到网卡配置。一旦挂载进去,chroot形同虚设。

第二,/dev目录只挂载必要的设备。只需要null、zero、urandom、random这几个,绝对不要挂载sda、sdb等块设备节点,更不要挂载/dev/pts(终端设备)。如果用户能访问块设备,他可以直接读写磁盘绕过所有限制。

第三,使用mount的绑定选项加限制。在/etc/fstab里配置时,加上ro(只读)和nosuid、nodev、noexec选项:

/bin  /home/chroot_sftp/bin  none  bind,ro,nosuid,nodev,noexec  0 0
/lib64  /home/chroot_sftp/lib64  none  bind,ro,nosuid,nodev,noexec  0 0

ro保证用户不能往里面写文件,nosuid防止setuid程序提权,nodev防止通过设备文件搞事,noexec禁止在这个挂载点执行任何二进制。这四个选项组合起来,基本把bind挂载的风险压到最低。

第四,考虑用tmpfs替代部分bind挂载。对于/tmp、/var/tmp这类临时目录,直接用tmpfs挂载而不是bind宿主机的,这样即使用户在里面搞事情,重启后全部清空,不会污染宿主机。

mount -t tmpfs -o size=100m,noexec,nosuid,nodev tmpfs /home/chroot_sftp/tmp
五、加固chroot环境的进阶手段

chroot加bind挂载只是第一层,要真正做到安全隔离,还需要叠加以下措施:

1. 降权运行。chroot里的进程绝对不能以root身份运行。创建一个低权限用户专门用于chroot环境,比如uid=10001的sftpuser,并且在sshd_config里用AllowUsers或Match限制只有这个用户能进chroot。

2. 限制资源。在/etc/security/limits.conf里对chroot用户设置资源上限:

sftpuser  hard  nproc  50
sftpuser  hard  nofile  1024
sftpuser  hard  as  256000

3. 使用seccomp过滤系统调用。CentOS 7以上可以通过seccomp-bpf限制进程能调用哪些系统调用,比如禁止mount、pivot_root、ptrace等危险调用。可以用libseccomp库编写过滤规则,或者直接用systemd的SystemCallFilter指令。

4. 配合AppArmor或SELinux。CentOS默认启用SELinux,确保chroot目录的安全上下文正确设置。用restorecon恢复默认标签,或者用semanage fcontext添加自定义策略。AppArmor虽然在CentOS上不是默认的,但也可以安装使用,针对chroot进程写专门的profile。

5. 监控和审计。开启auditd对chroot目录的访问审计,任何异常的文件读取、权限修改都能被记录下来。配置规则:

-w /home/chroot_sftp -p rwxa -k chroot_audit
六、常见坑和我的经验总结

第一个坑:OpenSSH的ChrootDirectory要求目录必须属于root且不能被chroot用户写入。很多人配置完发现ssh连不上,就是因为权限不对。解决方法是确保/home/chroot_sftp的属主是root:root,权限755,里面的用户家目录另设权限。

第二个坑:bind挂载后用户能看到宿主机上所有文件的inode信息,通过/proc/mounts可以看到挂载点列表。所以前面说的不挂载/proc是必须的,如果实在需要某些/proc下的文件,单独复制而不是bind整个目录。

第三个坑:CentOS 8之后用的是OpenSSH 7.4+,ChrootDirectory的行为有变化,要求目录必须是root所有且组不可写。CentOS 7的老版本要求更严格,目录甚至不能有任何写权限。一定要看清楚你的OpenSSH版本。

我个人的建议是:chroot+bind挂载适合做轻量级隔离,比如SFTP文件传输、不可信用户的shell限制。如果你需要真正的强隔离,应该上容器技术(如Podman、Docker)或者KVM虚拟机。chroot的本质是"视图欺骗",不是"安全边界",把它当安全方案用要心里有数。

最后说一句,安全是分层的,没有银弹。chroot解决的是"看不到"的问题,bind挂载解决的是"用得上"的问题,降权和seccomp解决的是"干不了"的问题。把这几层叠在一起,才是一个相对靠谱的隔离方案。