前两天有同事找我帮忙说他们团队经常要在几台设备之间交换固件包和配置文件每次靠U盘来回拷实在太原始了。正好单位新上线了一台麒麟Server 11让我想办法开一个内网文件交换通道。需求本身不复杂不需要账号体系访问的人越少门槛越好能上传能下载就行。我第一反应是做Samba但仔细一想在这个场景下匿名FTP反而更轻、更稳、更好管控。于是就有了这篇《麒麟server11部署vsftpd_3.0.5匿名模式》的实操记录。整件事情前后花了我两天时间其中一大半耗在权限和防火墙排错上我把完整过程和踩坑点都整理出来给需要在麒麟、openEuler这类系统上搭匿名FTP的运维同学做个参考。1. 为什么最后选了匿名FTP需求背景和方案取舍1.1 需求场景给设备和同事一个无门槛的“公共文件口”先说一下实际业务背景。这台麒麟Server 11要承担的任务是接收设备上传的配置文件、日志包同时允许其他同事下载公共的固件版本和工具包。使用方很杂有Linux终端、Windows电脑还有几台不带图形界面的嵌入式设备。这种情况下如果走Samba需要维护smbpasswd账号如果走SFTP匿名用户根本没法登录如果走HTTP上传要么上Web应用要么写额外的CGI脚本维护成本一下就上去了。匿名FTP在这类场景里的优势很明显客户端不需要装任何软件Windows资源管理器地址栏输入ftp://就能打开命令行一个curl就能拉取嵌入式设备更是天然支持FTP协议。vsftpd跑起来占用的内存不到1MB配置写明白之后可以稳定跑几个月不用管。缺点大家都清楚——匿名不设防但这可以通过网络层面的隔离和服务本身的参数控制补上后面我会专门讲。1.2 vsftpd 3.0.5版本选型的三个理由vsftpd目前最新的3.x稳定版就是3.0.5。选它而不是直接用2.x时代的旧包主要原因有三个。第一兼容性和安全性更好。vsftpd 3.0.5修复了之前版本里若干和路径处理、TLS会话相关的问题对于要长期运行在公网边界内网环境里的服务少一个隐患是一份心安。第二麒麟Server 11的仓库里如果带了vsftpd大概率也是3.x系列而3.x的配置语法和2.x基本一致迁过来没有额外学习成本。第三源码包很小编译安装不挑架构——麒麟的服务器有x86的也有ARM的我在编译前特意确认过同一套源码两个架构都能过不需要改任何地方。1.3 匿名模式的边界哪些能放哪些坚决不放匿名模式最重要的不是“怎么开”而是“开多大”。我在规划的时候给自己列了几条红线服务只监听业务内网网卡不绑0.0.0.0避免任何来自公网或管理网段的访问。上传目录和只读目录物理分离上传区只允许匿名用户写不允许匿名用户删除和重命名别人的文件。匿名用户被chroot在自己的根目录里看不到系统其他路径。开启完整日志记录谁上传了、下了什么、什么时候做的全部留痕。这几条红线后面都落地到了vsftpd.conf里。先记住这个原则匿名FTP不是“随便开个FTP”而是“在一个明确的权限边界里给外部用户一个可控的文件通道”。2. 麒麟Server 11环境摸底与vsftpd_3.0.5双安装路线2.1 先确认系统版本和仓库里的vsftpd版本拿到服务器第一件事确认系统版本cat /etc/kylin-release麒麟Server 11基于openEuler 22.03这个技术底座包管理用的是dnf/yum内核5.10。这类系统往下兼容RHEL系的目录习惯所以/etc/vsftpd/vsftpd.conf、/etc/pam.d/vsftpd这些路径都是约定的。接下来看软件仓库里有没有现成的vsftpddnf list vsftpd仓库里有的话直接装就行。不过我在这台机器上查到的版本是3.0.4和标题要求的3.0.5差了一个小版本。如果你的环境对版本号有硬性要求或者需要用到3.0.5里修复的某些问题那就走源码编译路线。两条路我都实测过下面分别说明。2.2 路线Adnf直接装仓库里的vsftpd如果仓库版本能满足需求这是最省事的dnf install -y vsftpd systemctl enable --now vsftpdRPM包装完自带/usr/lib/systemd/system/vsftpd.servicePAM配置文件/etc/pam.d/vsftpd也一并生成好了ftp用户也会被创建。你在这一步只需要做两件事改配置、开防火墙。对大多数只需要匿名下载的场景这条路足够。但如果你必须装3.0.5或者想完全掌控二进制和配置文件的部署位置走源码编译更合适。2.3 路线B源码编译安装3.0.5vsftpd的源码可以说是我见过最朴素的服务器软件源码——没有autoconf那一大套下载解压后直接make就能编出二进制。具体步骤# 安装编译依赖 dnf install -y gcc make pam-devel # 下载3.0.5源码包 wget https://security.appspot.com/downloads/vsftpd-3.0.5.tar.gz tar xf vsftpd-3.0.5.tar.gz cd vsftpd-3.0.5 # 直接编译vsftpd没有configure脚本 make -j4编译完成后把二进制复制到/usr/sbin/下同时建议创建好配置目录cp vsftpd /usr/sbin/vsftpd mkdir -p /etc/vsftpd这里有个地方要提醒源码包里的builddefs.h默认定义了一些编译选项最典型的是PAM支持。如果你的系统里连pam-devel都没装编译会报缺少security/pam_appl.h这类头文件。另外如果你想用FTPSFTP over TLS需要在builddefs.h里打开VSF_BUILD_SSL并安装openssl-devel。纯匿名明文FTP场景下不开SSL也问题不大。2.4 用systemd把源码版管起来源码编译的vsftpd不会自动生成systemd service需要手动写一个单元文件。我在/usr/lib/systemd/system/vsftpd.service里写了如下内容[Unit] DescriptionVsftpd ftp daemon Afternetwork.target [Service] Typeforking ExecStart/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf ExecReload/bin/kill -HUP $MAINPID Restarton-failure [Install] WantedBymulti-user.target然后systemctl daemon-reload systemctl enable --now vsftpd为什么启动命令里要显式带配置文件路径因为源码版默认找的是/etc/vsftpd.conf而RHEL系约定是/etc/vsftpd/vsftpd.conf。显式指定路径可以避免“明明装了却加载了默认空配置”这种诡异问题。另外源码版不会自动创建ftp用户。如果系统里没有需要自己补# 先建目录再建用户避免用户home目录缺失 mkdir -p /data/ftp useradd -r -d /data/ftp -s /sbin/nologin ftp匿名用户在vsftpd里最终被映射到一个系统账号上默认就叫ftp。这个账号的home目录会作为匿名用户的起始目录后续配置会用到。3. 匿名模式配置实录从最小参数集到上传目录规划3.1 vsftpd.conf里绕不开的匿名相关参数vsftpd的配置参数看起来多但匿名模式真正核心的其实就十几个。我最终落地的/etc/vsftpd/vsftpd.conf是这样的# 匿名与本地用户开关 anonymous_enableYES local_enableNO write_enableYES # 匿名用户权限细分 anon_upload_enableYES anon_mkdir_write_enableYES anon_other_write_enableNO anon_root/data/ftp anon_umask022 # chroot与安全 chroot_local_userYES allow_writeable_chrootNO nopriv_userftp secure_chroot_dir/usr/share/empty # 监听与端口 listenYES listen_ipv6NO listen_address192.168.10.20 connect_from_port_20YES pasv_enableYES pasv_min_port30000 pasv_max_port30010 pasv_address192.168.10.20 # 日志 dirmessage_enableYES xferlog_enableYES xferlog_file/var/log/xferlog log_ftp_protocolYES vsftpd_log_file/var/log/vsftpd.log # PAM pam_service_namevsftpd几个关键参数说下我的理解local_enableNO很重要。匿名模式如果忘记关这个开关系统上所有本地账号都能登录FTP那就不是“匿名共享”而是“账号泄露”了。除非你明确还要给本地用户开FTP否则这个参数必须设为NO。anon_other_write_enableNO控制的是删除、重命名这类操作权限。匿名场景下我强烈建议关掉否则任何人连接上来都能把别人传的文件删了。chroot_local_userYES让匿名用户被限制在anon_root目录里。配合allow_writeable_chrootNO如果/data/ftp本身可写vsftpd会直接拒绝启动——这是故意的防止chroot根目录可写导致的安全漏洞。正确的做法是让根目录只读单独划一个可写子目录给上传。3.2 目录和属主权限先想清楚再动手我规划的目录结构是这样的/data/ftp/ ├── pub/ # 公共只读区所有人可下载 │ └── upload/ # 上传区只有匿名用户可写创建命令mkdir -p /data/ftp/pub/upload chown root:root /data/ftp/pub chmod 755 /data/ftp/pub chown ftp:ftp /data/ftp/pub/upload chmod 755 /data/ftp/pub/upload这里有两层考虑。第一/data/ftp和/data/ftp/pub都不要给匿名用户写权限因为它们是chroot的根目录和只读根。一旦可写vsftpd会报refusing to run with writable root inside chroot()直接拒服后面第5章会细说。第二/data/ftp/pub/upload必须让ftp用户可写匿名用户上传数据时进程是以ftp身份在操作系统里落地的目录的属主和权限自然要对应上。chmod 755而不是777也有讲究。匿名FTP目录没必要用777755足够让匿名用户进入和读取同时对上传区而言属主是ftp就能写无需开放给其他系统账号。3.3 定制被动模式端口避免客户端连上后卡住配置里专门划了pasv_min_port到pasv_max_port这一段原因是FTP协议有两个工作模式。主动模式下客户端连服务器的21端口然后服务器主动往客户端的高位端口发起数据连接被动模式下客户端先发PASV命令服务器告诉客户端“你来连我这个数据端口”然后客户端主动连接。现在的办公网络和设备终端大多在NAT后面主动模式经常因为防火墙拦连接而失败所以默认都走被动模式。为了让被动模式稳必须做三件事固定一个端口段比如30000-30010方便防火墙放行。pasv_address设置成客户端实际能访问到的服务器IP。多网卡机器尤其要配这个参数不然后的响应包IP可能是错的。端口段别开太大10个端口足够几十个并发上传下载使用了。4. 防火墙与SELinux服务明明起来了外面却连不上的两座大山4.1 firewalld放行21和被动端口麒麟Server 11默认防火墙是firewalld。很多人只放21端口就以为完事了结果客户端连接后一列目录就卡死——因为被动模式的数据端口没放行。正确做法firewall-cmd --permanent --add-port21/tcp firewall-cmd --permanent --add-port30000-30010/tcp firewall-cmd --reload查看放行情况firewall-cmd --list-all确认21/tcp和30000-30010/tcp都出现在ports列表里。这里直接写端口比--add-serviceftp更直观因为ftp服务定义默认只覆盖21端口被动端口段还是得单独加。4.2 SELinux需要设置的布尔值和目录上下文麒麟Server 11默认开了SELinux且状态是Enforcing。很多FTP“服务起不来、文件传不进去”的问题最后都指向SELinux。我这边需要让匿名用户写上传目录于是设置对应的布尔值和目录上下文setsebool -P ftpd_anon_write 1 semanage fcontext -a -t public_content_t /data/ftp/pub(/.*)? semanage fcontext -a -t public_content_rw_t /data/ftp/pub/upload(/.*)? restorecon -Rv /data/ftp这两条布尔值/文件上下文配合的意思是public_content_t允许FTP进程读取public_content_rw_t在ftpd_anon_write开启后允许匿名用户写。如果不做这一步即使目录权限是777SELinux也会拦下写入动作日志里报Permission Denied非常隐蔽。如果不想细粒度管理也可以直接setsebool -P ftpd_full_access 1一把梭。我在生产环境不推荐毕竟SELinux本身就是一道安全防线直接全放开等于自废武功。但如果你只是想快速验证功能开一下也无妨。4.3 多网卡环境下的IP回包粘连问题这台服务器正好是双网卡一张管理网卡连办公网段一张业务网卡连设备网段。FTP服务我打算只对设备网段开放所以配置文件里写了listen_address192.168.10.20。这种场景最容易出现的问题是客户端连的是业务网卡IP服务器在被动模式回复PASV时却可能把另一张网卡的IP告诉客户端。客户端尝试连接那个“看起来通但实际上不通”的地址表现就是能登录、列目录特别慢或者直接卡死。解决办法就是上面提到的pasv_address配置项。它指定服务器对外通告的数据连接IP和客户端访问IP保持一致即可。单网卡环境可以忽略多网卡务必设置。5. 踩坑全记录匿名上传失败的完整排查链路这章是整篇文章最有价值的部分。我在部署过程中把匿名FTP最容易踩的四个坑全部踩了一遍每个都给出当时的排查链路和最终修复方案。5.1 最熟悉的陌生人500 OOPS refusing to run with writable root第一次配好配置文件启动服务时日志里报错500 OOPS: vsftpd: refusing to run with writable root inside chroot()字面意思是vsftpd拒绝在chroot根目录可写的情况下运行。原因非常典型——我把anon_root直接指向了/data/ftp然后又让匿名用户对/data/ftp有写权限。vsftpd的安全策略认为如果chroot根目录可写攻击者一旦突破FTP权限就能在根目录里创建文件这和chroot的本意冲突。排查链路就是按这个顺序走的先看启动日志确认是哪一行配置造成的再看anon_root的目录权限最后调整目录结构。修复方式有两个正常用第一个把可写目录单独放子目录chroot根目录保持只读。在配置里加allow_writeable_chrootYES强行绕过这个检查。我在测试机上试过第二个方案确实能启动但明显违背安全原则生产环境不建议用。最终我采用方案一也就是第3章里的目录结构。5.2 目录权限全对SELinux也没拦上传还是Permission Denied权限问题的第二个隐蔽点来自SELinux的文件上下文。我当时第一次建完目录ls -ld看权限全是755属主也没问题但匿名用户上传文件时一直报Permission Denied。排查步骤# 检查SELinux状态 getenforce # 检查目录的SELinux上下文 ls -Zd /data/ftp/pub/upload # 查最近被SELinux拒绝的审计日志 ausearch -m avc -ts recent结果ausearch里明确显示ftpd_t访问default_t目录被denied。原因是我在/data/ftp下新建的目录没有匹配FTP服务需要的public_content_t或public_content_rw_t标签。补上semanage fcontext和restorecon之后就正常了。这里给新手一个提示看到Permission Denied先别急着chmod 777应该按“普通权限→SELinux布尔值→文件上下文→防火墙”的顺序排查。chmod 777往往解决不了SELinux的拦截还会把系统权限搞乱。5.3 530 Login Incorrect问题出在PAM文件上另一个让我印象深刻的坑是匿名用户登录时报530 Login Incorrect。匿名模式理论上不需要密码为什么会被拒绝排查下来问题出在PAM。源码编译的vsftpd并不会自动生成/etc/pam.d/vsftpd文件而配置里写的是pam_service_namevsftpd。vsftpd启动后尝试加载这个PAM服务文件发现不存在认证直接失败。RPM包安装时这个文件是自带的源码版就需要自己创建。修复方式手动创建/etc/pam.d/vsftpd内容参考RHEL系标准写法cat /etc/pam.d/vsftpd EOF auth required pam_listfile.so itemuser sensedeny file/etc/vsftpd/ftpusers onerrsucceed auth required pam_unix.so account required pam_unix.so EOF同时创建/etc/vsftpd/ftpusers把不希望登录FTP的系统账号写进去cp /etc/passwd /tmp/passwd.bak awk -F: ($3 1000){print $1} /etc/passwd /etc/vsftpd/ftpusers重启服务后再测试匿名登录恢复正常。这个坑提醒我源码编译版本的“默认没有PAM配置”是必须手工补齐的一步不能等出了问题再想。5.4 下载正常但上传卡死回到防火墙被动端口最后一个坑是下载没问题上传文件时进度条走一点就卡住最后超时。第一反应以为是上传目录或者SELinux结果本机测试一切正常。排查链路# 本机验证用curl上传测试文件 curl -T /tmp/test.txt ftp://192.168.10.20/pub/upload/ # 如果本机正常说明服务端权限和SELinux都没问题 # 需要检查防火墙状态 firewall-cmd --list-all查完之后发现我firewalld只放行了21/tcp被动端口段30000-30010根本没有加进去。客户端发起PASV请求服务器返回数据端口客户端去连那个端口却被防火墙丢弃。控制连接没断数据连接建立不起来就表现为“上传卡死、最终超时”。补上端口放行后立即恢复。此后我在所有FTP部署里都养成了一个习惯被动端口段必须和21端口一起放行否则后面调试的时间成本远大于这两条命令。6. 验证与日常观测怎么确认这套服务真的能干活6.1 本机curl和lftp双通道验证全部配置完成后我用两种方式做最终验证。第一种curl查看目录curl ftp://192.168.10.20/pub/输出里能看到drwxr-xr-x的目录信息和文件名说明匿名读取正常。第二种lftp完整验证上传下载lftp 192.168.10.20 ls put /etc/hosts get hosts byeput能执行、get能成功说明匿名上传、下载都通。lftp还有一个好处是可以打开调试模式debug 3能看到FTP命令和响应码排查时很有用。6.2 Windows资源管理器里的主动/被动模式之争内网用户大多用Windows资源管理器直接访问。地址栏输入ftp://192.168.10.20/pub资源管理器默认会优先用被动模式所以只要防火墙放行了被动端口体验基本顺畅。但如果用户改用命令行的ftp.exe它默认走主动模式可能需要在ftp交互界面里敲passive切换。这个区别值得提前告诉使用方免得用户来问“为什么我命令行连不上”。6.3 用日志做日常健康检查日志是最好的监控手段。我配置里开了两个日志/var/log/vsftpd.log记录完整FTP会话/var/log/xferlog记录传输明细。日常检查的关键点# 实时观察连接和报错 tail -f /var/log/vsftpd.log # 查看最近传输记录 tail -100 /var/log/xferlog在vsftpd.log里看到OK、226 Transfer complete这类响应码说明服务正常看到530、550、500 OOPS这些关键字就要对照第5章的排查链路逐一检查。我一般每周看一次xferlog的传输量顺便确认有没有异常IP在反复访问或上传异常文件。最后说一点个人体会。匿名FTP这个方案在很多人眼里可能有点“老派”但在内网设备文件交换、公共固件分发这种具体场景里它确实是投入产出比最高的方案。麒麟Server 11上跑vsftpd 3.0.5我这边从源码编译到配置落地再到跨网段稳定运行整个过程的经验就是先画清楚权限边界再动手写配置出了问题按“目录权限→SELinux→防火墙→PAM”的顺序排查不要瞎猜。另外再多说一句我后来在另一台ARM架构的麒麟机器上用同样的配置重新部署了一遍两小时轻松搞定这套方案的可移植性也经住了验证。如果你们那里刚好也有类似的匿名文件共享需求照着上面的配置走一遍基本能把坑都避开。