内网服务器断外网,但又得往里传文件、跟外部系统对接数据,这种活儿我接过不止一次。离线搭建FTP服务器真正让人头疼的不是那几行配置,而是两大步:第一步,怎么把安装包弄进一个没有网的机器;第二步,装完之后连不上、列不出目录、传不了文件,全靠猜。这篇文章把我自己离线部署vsftpd的完整过程写出来,从在有网机器上准备RPM/DEB包、做本地软件源,到配置文件逐条拆解、放行防火墙、调整SELinux,再到常见坑的排查思路,一条龙讲清楚。适合在隔离网络环境下做运维和部署的同学直接照着操作。
1. 为什么离线环境也要用FTP,以及服务端怎么选
1.1 离线部署的场景比你想的更常见
很多人觉得现在的文件传输都该走HTTP接口或者SFTP,FTP已经过时了。但真到了生产环境你会发现,很多自动化设备、老平台、嵌入式终端只认FTP协议,数据交换链条上就缺这么一环。
我遇到过的典型场景是这样:一台刚上架的服务器,部署在独立网段,除了内网管理口之外没有任何外网通道。业务系统需要跟另一套老系统做文件对接,对方只支持通过FTP拉取或推送文件。这时候你手上就一台机器、一个U盘,和一纸变更申请。
还有一种更常见的情况:服务器本身能连内网,但内网和外网是物理隔离的,你没法直接在服务器上执行yum install或者apt install。这种“逻辑离线”的机器,处理思路和完全离线一模一样——必须把安装包提前准备好带进去。
在这些场景里,FTP仍然是投入产出比最高的方案,因为服务端软件体积小、配置简单、客户端兼容性极好,任何系统自带的命令行的ftp、浏览器、文件管理器都能连。先把服务搭起来解决业务问题,后续再考虑要不要换成更安全的传输方式,这是我在实际项目里的惯用策略。
1.2 服务端软件三选一:vsftpd、proftpd、pure-ftpd
开源的FTP服务端有不少,但我实际部署下来,基本就在三个里面选:
| 软件 | 特点 | 配置复杂度 | 资源占用 | 适用场景 |
|---|---|---|---|---|
| vsftpd | 名字就是Very Secure FTP Daemon,安全设计优先,各发行版仓库都自带 | 低,单配置文件 | 极低 | 绝大多数常规场景,默认首选 |
| proftpd | 配置风格类似Apache,模块化,扩展能力强 | 中高 | 中 | 需要复杂虚拟主机、多域名、细粒度指令控制的场景 |
| pure-ftpd | 轻量,自带Web管理面板配套,配置也简单 | 低 | 低 | 偏个人使用或需要简单管理界面的小团队 |
我选vsftpd的理由很直接:第一,它是很多常用发行版默认就带的服务端,离线包里最好找;第二,全部配置集中在一个文件里,出错容易定位;第三,SELinux对vsftpd有现成的布尔值开关,放行时不用瞎折腾;第四,资源占用真的低,一台低配虚拟机跑它毫无压力。后面所有实操都基于vsftpd展开,选型上如果你没有特殊需求,跟这个走就行。
2. 离线安装包怎么准备——这是最容易被卡住的一步
2.1 用包管理工具在有网机器上拉包,而不是乱找“绿色版”
离线安装的第一步,是找一台和目标任务机器同发行版、同大版本、同CPU架构的“有网机器”,用它的包管理工具把vsftpd以及所有依赖项下载下来。
RPM系的操作是这样:
# 先装 yum-utils,里面带 yumdownloader 工具 yum install -y yum-utils mkdir -p /tmp/ftp_pkgs && cd /tmp/ftp_pkgs # --resolve 会自动把依赖包一起拉下来 yumdownloader --resolve --destdir=. vsftpd这里有两个关键点。第一,--resolve是按当前这台机器的依赖关系去解析的,所以准备包的机器和目标机系统版本不能差太远,否则解析出来的依赖版本对不上,拷过去装的时候会报依赖冲突。第二,--destdir指定下载目录,把vsftpd和它的一堆依赖都放到同一个目录里,后面好一起拷走。
Debian系的操作思路一样,命令不同:
cd /tmp/ftp_pkgs # 只下载不安装 apt-get download vsftpd # 收集依赖列表 apt-rdepends vsftpd # 如果只是临时下载,也可以这样,包会缓存到 /var/cache/apt/archives apt-get install --download-only vsftpd我个人的经验是:RPM系用yumdownloader --resolve最省心,依赖一次到位;Debian系直接在有网的目标同版本机器上执行一次apt-get install --download-only,把缓存目录里的deb包全部拷走,基本就能覆盖依赖。
2.2 离线安装包到底去哪下——几个可靠渠道
在准备包的时候,很多人会习惯性去搜索引擎找下载链接,这其实是下策。离线包的正确获取渠道按优先级排是这些:
- 发行版官方软件源站点。每个发行版都有自己的软件源仓库,浏览器打开后按架构、组件、版本一层层点进目录,能找到打包好的rpm或deb文件。这种渠道的包经过官方构建和签名,路径规范,不会出现从第三方网站下载到被篡改包的风险。
- 内网已有的镜像源。不少单位内部有yum或apt镜像站,这种情况最省事,直接把
/etc/yum.repos.d里的地址指向内网源,离线机器秒变“在线”,依赖全部自动解决。 - 系统安装ISO。你的U盘里如果有同版本的系统安装镜像,挂载ISO,里面自带一个完整的软件包仓库。很多服务版ISO里能找到vsftpd包,挂载之后直接用ISO当本地源安装,这是一个经常被忽略的路子。
- 软件官方源码发布页。拿不到二进制包的时候,可以下载vsftpd源码tar包,目标机上用gcc编译安装。这个坑比较多,目标机得提前装好编译工具链和openssl开发头文件,我一般只把它作为最后手段。
2.3 包到手上之后:校验、拷贝、做本地软件源
下载下来的包不能直接用,先做完整性校验:
# 官网或源站会给每个文件发布SHA256值 sha256sum vsftpd-*.rpm校验值对得上再拷。拷贝方式上,U盘或移动硬盘最直接,如果内网里有跳板机且目标机开着sshd,用scp传也行。注意一个逻辑问题:你搭FTP之前可能没有其他文件传输通道,所以第一次进服务器的文件传输基本靠物理介质,不要慌,这是正常流程。
包拷进目标机之后,强烈建议做一步:用createrepo把目录变成本地软件源。这样之后就能用yum正常安装,依赖自动处理,比手动rpm -ivh一个个按顺序装要省心得多:
# 在目标机上的包目录里执行 cd /tmp/ftp_pkgs createrepo . # 写一个repo文件 /etc/yum.repos.d/local-ftp.repo [local-ftp] name=Local FTP Repo baseurl=file:///tmp/ftp_pkgs enabled=1 gpgcheck=0 # 然后就像在线环境一样安装 yum install -y vsftpd重要提醒:如果你用ISO做仓库,repo文件里的baseurl要写file:///mnt/cdrom,并且先把ISO挂载到/mnt/cdrom。Debian系如果没有本地源条件,就用dpkg -i安装,缺依赖再单独下载补齐,顺序通常是先装依赖再装主包。
3. 安装和核心配置——配置项看懂了再动手
3.1 安装后的初始检查和启动
包装好之后,第一件事是确认服务能不能起来、端口有没有监听:
systemctl enable --now vsftpd ss -tlnp | grep :21如果能看到*:21的LISTEN状态,说明服务已经起来了。这时候且慢高兴,默认配置下大概率只能匿名访问,离“能正常使用”还有一段距离。下一步要改配置。
3.2 vsftpd.conf核心配置项逐条拆解
vsftpd的配置集中在/etc/vsftpd/vsftpd.conf一个文件里,注释写得还算清楚,但很多人喜欢直接抄网上的配置,出了问题就抓瞎。下面这份是我在离线内网场景下常用的配置,每一条都标了为什么这么写:
# 关闭匿名访问,内部使用场景不需要匿名 anonymous_enable=NO # 允许本地用户登录 local_enable=YES # 开启写权限,包括上传、删除、重命名等写操作 write_enable=YES # 上传文件的权限掩码:022 表示文件644、目录755 local_umask=022 # 进入目录时显示目录说明文件 dirmessage_enable=YES # 开启传输日志 xferlog_enable=YES xferlog_std_format=YES # 主动模式数据连接使用20号端口 connect_from_port_20=YES # 以独立服务方式监听,而不是走xinetd listen=YES listen_ipv6=NO # 指定PAM认证方式 pam_service_name=vsftpd # 用户白名单机制,user_list 里列出的用户才允许登录 userlist_enable=YES userlist_file=/etc/vsftpd/user_list userlist_deny=NO # 所有本地用户chroot到自己的家目录 chroot_local_user=YES chroot_list_enable=YES chroot_list_file=/etc/vsftpd/chroot_list # 家目录可写时允许chroot访问(有风险,见下文说明) allow_writeable_chroot=YES # 被动模式端口段 pasv_enable=YES pasv_min_port=30000 pasv_max_port=31000逐个说几个关键点。
write_enable=YES控制的是整个FTP会话能不能执行写相关的命令。如果这个不开,哪怕目录权限给得再足,客户端上传也会收到550错误,而且提示不明确,容易误判为目录权限问题。所以只要业务需要上传,这个必须开。
chroot_local_user=YES把每个本地用户限制在自己的家目录里,用户登录后ls /看到的就是家目录,不会跑到系统其他路径瞎逛。chroot_list_enable=YES配合chroot_list_file的意思是:在chroot_list文件里列出的用户不受chroot限制。注意这个逻辑是“名单里的人不限制”,和直觉相反,容易搞混。
allow_writeable_chroot=YES这条要单独说。vsftpd出于安全考虑,默认如果用户的chroot目录(也就是家目录)对用户可写,它会拒绝在这个chroot内提供写服务,日志里报500 OOPS: refusing to run with writable root inside chroot()。很多人遇到这个错误就加allow_writeable_chroot=YES,加完确实不报错了,但代价是取消了vsftpd对这个风险点的保护。我自己的做法是:尽量把用户家目录的属主改成root、权限改成755,然后在里面建一个可写的upload子目录,既满足上传需求,又不用打开这个开关。
被动模式那三个参数值得单独拎出来讲。
3.3 主动模式与被动模式——为什么连不上多数是这里
FTP和普通HTTP不一样,它有两个连接:21端口是控制连接,数据连接是另外建立的。主动模式(PORT)下,服务器主动去连客户端的20端口;被动模式(PASV)下,服务器开一个高位端口等着客户端来连。
用生活化的比喻:主动模式像你给商家留了个座机号,商家主动打电话给你报商品清单;被动模式像商家给你报了一串分机号,你自己拨过去听清单。现在绝大多数客户端环境都在NAT后面,客户端根本没有公网端口能让服务器主动来连,所以被动模式是绝对主流。
配置里的pasv_min_port=30000和pasv_max_port=31000就是给被动模式准备的端口段。端口段的选择标准是:太小并发连接会冲突,太大防火墙策略难写。1000个端口对一个小团队或十来台对接设备完全够用。如果服务器本身也在NAT后面,还要加一行pasv_address=服务器对外的地址,否则客户端拿到的是内网地址,连不过来。这一条我踩过不止一次坑,后面排查部分会详细说。
4. 用户与权限——按最小权限原则开账号
4.1 本地账号方案:每个业务一个nologin用户
离线内网场景,我一般不给FTP开匿名访问,而是按业务开系统账号,每个人一个专属目录:
# 创建FTP专用用户,shell设为nologin,禁止登录系统 useradd -d /data/ftp/ftpuser1 -s /sbin/nologin ftpuser1 # 家目录由root持有,普通用户只读 mkdir -p /data/ftp/ftpuser1 chown root:root /data/ftp/ftpuser1 chmod 755 /data/ftp/ftpuser1 # 上传目录给用户自己,可写 mkdir -p /data/ftp/ftpuser1/upload chown ftpuser1:ftpuser1 /data/ftp/ftpuser1/upload chmod 755 /data/ftp/ftpuser1/upload # 设置密码 passwd ftpuser1/sbin/nologin这个设置很重要:FTP只需要这个账号的文件读写能力,不需要它登录系统,把shell设成nologin就从源头上封死了SSH登录的可能。另一个容易忽略的点是,/sbin/nologin必须写进/etc/shells,否则PAM认证会拒绝这个用户,登录时报530。多数发行版默认已经有,没有的话手动加一行。
家目录属主设为root、755,再单独建一个upload子目录给用户写,这套组合拳既满足了上传需求,又避免了chroot内可写目录的安全告警,还顺手把“用户只能看到自己想让他看到的东西”这件事做了。
4.2 虚拟用户方案:不建系统账号,用数据库认证
如果你不想为每个FTP用户都创建一个系统账号,vsftpd还支持虚拟用户方案。虚拟用户不是真实系统用户,它统一映射到一个系统账号上,但每个虚拟用户可以单独指定目录、权限、设置。我维护起来反而更清爽,因为系统账号不会越积越多。
操作分四步。
第一步,建一个映射用的系统账号:
useradd -d /data/ftpv -s /sbin/nologin ftpvuser第二步,写虚拟用户账号密码文件,格式是奇数行用户名、偶数行密码:
cat > /etc/vsftpd/virtual_users <<'EOF' user1 passwd1 user2 passwd2 EOF第三步,把文本文件转成hash数据库:
db_load -T -t hash -f /etc/vsftpd/virtual_users /etc/vsftpd/virtual_users.db第四步,改PAM认证配置,/etc/pam.d/vsftpd改成:
auth required pam_userdb.so db=/etc/vsftpd/virtual_users account required pam_userdb.so db=/etc/vsftpd/virtual_users然后在vsftpd.conf里加上:
guest_enable=YES guest_username=ftpvuser virtual_use_local_privs=YES这样虚拟用户登录后就会以ftpvuser的身份干活。每个虚拟用户还能单独定义家目录,在/etc/vsftpd/user_conf/目录下建一个和用户名同名的文件,内容写local_root=/data/ftp/user1,这样账号和目录的对应关系就完全由你控制了。
4.3 上传权限与umask的细节
上传文件的权限是由local_umask决定的。022是常见选择,上传的文件是644、目录是755,其他人可以读但不能写。如果目录里有敏感数据,不想让其他同组用户看到,把local_umask改成027,文件就变成640。这个值直接影响了多方对接时别人能不能读到你的转运文件,建议在项目开始时就跟业务方确认好。
还有个容易忽略的点:write_enable=YES是全局的,你没办法在配置里单独给某个目录开写权限。目录能不能写,完全靠文件系统权限控制。所以权限设计的核心思路是:全局开关开到位,细粒度控制交给目录属主和权限位。
5. 防火墙、SELinux与开机自启——装完不代表能用
5.1 防火墙放行:除了21,别忘了被动端口段
很多人在本机测试FTP一切正常,换台机器一连就超时,问题基本出在防火墙。如果用的是firewalld:
# 放行21端口的标准FTP服务 firewall-cmd --permanent --add-service=ftp # 放行被动模式端口段 firewall-cmd --permanent --add-port=30000-31000/tcp # 重载生效 firewall-cmd --reload如果用的是iptables,就加两条规则:
iptables -A INPUT -p tcp --dport 21 -j ACCEPT iptables -A INPUT -p tcp --dport 30000:31000 -j ACCEPT被动模式端口段是最容易被漏掉的部分。很多运维开了21就以为够了,结果客户端能登录但一列目录就卡住,因为数据连接建立不了。另外要提醒的是:如果服务器前面还有硬件防火墙或云安全组,记得把端口段一起加进去,别只在服务器本机放行。
5.2 SELinux不要一把梭关掉,开对应布尔值就行
SELinux是离线内网机器上另一个“装完连不上”的大户。现象往往是:服务起来了、防火墙也放行了,但客户端登录时报500 OOPS: cannot change directory:/home/xxx,或者连接后行为异常。
很多人的第一反应是setenforce 0直接关掉,我强烈不建议这么干。内网环境同样需要纵深防御,SELinux作为一种强制访问控制机制,对上线的服务器是有实际价值的。正确的做法是先看审计日志:
grep vsftpd /var/log/audit/audit.log | grep denied确认是SELinux拦截之后,有针对地放行:
# 允许FTP完全访问(包括读写家目录之外的文件) setsebool -P ftpd_full_access on # 如果只是需要匿名写或特定目录,用更细的布尔值 setsebool -P ftpd_anon_write on # 给FTP专用目录打标签 semanage fcontext -a -t public_content_t "/data/ftp(/.*)?" restorecon -R -v /data/ftpsemanage命令如果不存在,先装policycoreutils-python-utils这个包。用-P参数是为了让布尔值持久化,重启不丢失。
5.3 systemd服务管理的基本操作
vsftpd在现代发行版里以独立守护进程方式运行,服务管理就三句话:
systemctl enable vsftpd # 开机自启 systemctl restart vsftpd # 改完配置重启生效 systemctl status vsftpd # 看运行状态排查问题时,日志是第一个要去看的地方:
journalctl -u vsftpd -n 50 tail -f /var/log/xferlog # 传输日志 tail -f /var/log/vsftpd.log # 调试日志(需要开启)vsftpd.conf里如果没开log_ftp_protocol=YES,vsftpd.log是空的,需要排问题时临时把它打开,能记录完整的FTP命令交互过程,定位问题非常有效。排完再关掉,避免日志膨胀。
6. 常见问题与排查实录——照着这张表能省两小时
6.1 排查速查表
以下是我在多次离线部署中遇到的典型问题和解决方案,整理成表,建议收藏:
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 连接直接被拒 | 服务没起、防火墙拦截 | 先`ss -tlnp |
| 登录后列目录卡死 | 被动模式端口未放行、NAT环境缺pasv_address | 放行30000-31000端口段;服务端在NAT后配置pasv_address |
| 530 Login incorrect | 用户不存在、密码错、user_list白名单限制、shell不在/etc/shells | 逐项排查,重点确认shell和pam认证方式 |
| 500 OOPS: cannot change directory | SELinux拦截、目录不存在或属主不对 | 看audit.log确认;restorecon目录标签;检查目录权限 |
| 500 OOPS: writable root inside chroot | 家目录对用户可写 | 家目录属主改root、755,或确认风险后加allow_writeable_chroot=YES |
| 553 Could not create file | 目录无写权限、磁盘满 | 检查upload目录属主和权限;df -h看磁盘 |
| 传输速度很慢 | 客户端被动模式连到服务器I/O瓶颈 | 看xferlog判断瓶颈;检查服务端网卡和磁盘负载 |
6.2 实录一:被动模式端口段没放行
有一次部署完成后,本地用命令行ftp连上去,登录成功,输入ls就卡住,直到超时。当时服务端防火墙、SELinux都检查过,看似没问题,最后发现是前置防火墙要单独放行被动端口段,服务器本机放行还不够。
排查思路是:先在服务器本机用ftp客户端连一次,如果本机正常、跨机异常,基本可以判定问题出在网络路径上的某个过滤设备。再在服务器上临时开log_ftp_protocol=YES,会看到客户端发来PASV命令,服务器回了227 Entering Passive Mode (10,0,0,1,117,45),这个地址和端口就是客户端要去连的,如果连不通,问题就出在地址或端口被拦。
这里还有个细节:227里的四个数是被动模式IP地址,后两个数是被动端口。117*256+45等于30000左右的端口,正好落在我们配置的端口段内。如果发现返回的IP是内网地址而客户端在别的网段,就要配置pasv_address指向服务端的对外地址。
6.3 实录二:SELinux拦截导致的目录切换失败
另一个项目里,用户登录正常,但访问家目录时报500 OOPS: cannot change directory:/data/ftp/ftpuser1。第一反应是目录权限问题,查了权限完全正常,直到看了audit.log才发现是SELinux在拦截。
解决办法就是上一节说的:先semanage fcontext给FTP目录打上public_content_t标签,再restorecon生效。如果你不想记这些命令,setsebool -P ftpd_full_access on也能解决问题,但属于“一把梭”,把SELinux对FTP的所有限制全放开了。我个人的习惯是能精细到目录标签就尽量精细,毕竟多一层控制多一层安全。
6.4 实录三:530 Login incorrect的隐藏原因
还有一次用户报登录总是530,密码确认没错。查了一圈发现,问题出在用户的shell被改成了nologin之后,PAM模块检查/etc/shells,发现里面没有/sbin/nologin,直接拒绝认证。往/etc/shells里加上这一行就解决了。
所以排查530的顺序应该是:用户是否存在、shell是否在/etc/shells里、密码是否正确、user_list是否限制、PAM配置是否被改过。按这个顺序走一遍,基本能解决九成登录问题。
7. 上线前的安全加固和运维建议
7.1 FTP是明文协议,至少要给它加一层TLS
FTP的账号密码和数据传输都是明文,在同网段里用抓包工具就能直接看到密码。内网不代表绝对安全,这一点必须说明白。
如果你的对接方客户端支持FTPS,建议开启TLS。vsftpd开启TLS的配置比较简单:
ssl_enable=YES allow_anon_ssl=NO force_local_data_ssl=YES force_local_logins_ssl=YES rsa_cert_file=/etc/ssl/certs/vsftpd.pem rsa_private_key_file=/etc/ssl/private/vsftpd.key证书用openssl自签一个就够:
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/ssl/private/vsftpd.key \ -out /etc/ssl/certs/vsftpd.pem自签证书客户端首次连接会提示证书不受信任,正常现象,内部使用可以接受。另外要提醒的是,开启TLS后,老的客户端如果只支持明文FTP就连接不上了,切TLS前要确认对接方客户端的兼容性。
7.2 日志、限速和防爆破
上线前至少把三件事做了:一是确认传输日志打开,/etc/vsftpd/vsftpd.conf里有xferlog_enable=YES,这样每次传输都有记录,出问题能追溯到人;二是本地账号按业务最小化分配,能用虚拟用户管理的就不要开系统账号;三是如果服务暴露在比较恶劣的网络环境里,配合fail2ban类工具监控登录失败,连续失败自动封禁来源IP。
常见的vsftpd + fail2ban配置不难,关键是找到日志路径,让它监控vsftpd的登录失败记录。这块我建议不要跳过,内网扫描和爆破脚本其实也很常见,遇到过几次之后你就知道这步不能省。
7.3 把这套流程固化下来
离线部署FTP这件事,做一次可能觉得繁琐,但你把它拆解成固定流程之后,复用成本很低。我现在每个项目都按统一清单走:有网机器上同版本拉包、校验、createrepo、本地源安装、防火墙放行21和被动端口段、SELinux布尔值或目录标签、本机先连一次、再用对端验证一遍传输。走完这套流程,半小时内肯定收工,而且基本不会翻车。
我个人在实际操作中的体会是,离线环境部署最忌讳“装完就跑”。服务能用只是及格线,配置项背后的安全含义、日志怎么看、出问题从哪查起,这些才是真正决定你后续运维省不省心的关键。照这篇文章的思路搭一次,后续再遇到类似需求,你会感谢当初把这些坑都踩过一遍的自己。