vsftpd writable root inside chroot 的 allow_writeable_chroot=YES

12次阅读

allow_writeable_chroot=yes 的作用是绕过 vsftpd 对 chroot 根目录不可写的强制检查,使用户能登录并上传文件,但需确保目录属主正确、权限可写且 selinux/apparmor 未阻止。

vsftpd writable root inside chroot 的 allow_writeable_chroot=YES

vsftpd 启用 allow_writeable_chroot=YES 的真实作用

这个配置项不是“允许写入根目录”,而是绕过 vsftpd 对 chroot 环境下 / 目录权限的硬性检查。默认情况下,vsftpd 要求 chroot jail 的根目录(即用户登录后的 /)必须不可写(即 chmod -w),否则直接拒绝登录并报错:500 OOPS: vsftpd: refusing to run with writable root inside chroot()

设为 YES 后,vsftpd 就不再校验该目录是否可写,从而让 chroot 用户能正常登录并上传文件——但前提是目录本身确实有写权限,且 SELinux/AppArmor 没拦着。

  • 它只影响 chroot 用户的登录阶段,不改变文件系统级的权限控制
  • 仅对 chroot_local_user=YESchroot_list_enable=YES 生效
  • 启用后,/ 目录仍需属于用户或其组,且有 w 权限,否则 STOR 仍会失败

为什么不能直接 chmod 755 /home/username 就完事?

因为 vsftpd 的原始设计认为:如果 chroot jail 的根是可写的,攻击者可能通过符号链接、硬链接或覆盖关键文件(如 /etc/passwd 的挂载点)逃逸沙箱。所以它宁可拒绝启动,也不妥协。

现代 Linux 发行版(如 CentOS 8+/Ubuntu 20.04+)默认启用了更严格的命名空间隔离,这种风险已大幅降低,但 vsftpd 没跟进,默认行为没变。

  • 你改了 chmod,但没改 allow_writeable_chroot → 登录失败,报错如上
  • 你改了 allow_writeable_chroot=YES,但 /home/username 所属用户不对(比如还是 root:root)→ 登录成功,但上传时提示 553 Could not create file
  • SELinux 开启时,即使权限全对,也可能被 avc denied 拦住,日志在 /var/log/audit/audit.log

allow_writeable_chroot=YES 的安全代价和替代方案

这不是一个“开关式”安全降级,而是一次明确的风险接受:你确认该 chroot 环境里没有其他用户、不运行可疑二进制、且系统补丁及时。它本身不引入漏洞,但移除了 vsftpd 原本的一道防护假定。

  • 真正安全的做法是:保持 allow_writeable_chroot=NO,改用 chroot_local_user=YES + 在用户家目录下建子目录(如 /home/username/ftp),然后 chown username:username /home/username/ftp,再把 vsftpd 的 local_root 指向它
  • 或者用 user_sub_tokenlocal_root=/home/$USER/ftp 实现每人独立可写路径
  • 若必须用 YES,请确保该用户无 shell(/sbin/nologin)、无 SSH 访问、且 FTP 目录外无敏感挂载

调试时最常漏掉的三个检查点

配置写对了,重启也执行了,但还是连不上或传不了——大概率卡在这三处:

  • vsftpd.confchroot_local_user=YES 必须显式开启,仅靠 allow_writeable_chroot=YES 不生效
  • 用户主目录(如 /home/username)的 owner 必须是该用户,不能是 root;若用 userlist_enable=YES,还要确认该用户不在 /etc/vsftpd/user_list/etc/vsftpd/ftpusers
  • systemd 下改完配置要 sudo systemctl restart vsftpd,别只 kill -HUP;且检查 sudo journalctl -u vsftpd -n 50 有没有 parse error 或 bind failure

chroot + 可写根这事,表面是权限开关,实际是权限模型、进程上下文、内核安全模块三者对齐的结果。少对一环,就卡在“明明配了却不行”上。

text=ZqhQzanResources