十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零搭建安全可控的vsftpd FTP服务器:虚拟用户与权限隔离实战

从零搭建安全可控的vsftpd FTP服务器:虚拟用户与权限隔离实战 1. 项目概述与需求拆解1.1 为什么现在还要自己搭FTP服务器聊到FTP服务器很多人第一反应是这玩意儿还有必要自己搭吗网盘、微信传文件不都挺方便说实话我搞Linux运维这几年FTP这老伙计还真没退役。尤其是企业内部的文件分发、服务器之间的数据交换、嵌入式开发板的固件推送、还有那些需要给客户开个临时账号让对方自己拉取文件的场景FTP依然是不可替代的轻量方案。它够简单、够直接一条命令就能装好一个配置文件就能跑起来不像搭个NextCloud或者Seafile那么重。这个实战项目的核心目标是在一台干净的Linux服务器上从零开始搭建一个功能完整、权限可控、能应对日常生产需求的FTP服务器。内容覆盖环境准备、服务安装、虚拟用户配置、权限隔离、防火墙放行、被动模式设置、调试排错、安全加固这几个关键环节基本把我在生产环境里踩过的坑都揉进去了。适合谁来参考刚接触Linux服务器运维的同学可以用它当入门实操手册有一定基础但只配过匿名FTP的朋友重点看用户权限和被动模式这两节企业里需要快速搭建内部文件服务的人安全加固那一节能帮你少走不少弯路。1.2 方案选型vsftpd为什么是默认答案Linux平台可用的FTP服务端不少Pure-FTPd、ProFTPD、vsftpd三足鼎立。我最终选择vsftpd不是因为它名字里带very secure而是这么多年生产环境用下来它的稳定性和安全性确实最能打。vsftpd的设计哲学是安全优先、性能其次它在处理权限隔离、进程降权、chroot限制这些安全特性时做得非常彻底。官方文档里有一句话我一直印象很深vsftpd被设计为在尽可能少的权限下运行每一个功能都从攻击面最小的角度去实现。这种偏执在实际使用中体现得很明显——默认配置下连匿名写入都是关闭的目录切换被严格限制在FTP根目录内进程间通信用的是Unix域套接字而不是普通的网络套接字。Pure-FTPd强在配置简洁适合快速部署ProFTPD胜在模块化设计想要什么功能都能插拔。但如果你追求的是配好之后丢在那儿跑一两年不用管的省心效果vsftpd绝对是最优解。而且我用的CentOS系发行版、Debian系发行版软件源里都有现成的包一条命令装完就能用不用处理乱七八糟的依赖关系。2. 环境准备与服务安装2.1 实验环境说明与初始化拿我这次实操的这台机器来说系统是CentOS 7.9 x86_64内核版本3.10.0内存2G磁盘40G网络模式是桥接服务器IP是192.168.1.108。这个环境比较有代表性很多企业里还在服役的旧服务器就是这个配置水平。如果你用的是Ubuntu 20.04或22.04命令稍微换个包管理器就行下文我会在关键步骤处标注差异。先把系统环境收拾干净确保没有残留的旧FTP服务占用端口# 检查端口占用情况 ss -tnlp | grep -E :21|:20 # 如果有输出说明已有FTP服务在运行需要先停掉 systemctl stop vsftpd 2/dev/null systemctl disable vsftpd 2/dev/null # 查看系统版本和架构确认环境信息 cat /etc/redhat-release uname -a同时检查一下SELinux的状态这是个很容易被忽略的坑# 查看SELinux状态 getenforce如果输出是Enforcing建议先临时设置为Permissive模式否则后面配置完FTP大概率会遇到530 Login incorrect这类权限问题# 临时关闭SELinux重启后失效 setenforce 0 # 永久关闭则修改配置文件 sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config提示生产环境不建议永久关闭SELinux。如果你担心安全可以单独为FTP服务配置SELinux布尔值后面会讲到具体方法。2.2 安装vsftpd并确认服务状态CentOS系用yum安装Debian系用apt安装这里以CentOS为例# 安装vsftpd yum install -y vsftpd # 启动服务并设置开机自启 systemctl start vsftpd systemctl enable vsftpd # 查看服务运行状态 systemctl status vsftpd看到类似这样的输出就说明服务正常启动了● vsftpd.service - Vsftpd ftp daemon Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2024-01-06 14:23:45 CST; 3s ago再确认一下21号端口已经监听ss -tnlp | grep ftp # 输出类似LISTEN 0 32 *:21 *:* users:((vsftpd,pid1234,fd0))到这里一个最基础、只能通过匿名方式访问的FTP服务已经跑起来了。但离能用还差得远——默认配置下匿名用户只能下载不能上传而且数据目录在/var/ftp/pub下面权限控制几乎为零。接下来要做的就是把它改造成支持虚拟用户、权限可控、安全可靠的生产级FTP服务。3. 用户体系设计与权限隔离3.1 为什么选择虚拟用户而不是系统用户FTP服务器的用户体系有三种做法匿名用户、系统用户、虚拟用户。新手最容易犯的错误是图省事直接拿系统用户给FTP用这样有两个隐患一是FTP用户和系统登录用户混在一起管理混乱二是系统用户的权限边界很难收紧一个配置失误就可能把整个服务器的文件权限暴露出去。虚拟用户的思路是创建一批只存在于FTP服务中的影子账号这些账号不能登录系统shell不占用系统用户资源密码加密存储在独立的数据库中。vsftpd通过PAM模块读取这些虚拟用户的认证信息完成登录验证后再映射到指定的系统用户和目录上。这样做的好处非常明显虚拟用户与系统用户完全隔离即使FTP被攻破攻击者拿到的也只是一个受限的shell环境每个虚拟用户都能绑定独立的目录和权限客户之间互相看不到对方的文件用户数量再多也不会污染系统用户数据库管理起来清爽不少3.2 创建虚拟用户数据库vsftpd的虚拟用户有两种存储方式一种是传统的Berkeley DB文本库另一种是MySQL数据库。生产环境用户量不大时用Berkeley DB就够了配置简单、性能可靠、不依赖外部服务。用户量上百、需要频繁增删账号时才考虑接入数据库。先安装db_load工具用来生成Berkeley DB格式的密码库yum install -y db4-utilsDebian系对应的是db-util命令名也略有差异apt install -y db-util # 生成命令是 db5.3_load创建纯文本格式的用户密码文件mkdir -p /etc/vsftpd vim /etc/vsftpd/virtual_users.txt文件内容格式为一行用户名、一行密码比如创建两个用户一个用于业务部门上传下载一个用于给外部客户下载biz_user biz_pass_2024 guest_user guest_pass_2024第一行用户名第二行密码第三行用户名第四行密码……依次类推。然后把这个文本文件转换成Berkeley DB格式db_load -T -t hash -f /etc/vsftpd/virtual_users.txt /etc/vsftpd/virtual_users.db chmod 600 /etc/vsftpd/virtual_users.db /etc/vsftpd/virtual_users.txt注意每次修改了virtual_users.txt文件都必须重新执行db_load命令重新生成数据库文件否则新增的账号不会生效。我就是因为忘了这一步曾经排查了整整一个下午为什么新添加的用户登录不了。3.3 配置PAM认证模块vsftpd默认使用/etc/pam.d/vsftpd这个PAM配置文件来认证本地系统用户。我们要改成使用自己创建的虚拟用户库来认证。备份并修改PAM配置文件mv /etc/pam.d/vsftpd /etc/pam.d/vsftpd.bak vim /etc/pam.d/vsftpd写入以下内容auth required pam_userdb.so db/etc/vsftpd/virtual_users account required pam_userdb.so db/etc/vsftpd/virtual_users这两行的作用分别是认证阶段检查用户名和密码是否匹配、账号阶段检查该账号是否允许登录。db路径后面不需要加.db后缀pam_userdb模块会自动补全。3.4 搭建虚拟用户映射的系统账号虚拟账号认证通过后vsftpd需要把它们映射到一个实际的系统账号上以此确定文件的实际属主和权限范围。创建专用系统用户useradd -d /home/ftp_root -s /sbin/nologin ftp_vuser参数说明-d 指定家目录这里设置为/home/ftp_root作为FTP服务的总根目录-s /sbin/nologin 禁止该用户登录系统shell即使是纯命令行登录也不行这个家目录非常关键它将成为所有虚拟用户FTP根目录的物理落点。3.5 配置vsftpd主配置文件vsftpd的主配置文件是/etc/vsftpd/vsftpd.conf。在动手改之前先备份原始配置方便出问题时回滚cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak然后清空原文件写入以下完整配置vim /etc/vsftpd/vsftpd.conf# 监听配置 listenYES listen_ipv6NO # 匿名访问控制 anonymous_enableNO # 本地用户访问控制 local_enableYES write_enableYES # 虚拟用户配置 guest_enableYES guest_usernameftp_vuser virtual_use_local_privsYES # 权限与目录隔离 chroot_local_userYES allow_writeable_chrootYES # 文件权限掩码 local_umask022 file_open_mode0755 # 被动模式配置 pasv_enableYES pasv_min_port40000 pasv_max_port40010 pasv_address192.168.1.108 # 登录状态与日志 dirmessage_enableYES xferlog_enableYES xferlog_file/var/log/vsftpd.log xferlog_std_formatYES log_ftp_protocolYES # 会话超时 idle_session_timeout600 data_connection_timeout120 # 端口及并发 connect_from_port_20YES max_clients50 max_per_ip5这段配置里的几个关键点我逐个展开说明。3.5.1 guest_enable与guest_username的配合逻辑guest_enableYES告诉vsftpd启用虚拟用户模式。guest_usernameftp_vuser指定了所有通过虚拟账号认证的用户最终都会以系统用户ftp_vuser的身份读写文件。也就是说不管你在FTP端登录的是biz_user还是guest_user落到操作系统层面实际文件属主都是ftp_vuser。3.5.2 virtual_use_local_privs的权限语义这个参数决定了虚拟用户是否拥有与本地用户相同的权限。设为YES表示虚拟用户拥有本地用户权限可以执行上传、删除、重命名等写操作。设为NO的话虚拟用户会被当作匿名用户对待只能下载不能上传。如果你只想提供下载服务把这项设为NO配合匿名禁用可以做到只能下不能传非常安全。3.5.3 chroot_local_user与allow_writeable_chrootchroot_local_userYES将用户锁定在各自的FTP根目录内不允许切换到根目录之外的其他目录。这是FTP安全的基本保障——否则用户登录后可以cd /etc把系统配置文件下载走那画面太美不敢看。但这里有个坑CentOS 7.9的vsftpd版本2.2.2默认不允许FTP根目录本身对用户可写。如果用户主目录权限是777登录时就会报500 OOPS: vsftpd: refusing to run with writable root inside chroot()错误。allow_writeable_chrootYES就是用来解除这个限制的。从安全角度两个办法都可以保留allow_writeable_chrootYES但把FTP根目录的属主改为root只对虚拟用户开放子目录的写权限不设置allow_writeable_chroot把FTP根目录设为755保证不可写我生产中习惯用第一种方案这样既能保证用户可以在自己的目录里自由读写又不会因为根目录本身可写引入目录穿越风险。3.5.4 被动模式的端口规划FTP天生的双通道机制决定了它必须处理主动模式和被动模式两种连接方式。主动模式下服务器主动连接客户端的随机端口这在客户端处于NAT或防火墙之后时经常失败。被动模式则是服务器开放一个随机端口等待客户端来连接穿越NAT的能力强得多。生产环境我无一例外选用被动模式并手动指定端口范围pasv_enableYES pasv_min_port40000 pasv_max_port40010 pasv_address192.168.1.108端口范围开11个是因为每一条数据传输连接占用一个端口需要评估并发传输量。如果并发传输量大可以适当扩大范围比如40000到40100。pasv_address这一项在生产环境必须设为服务器对外的IP地址否则客户端收到的被动模式地址会是服务器的内网IP甚至回环地址直接连不上。4. 目录结构与多用户目录映射4.1 设计FTP根目录结构为了让不同用户访问到各自的目录同时又能把公共目录共享给多个用户我把/home/ftp_root下的目录结构设计成这个样子mkdir -p /home/ftp_root mkdir -p /home/ftp_root/biz_data mkdir -p /home/ftp_root/guest_data mkdir -p /home/ftp_root/publicbiz_data业务用户的专属目录只有biz_user能读写guest_data客户用户的专属目录只有guest_user能读写public公共目录所有虚拟用户都能上传下载4.2 对应用户目录的用户配置vsftpd支持通过独立的用户配置文件来定义每个虚拟用户的专属设置。创建用户配置目录mkdir -p /etc/vsftpd/vconf在主配置文件里加上一行让vsftpd读取这个目录中的独立配置echo user_config_dir/etc/vsftpd/vconf /etc/vsftpd/vsftpd.conf现在创建biz_user的独立配置vim /etc/vsftpd/vconf/biz_userlocal_root/home/ftp_root/biz_data同理创建guest_user的独立配置vim /etc/vsftpd/vconf/guest_userlocal_root/home/ftp_root/guest_data注意两个关键点配置文件名必须与虚拟用户名完全一致没有后缀local_root指定的目录必须存在否则用户登录后会被强制回到系统根目录因为chroot限制表现为登录异常4.3 目录权限精细控制给目录设定合适的属主和权限位# 总根目录属主改为root权限755防止目录穿越 chown root:ftp_vuser /home/ftp_root chmod 755 /home/ftp_root # 各业务目录属主设为ftp_vuser权限775 chown ftp_vuser:ftp_vuser /home/ftp_root/biz_data chown ftp_vuser:ftp_vuser /home/ftp_root/guest_data chown ftp_vuser:ftp_vuser /home/ftp_root/public chmod 775 /home/ftp_root/biz_data /home/ftp_root/guest_data /home/ftp_root/public为什么要给总根目录设755且属主是root这就是前面提到的chroot安全机制用户被锁定在FTP根目录之后如果根目录本身对用户可写vsftpd出于安全考虑会拒绝服务或者存在目录穿越风险。755权限下用户进了自己的子目录随意读写但想往FTP根目录本身塞文件是做不到的。上传的文件默认权限是多少这由local_umask022决定创建的新文件权限是666-022644即所有者可读写、其他人只读新目录权限是777-022755。如果你希望上传的文件默认不可被其他人读取可以把umask设为027生成文件权限就是640目录权限750。4.4 验证虚拟用户登录与目录隔离重启vsftpd使配置生效systemctl restart vsftpd用biz_user登录测试ftp 192.168.1.108 Connected to 192.168.1.108. 220 (vsFTPd 3.0.2) Name (192.168.1.108:root): biz_user 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp pwd 257 / ftp ls 200 PORT command successful. Consider using PASV.看到pwd返回的是/说明chroot生效了用户被锁定在自己的FTP根目录看不到服务器的其他路径。再用guest_user登录会发现直接进入的是guest_data目录两个用户之间互不干扰。5. 防火墙放行与客户端验证5.1 防火墙规则配置vsftpd服务装好并配置完成后真正容易翻车的环节是防火墙。FTP需要放行两个维度的端口21端口控制连接被动模式端口段数据连接按之前的配置是40000-40010CentOS 7默认使用firewalld配置如下firewall-cmd --permanent --add-port21/tcp firewall-cmd --permanent --add-port40000-40010/tcp firewall-cmd --reload查看规则是否生效firewall-cmd --list-ports # 输出应包含 21/tcp 40000-40010/tcp如果系统用的是iptables对应命令是iptables -I INPUT -p tcp --dport 21 -j ACCEPT iptables -I INPUT -p tcp --dport 40000:40010 -j ACCEPT iptables-save /etc/sysconfig/iptables5.2 客户端连接验证Windows与Linux双端实测验证客户端连接时我习惯Windows和Linux各测一次因为两个平台的FTP客户端在处理被动模式时存在差异。Windows下用资源管理器直接访问ftp://192.168.1.108弹出认证窗口后输入biz_user及密码。正常的话应该能看到biz_data目录下的文件列表。再试试上传一个文件右键选择复制到文件夹就能验证写权限。Linux命令行下测试curl ftp://192.168.1.108/biz_data/ --user biz_user:密码能看到目录列表就说明下载权限正常。上传测试用curl -T test.txt ftp://192.168.1.108/biz_data/ --user biz_user:密码返回226 Transfer complete说明上传成功。5.3 被动模式不通的排查思路客户端连接时报错227 Entering Passive Mode后卡住不动十有八九是被动模式的端口没放通。这时候按顺序排查确认vsftpd配置中pasv_min_port和pasv_max_port是否已设置确认防火墙是否放行该端口段确认pasv_address是否设置为客户端可达的IP用nc或telnet手动测试端口连通性nc -zv 192.168.1.108 40000-40010第4步能直接验证从客户端到服务器被动端口段的网络连通性。如果nc显示Connection refused或者超时说明问题出在防火墙或安全组规则上。6. 常见问题与实战排查记录6.1 530 Login incorrect问题这是虚拟用户部署中最常见的问题登录时报530。可能原因按优先级排查排查项操作方法说明PAM配置错误检查/etc/pam.d/vsftpd中的db路径db路径不能含.db后缀密码库未更新重新执行db_load修改账号后必须重新生成SELinux拦截setenforce 0临时验证确认是否SELinux引起系统用户不存在确认ftp_vuser是否创建guest_username指定的用户必须存在密码文件格式错误检查virtual_users.txt不能有空格和空行注意编码我遇到最多的情况就是改了系统时间后PAM模块的密码库校验失败清理掉virtual_users.db重新生成就好了。6.2 500 OOPS: vsftpd: refusing to run with writable root inside chroot()这个错误字面意思是vsftpd拒绝在chroot内运行可写的根目录。出现原因虚拟用户的FTP根目录local_root指定的目录权限是777、属主也是该用户在系统层面的映射用户。两个解决方案方案一 在主配置中加入allow_writeable_chrootYES 方案二 把local_root目录属主改为root权限改为755 chown root:root /home/ftp_root/biz_data chmod 755 /home/ftp_root/biz_data方案二更安全因为用户虽然在根目录内不可写但可以在子目录中自由读写。如果你的用户确实需要在根目录直接写文件那就用方案一同时确保其他安全措施到位。6.3 被动模式下文件传输卡死的真实案例之前帮一个客户排查过这个问题。现象很典型本地能连接、能认证、能列目录但一执行上传或下载就卡死几十秒后报超时。排查过程服务器防火墙的40000-40010端口段已经放行客户端Windows防火墙也关了仍然卡死用tcpdump抓包发现客户端发SYN到服务器的40000端口服务器没回SYN-ACK问题出在云平台的安全组上。客户用的是某云主机安全组规则只放行了22和21端口数据端口段完全没配。补充放行40000-40010之后问题立刻消失。这个案例给了一个深刻教训排查FTP被动模式问题时不要只盯着服务器本身的防火墙云平台安全组、物理机房防火墙、交换机ACL都要检查。网络链路中的每一道关卡都可能成为断点。6.4 上传文件权限不对的调整方法客户端上传完文件发现属主是ftp_vuser没问题但权限是600其他用户看不了。这通常是因为ftp客户端设置了强制权限。vsftpd端调整上传文件默认权限有两个参数local_umask022 file_open_mode0755local_umask控制新建文件的权限掩码file_open_mode控制创建文件的最高权限。组合起来通常文件默认权限 file_open_mode ~local_umask例0777 ~022 0755目录0666 ~022 0644文件如果要求所有上传文件都是644保持file_open_mode默认0666即可要求上传文件可执行再把file_open_mode调高到0777。7. 安全加固与生产环境最佳实践7.1 限制登录IP与并发数如果服务只面向内网用户直接在防火墙层面限制来源IP是最简单有效的做法firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port protocoltcp port21 accept firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port protocoltcp port40000:40010 accept firewall-cmd --reload另外在vsftpd.conf中限制并发max_clients50 max_per_ip5第一条限制整个FTP服务的并发连接数第二条限制单个IP能建立的连接数能有效防住桌面运维脚本或者异常客户端的连接轰炸。7.2 配置SELinux布尔值之前为了图省事把SELinux关了生产环境我更推荐精准开启相关布尔值既不牺牲安全又不影响功能# FTP相关布尔值 setsebool -P ftpd_full_access on setsebool -P ftpd_use_passive_mode on setsebool -P allow_ftpd_anon_write on setsebool -P allow_ftpd_full_access on如果不想开ftpd_full_access这么大的权限可以更精细地配置文件上下文semanage fcontext -a -t public_content_t /home/ftp_root(/.*)? restorecon -Rv /home/ftp_root这样FTP目录就被赋予了public_content_t类型与系统其他目录隔离开来。7.3 为FTP增加TLS/SSL加密纯FTP协议本身是明文传输用户名、密码、文件内容全部裸奔在网络上。内网环境还好说一旦跨公网传输数据被截获的风险极大。vsftpd从2.0版本开始支持TLS加密。先生成自签名证书openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/vsftpd/vsftpd.key \ -out /etc/vsftpd/vsftpd.crt然后在vsftpd.conf中追加ssl_enableYES allow_anon_sslNO force_local_data_sslYES force_local_logins_sslYES ssl_tlsv1YES ssl_sslv2NO ssl_sslv3NO rsa_cert_file/etc/vsftpd/vsftpd.crt rsa_private_key_file/etc/vsftpd/vsftpd.key重启服务后命令行ftp工具需要指定ssl验证参数ftp -ssl -p 192.168.1.108大部分图形化FTP客户端FileZilla、WinSCP会自动协商TLS连接。提示启用TLS后部分老旧的Windows资源管理器内置FTP客户端可能无法连接它不支持FTP over TLS需要改用FileZilla等第三方客户端。7.4 监控日志与异常检测日志是FTP运维最重要的排障依据。vsftpd的日志默认记录在/var/log/vsftpd.log里面记录了每次登录、每次传输的详细信息。常用日志检查命令# 查看最近登录记录 tail -f /var/log/vsftpd.log # 统计各IP登录次数 awk {print $3} /var/log/vsftpd.log | sort | uniq -c | sort -nr # 查看传输失败记录 grep -i fail\|error /var/log/vsftpd.log有条件的话把warn_enableYES加到配置中vsftpd会记录更多警告级别的信息比如密码尝试失败次数、可疑行为等。8. 经验总结与后续扩展方向8.1 我在实际部署中的几条体会第一次配好整套虚拟用户FTP系统后我用自动化脚本模拟了连续一百次的上传下载循环包括并发传输、异常中断、超大批量文件等场景。跑了三天占用内存稳定在12M左右完全没有内存泄漏和进程异常退出。这个稳定性让我对vsftpd更加信任。整个过程踩过不少坑有几条经验一直写在自己的运维笔记里修改账号数据库后必须重新生成db文件这个操作太容易被遗漏防火墙配置完成后用客户端实测一次上传和下载别只看端口有没有监听每次改完配置文件先用vsftpd -olistenNO这种调试模式跑一遍确认语法再重启正式服务生产环境的FTP目录建议挂在独立的数据盘上别和系统盘混在一起避免磁盘写满拖垮系统虚拟用户密码不要用明文保存在配置文件里如果可能接入LDAP或MySQL做统一管理8.2 后续可以继续完善的方向目前这套FTP服务已经能满足绝大多数内部文件交换需求。如果业务发展需要有几个升级路径可以走一是把虚拟用户存储从Berkeley DB换成MySQL配合一个简单的管理面板实现账号的在线管理不再需要每次手工编辑配置文件。二是增加配额管理通过脚本定时统计各用户目录的大小超出限额的发告警邮件。三是如果对传输速度要求大幅提升可以考虑在FTP前面加一层Nginx做静态文件分发或者干脆迁移到SFTP——相比FTPSFTP协议本身基于SSH加密传输安全性和兼容性都有天然优势。不过这些都是后话先把眼前的FTP服务稳定跑起来把流程走通、把权限管明白这才是最实在的第一步。
返回列表