ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

vsftpd权限配置实战:不同用户不同目录权限的三种方案

vsftpd权限配置实战:不同用户不同目录权限的三种方案

1. 从一个真实运维场景说起:为什么共享目录的权限总在“打架”

但凡在Linux环境下做过文件共享服务的人,大概率都遇到过这样一种局面:团队里几个同事需要往同一台服务器上传资料,有人只能看不能改,有人只能往自己目录里丢文件,还有人需要读写全部目录。听起来需求很朴素,但真动手配起来,往往就是“改完这个坏那个”。

我印象很深的一次,是帮一个做设计协作的小团队整理文件服务器。他们的诉求特别典型:三个角色,管理员要能读写所有目录,设计师只能读写自己的目录,外包人员只能上传不能下载别人的东西。最开始有人图省事,直接给所有人开了系统账号,然后靠目录的chmod 777硬扛。结果没两天就出问题了——外包人员误删了设计师的源文件,因为目录权限太松,谁都能动。

这就是vsftpd这类FTP服务在真实场景里的核心痛点:用户隔离和目录权限控制。vsftpd本身功能不算花哨,但它的权限模型其实相当灵活,关键在于你得搞清楚它有几层“锁”——系统用户层、虚拟用户层、目录归属层、配置文件层,每一层管什么、怎么配合,想明白了配置就是水到渠成的事。

这篇内容我打算把“不同用户不同目录权限”这件事彻底讲透。不管你是刚接手一台文件服务器的新手,还是被权限问题折腾过几轮的老运维,都能从里面找到可以直接抄的配置和踩过的坑。核心会围绕vsftpd的三种典型权限控制方案展开:本地用户隔离、虚拟用户映射、以及基于目录归属的精细控制。全程用可复现的步骤说话,参数为什么这么设,我也会一并交代清楚。

2. 先把vsftpd的权限模型拆开看:三层锁各管什么

很多人配vsftpd配得稀里糊涂,根本原因是没分清“谁在管权限”。vsftpd的权限其实是被三套机制叠加控制的,任何一层没对齐,最终效果就会和你预期的不一样。我把它拆成三层来讲,理解了这三层,后面所有配置你都能自己推导出来。

2.1 第一层:系统用户与虚拟用户的身份差异

vsftpd登录的用户,本质上分两类。一类是本地用户,也就是系统里真实存在的账号,比如你用useradd建出来的那些。这类用户登录后,默认会进入自己的家目录,权限直接受Linux文件系统那套rwx规则约束。另一类是虚拟用户,它们不是系统账号,而是存在一个独立的账号密码文件里,登录时由vsftpd读取校验,然后“映射”到一个指定的系统用户身份上去操作文件。

这个区别非常关键。本地用户的好处是简单直接,家目录天然隔离;坏处是每加一个人就得建一个系统账号,账号一多管理起来很烦,而且这些账号理论上还能用来登录系统(虽然可以限制)。虚拟用户的好处是账号集中管理、不污染系统用户列表、可以给成百上千个用户共用同一个系统身份,特别适合“只做文件传输、不需要登录系统”的场景。

我个人的经验是:人数少、需要各自独立家目录的,用本地用户;人数多、权限规则统一的,用虚拟用户。当然两者也能混用,但混用会让权限排查变复杂,新手不建议一上来就这么干。

2.2 第二层:目录归属与文件系统权限的决定作用

这一层是最容易被忽略的。vsftpd再怎么配,最终能不能写文件,还是要看Linux文件系统同不同意。也就是说,FTP的权限配置只是“允许你尝试”,文件系统的rwx才是“最终拍板”。

举个具体的例子。假设你把某个虚拟用户映射到了系统用户ftpuser,然后配置里写了write_enable=YES,看起来是允许写入了。但如果目标目录的属主是root,权限是755,那ftpuser作为“其他人”只有r-x权限,照样写不进去。这时候你会看到FTP客户端报“550 Permission denied”,然后一头雾水地去翻vsftpd配置,其实问题根本不在那儿。

所以配权限的正确顺序应该是:先规划目录归属和文件系统权限,再配vsftpd。目录属主是谁、属组是谁、其他人有没有写权限,这些想清楚了,vsftpd那边只是把开关打开而已。

2.3 第三层:vsftpd配置文件里的关键开关

vsftpd的主配置文件通常在/etc/vsftpd/vsftpd.conf或/etc/vsftpd.conf,不同发行版路径略有差异。里面和权限相关的开关有一批,我挑最核心的几个说:

配置项作用常见取值
local_enable是否允许本地用户登录YES/NO
write_enable全局写开关YES/NO
local_umask本地用户上传文件的权限掩码022/077
chroot_local_user是否把本地用户锁在家目录YES/NO
allow_writeable_chroot允许被锁的家目录可写YES/NO
userlist_enable是否启用用户黑白名单YES/NO
userlist_deny名单是黑名单还是白名单YES/NO

这里面有个特别经典的坑:chroot_local_user=YES把用户锁在家目录后,如果家目录本身对用户是可写的,vsftpd出于安全考虑会拒绝登录,报“500 OOPS: vsftpd: refusing to run with writable root inside chroot”。解决办法要么把家目录设成不可写(但这样用户就没法在家目录建文件了),要么加allow_writeable_chroot=YES。这个坑我后面还会专门讲。

理解了这三层,你就明白为什么“不同用户不同目录权限”这件事不能只改一个地方。接下来我按三种典型方案,把配置一步步落地。

3. 方案一:本地用户各自家目录隔离,最省心的基础玩法

如果你的需求是“每个用户有自己的独立目录,互相看不到,各自能读写自己的”,那本地用户方案是最直接的。这个方案配置量最小,逻辑也最清晰,适合十人以内的小团队。

3.1 建用户、规划目录结构

先建用户。假设我们要建三个用户:alice、bob、carol。用useradd建的时候,顺手把家目录也建出来:

useradd -m -d /data/ftp/alice -s /sbin/nologin alice useradd -m -d /data/ftp/bob -s /sbin/nologin bob useradd -m -d /data/ftp/carol -s /sbin/nologin carol

这里有几个细节值得说。-m是自动建家目录,-d指定家目录路径,我特意把它们放在/data/ftp/下面统一管理,而不是默认的/home/,这样备份和迁移都方便。-s /sbin/nologin是把登录shell设成不可登录,因为这些账号只用来传文件,不需要SSH登录系统,这是基本的安全习惯。

建完之后给每个家目录设权限。这里有个取舍:如果家目录属主是用户自己、权限755,那用户能在家目录里建文件删文件,但chroot之后vsftpd会因为“可写根目录”报错。所以要么用allow_writeable_chroot=YES放行,要么把家目录设成555再在里面建一个可写的子目录。我一般推荐后者,更安全:

chown alice:alice /data/ftp/alice chmod 555 /data/ftp/alice mkdir /data/ftp/alice/upload chown alice:alice /data/ftp/alice/upload chmod 755 /data/ftp/alice/upload

这样用户登录后落在/data/ftp/alice(只读),但可以进upload目录读写。这个结构在实际使用中很稳,用户也不会因为误操作把家目录搞乱。

3.2 配置文件的关键几行

主配置文件里,本地用户方案需要打开这些开关:

local_enable=YES write_enable=YES local_umask=022 chroot_local_user=YES allow_writeable_chroot=YES

local_umask=022意味着上传的文件权限是644、目录是755,这是比较通用的设置。如果你希望上传的文件更私密,可以设成077,那样只有属主能读写,但要注意这会影响同组用户的访问。

chroot_local_user=YES是隔离的核心,它把用户锁在自己的家目录里,登录后/就是家目录,看不到系统其他路径。这个开关一开,用户之间的隔离就天然实现了——alice看不到bob的目录,因为她的根就是自己的家目录。

3.3 实测中容易翻车的两个点

第一个坑是家目录的父目录权限。chroot之后,vsftpd需要能“穿过”家目录的父目录。如果/data/ftp/的权限是700且属主是root,那用户chroot进去后可能因为无法访问父目录而报错。稳妥的做法是把父目录设成755:

chmod 755 /data /data/ftp

第二个坑是SELinux。在开启了SELinux的系统上(比如某些默认配置的发行版),即使文件系统权限都对,FTP写入还是会被拦。这时候需要给FTP目录打上正确的SELinux上下文:

setsebool -P ftp_home_dir on chcon -R -t public_content_rw_t /data/ftp

如果不想折腾SELinux,也可以临时用setenforce 0验证是不是它在捣乱,但生产环境不建议直接关掉,还是老老实实配上下文。

4. 方案二:虚拟用户映射,一套系统身份管住一群人

当用户数量上来了,或者你压根不想为每个FTP用户建系统账号,虚拟用户就是更优雅的选择。它的核心思路是:所有虚拟用户共用一个系统身份(比如vftp),登录时vsftpd校验虚拟账号密码,然后以vftp的身份去操作文件。权限控制则通过“每个虚拟用户对应不同的目录”来实现。

4.1 虚拟用户的账号文件怎么组织

虚拟用户的账号密码存在两个文件里:一个明文文件(用来生成数据库),一个数据库文件(vsftpd实际读取的)。明文文件格式是“一行用户名、一行密码”交替:

# /etc/vsftpd/vusers.txt alice alice_password bob bob_password carol carol_password

然后用db_load把它转成数据库:

db_load -T -t hash -f /etc/vsftpd/vusers.txt /etc/vsftpd/vusers.db chmod 600 /etc/vsftpd/vusers.db

数据库文件权限一定要收紧到600,因为它包含了所有用户的密码。明文文件生成完数据库后建议删掉或妥善保管,别留在原地。

4.2 PAM配置:让vsftpd认识虚拟用户

vsftpd通过PAM来校验虚拟用户,需要写一个PAM配置文件,通常放在/etc/pam.d/vsftpd.virtual:

auth required pam_userdb.so db=/etc/vsftpd/vusers account required pam_userdb.so db=/etc/vsftpd/vusers

注意db=后面的路径不带.db后缀,pam_userdb会自动补上。然后在vsftpd主配置里指定用这个PAM文件:

pam_service_name=vsftpd.virtual guest_enable=YES guest_username=vftp

guest_enable=YES是开启虚拟用户映射的总开关,guest_username=vftp指定所有虚拟用户映射到的系统身份。这个vftp账号需要提前建好:

useradd -d /data/vftp -s /sbin/nologin vftp

4.3 用user_config_dir实现“一人一目录一权限”

虚拟用户方案最漂亮的地方,是可以用user_config_dir给每个虚拟用户单独配权限。在主配置里加一行:

user_config_dir=/etc/vsftpd/vusers_conf

然后在这个目录下,为每个虚拟用户建一个同名配置文件。比如/etc/vsftpd/vusers_conf/alice:

local_root=/data/vftp/alice write_enable=YES anon_upload_enable=YES anon_mkdir_write_enable=YES

local_root指定这个用户登录后落在哪个目录,这就实现了“不同用户不同目录”。bob的配置文件里local_root指向/data/vftp/bob,以此类推。

如果某个用户只读,就把write_enable设成NO;如果某个用户只能上传不能删除,可以配合anon_upload_enable=YES和anon_other_write_enable=NO。这种“一人一文件”的方式,权限粒度可以做到非常细。

4.4 目录归属必须对齐映射身份

这里有个必须强调的点:所有虚拟用户最终都是以vftp的身份操作文件,所以每个用户目录的属主都必须是vftp,否则写不进去:

mkdir -p /data/vftp/alice /data/vftp/bob /data/vftp/carol chown -R vftp:vftp /data/vftp chmod 755 /data/vftp/alice /data/vftp/bob /data/vftp/carol

我见过有人把目录属主设成root,然后纳闷为什么虚拟用户上传失败。原因就是vftp对root属主的目录没有写权限。记住这条:虚拟用户方案里,目录属主跟着guest_username走。

5. 方案三:共享目录+差异化权限,最难也最实用的组合

前面两个方案都是“各管各的目录”,但真实场景里经常有共享需求:一个公共目录,管理员能读写删,普通用户只能读,外包只能上传。这种“同一目录、不同人不同权限”的需求,才是权限控制真正考验功力的地方。

5.1 用属组来区分权限层级

Linux文件系统权限只有“属主、属组、其他人”三档,要实现多层级权限,最实用的办法是用属组划分人群。假设我们建两个组:ftp_admin和ftp_user。

groupadd ftp_admin groupadd ftp_user usermod -aG ftp_admin alice usermod -aG ftp_user bob usermod -aG ftp_user carol

共享目录/data/share的规划是这样的:管理员组能读写执行,普通用户组只能读和执行,其他人无权限。

mkdir /data/share chown root:ftp_admin /data/share chmod 750 /data/share

750的含义是:属主root有rwx,属组ftp_admin有r-x,其他人无权限。但这样ftp_user组的人就完全进不去了,不符合需求。所以还得给ftp_user组单独授权,这时候就要用到ACL(访问控制列表)。

5.2 ACL:突破三档权限限制的利器

ACL允许你给额外的用户或组单独设权限,不受“属主/属组/其他人”三档的限制。给ftp_user组加上读和执行权限:

setfacl -m g:ftp_user:r-x /data/share setfacl -m d:g:ftp_user:r-x /data/share

第一条是给当前目录加ACL,第二条d:前缀是设默认ACL,让以后在目录里新建的文件和子目录自动继承这个权限。这两条配合使用,才能保证权限“传下去”。

查看ACL用getfacl:

getfacl /data/share

你会看到除了常规的user、group、other,还多了group:ftp_user:r-x这样的条目。这就是ACL生效的标志。

5.3 外包人员“只能上传不能下载”怎么实现

“只能上传不能下载”这个需求听起来矛盾,其实在FTP场景里很常见——比如收集作业、收集素材,不希望上传者看到别人传的东西。实现思路是:给目录写权限,但不给读权限。

在Linux里,目录的w权限允许创建和删除文件,x权限允许进入目录,r权限允许列出目录内容。所以“能上传不能看”对应的权限组合是-wx,也就是写和执行,但没有读。

mkdir /data/dropbox chown root:ftp_drop /data/dropbox chmod 730 /data/dropbox

730意味着属主rwx,属组-wx,其他人无权限。把外包人员加进ftp_drop组,他们就能往目录里传文件,但ls列不出内容,也下载不了别人的文件。这个技巧非常实用,我第一次用的时候还担心不生效,实测下来完全符合预期。

不过要注意,如果外包人员知道确切文件名,理论上还是能通过get命令下载——因为x权限允许访问已知路径。要彻底堵死,得配合vsftpd的download_enable=NO,但那是全局的,会影响所有人。所以这个方案适合“防君子不防小人”的场景,真要严格管控,还是得靠应用层做上传接口。

5.4 把共享目录和虚拟用户结合起来

如果你的用户是虚拟用户,共享目录的权限控制会稍微绕一点,因为所有虚拟用户都是vftp身份,文件系统层面区分不了他们。这时候有两个思路:

一是用不同的guest_username。vsftpd支持在单个用户的配置文件里覆盖guest_username,也就是说你可以让alice映射到vftp_admin,bob映射到vftp_user,这样文件系统层面就能用属组区分了。配置写在各自的user_config_dir文件里:

# /etc/vsftpd/vusers_conf/alice guest_username=vftp_admin local_root=/data/share

二是完全靠vsftpd的权限开关。比如给只读用户设write_enable=NO,给可写用户设write_enable=YES,共享同一个目录。这种方式简单,但粒度粗,没法做到“能上传不能下载”这种精细控制。

我一般推荐第一种,虽然多建几个系统账号,但权限模型清晰,排查问题也容易。

6. 权限不生效时的排查链路:从现象倒推原因

配置这东西,写对了皆大欢喜,写错了就得靠排查。我把这些年遇到的权限问题整理成一条排查链路,你按顺序走一遍,基本能定位到问题所在。

6.1 第一步:确认登录本身是否成功

如果连登录都失败,先别管权限。看vsftpd日志,通常在/var/log/vsftpd.log或/var/log/secure。常见登录失败原因有:PAM配置路径写错、数据库文件权限不对、guest_username指定的系统账号不存在。日志里一般会明确写“authentication failure”或“user not found”。

6.2 第二步:确认chroot是否报错

登录成功但立刻断开,报“500 OOPS: vsftpd: refusing to run with writable root inside chroot”,这就是前面说的可写根目录问题。解决要么加allow_writeable_chroot=YES,要么把家目录改成不可写。

6.3 第三步:确认目录归属和文件系统权限

登录进去了,但ls看不到东西或者写不进去,先退出来在系统层面检查:

ls -ld /data/vftp/alice getfacl /data/vftp/alice

看属主是不是映射身份、权限位对不对、ACL有没有生效。这一步能解决八成以上的“权限不生效”问题。

6.4 第四步:确认SELinux和防火墙

如果文件系统权限全对,还是不行,检查SELinux:

getenforce ls -Z /data/vftp/alice

如果是Enforcing状态且上下文不对,用chcon或semanage修正。防火墙一般影响的是连接而不是权限,但如果被动模式端口没开,会出现“能登录但列不出目录”的现象,这时候要检查pasv_min_port和pasv_max_port是否在防火墙放行。

6.5 第五步:用最小配置验证

如果以上都排查了还是不行,我会用一个“最小可用配置”来验证。把主配置精简到只剩必要项,去掉所有花哨的开关,确认基础功能通了,再一项项加回去。这样能快速定位是哪个配置项在捣乱。这个方法虽然笨,但极其有效,我靠它定位过好几次诡异的权限问题。

7. 几个我踩过之后才明白的实操心得

配置写完了,最后分享几个文档里不会写、但实际用起来特别重要的经验。

关于umask的选择。local_umask=022是通用值,但如果你希望上传的文件默认只有属主能改,同组能读,其他人不能碰,可以用027。这个值没有绝对的对错,取决于你的协作模式。我一般会问清楚“上传的文件要不要给同组人看”,再决定umask。

关于日志的粒度。vsftpd默认日志信息不算详细,建议打开xferlog_enable=YES和log_ftp_protocol=YES,这样每次上传下载都有记录,出问题时有据可查。日志文件记得配logrotate,不然时间长了会把磁盘撑满。

关于被动模式端口范围。如果服务器在防火墙后面,一定要显式指定被动模式端口范围,并在防火墙放行:

pasv_enable=YES pasv_min_port=30000 pasv_max_port=30100

不指定的话,vsftpd会随机用高端口,防火墙根本没法放行,客户端就会卡在“列目录”那一步。

关于备份配置文件。每次改vsftpd配置前,先cp vsftpd.conf vsftpd.conf.bak。这个习惯救过我很多次,尤其是改到一半发现方向错了,直接回滚比一点点改回去快得多。

关于测试顺序。配完权限后,一定要用每个角色的账号实际登录测一遍,别只看配置文件觉得“应该没问题”。我见过太多“配置看着对、实际跑不通”的情况,原因往往是某个目录的属主没改、某条ACL没生效。实测是唯一的真理。

这套方案我在不同规模的场景里都用过,小到三五个人的工作室,大到几十个账号的部门共享,核心逻辑都是一样的:先想清楚谁需要什么权限,再规划目录归属,最后配vsftpd开关。顺序反了,就会陷入“改了这里坏了那里”的循环。希望这些内容能帮你少走点弯路,一次把权限配到位。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进