我们工作中经常要在Ubuntu上搞定机器与机器、机器与人的文件传递。以前我习惯用FTP后来吃过几次被动模式连不上的亏转过SCP又发现它每次传目录都得吃药一样重复敲参数。绕了一圈之后我稳定用SFTP作为主力方案在Ubuntu系统里传文件它既不额外开端口也不用单独起一套服务只要SSH是活的文件传输通道就一起带上了。这篇就专门把SFTP在Ubuntu下的使用方式、配置细节和我踩过的坑整理出来。先说清楚适合谁看刚接触Linux、想把文件在本地和服务器之间搬来搬去的同学能在半小时内搭出一套能用的传输通道已经会基本操作但想规范权限、做自动化脚本的运维朋友也能从后半段的配置和排查记录里找到一些参考。1. 选型之前的几个判断为什么SFTP值得当主力1.1 SFTP不是FTP加个S别再被名字误导很多人第一次看到SFTP都以为它就是把FTP协议外面包了一层TLS/SSL跟HTTPS和HTTP的关系一样。这个理解是错的。SFTP全名是SSH File Transfer Protocol注意关键词是SSH。它跟传统FTP是两个完全不同的协议只是名字撞车了。传统FTP走21号端口传指令然后用20号端口或者随机高端口传数据。这就带来一个很现实的问题只要中间有防火墙FTP的主动/被动模式就很容易翻车要么数据端口被挡要么客户端模式切错了传不动。另外FTP的账号密码默认是明文在网络里跑抓包工具一开账号密码直接裸奔。我在内网见过不止一次因为FTP明文密码被嗅探导致服务器被登录的情况。SFTP完全不同。它建立在SSH连接之上默认走22端口所有指令、认证、数据流量都在同一条加密通道里传输。不需要额外开数据端口不存在主动被动模式的说法防火墙策略只需要放行SSH这一个端口即可。更关键的是它能复用系统已有的SSH用户体系不需要像FTP那样单独管理一套虚拟账号。1.2 和SCP、rsync放在一起比差异比想象中更大经常被人拿来对比的概念还有SCP和rsync我干脆把它们放一张表里看方便取舍。对比项SFTPSCPrsyncSSH模式基于SSH是是可通过SSH隧道交互式目录浏览支持不支持不支持断点续传新版支持reget/reput不支持支持目录递归传输支持支持支持且增量文件列表操作删、改名、建目录支持不支持有限支持自动化脚本友好度较高批处理方便简单直接增量同步更高效最典型场景交互式管理远端文件快速拷单个文件目录级增量同步SCP胜在简单一条命令传完就走适合脚本里快速搬运单个文件。但它不能列出远端目录、不能执行远端删除改名而且传输中断之后只能重新来。rsync强在大目录增量同步但日常连上去看看服务器上有哪些文件、挨个目录拉下来这种交互操作rsync完全做不了。SFTP把这些都揉在了一起既能像FTP客户端那样交互浏览又能写脚本批处理还不需要额外交付。1.3 我实际用它解决的几类问题SFTP最常见的落地场景有这么几种我自己的使用经历基本都覆盖了服务器之间传部署包不需要在目标机上额外安装FTP服务端帮同事把公网服务器上的日志拉回本地分析只需要给一把只读账号不用开放整个服务器SSH权限在脚本里定时从远端目录下载报表文件用批处理参数执行sftp命令配合cron跑得挺稳需要临时给外部人员一个目录让他们上传文件用chroot把对方限制在一个固定目录里安全边界可控。这些场景里SFTP都能用一套SSH体系解决省心之处在于不用记住多套端口、多套协议规则。内网越是复杂、防火墙越严格这种只开一个端口的方案就越有优势。2. 环境准备与服务端配置先把传输通道搭起来2.1 确认SSH服务状态SFTP是SSH服务的一个子系统所以前置条件只有一个目标Ubuntu机器上运行着sshd服务。先检查系统里有没有这个服务在跑systemctl status ssh看到 active (running) 就直接跳到下一步。如果提示找不到服务就是没装OpenSSH Server安装很轻量sudo apt update sudo apt install openssh-server装完顺手设成开机自启sudo systemctl enable --now ssh2.2 确认sftp子系统已启用SSH服务默认自带SFTP子系统配置位于 /etc/ssh/sshd_config 中Subsystem sftp internal-sftp这一行默认是存在的。它表示SSH服务自带的内部SFTP实现不依赖外部的sftp-server二进制文件。我建议保留 internal-sftp 而不是外部的 /usr/lib/openssh/sftp-server前者对chroot的支持更自然后面配置权限限制时省事。改完任何sshd_config配置之后都要重载服务sudo systemctl reload ssh这里注意是reload而不是restart。reload只是让sshd重新读配置不会断掉当前已经建立的SSH会话restart会断开全部连接如果你正在通过SSH去远程改配置restart有可能让自己掉线。2.3 最小可用配置建议需要连SFTP的账号可以是普通系统用户也可以是已有的root用户。但我不建议直接拿root账号跑日常文件传输权限太大误操作风险高。建议的做法是新建一个专用账号sudo useradd -m -s /usr/sbin/nologin filetransfer sudo passwd filetransfer把shell设成 /usr/sbin/nologin 是让这个账号无法登录SSH执行命令只能用来传文件。配合SFTP的ChrootDirectory设置等于把对方锁在一个目录里只能在这块隔离区里活动。具体配置在第4节展开。权限设计上记住一个核心原则传输文件的目录属主最好归实际使用人不要直接chmod 777。我看到很多教程让人把目录改成777解决写入问题这是典型的饮鸩止渴。正确做法是让目录属主和用户匹配必要时在组权限上放开写权限就够用了。3. 客户端实操命令行、脚本和图形化工具3.1 交互模式的核心命令命令行sftp是Ubuntu自带的客户端最朴素也最可靠。基本用法sftp usernameserver_ip如果SSH不是默认22端口用-P指定sftp -P 2222 usernameserver_ip连进去后就是sftp提示符交互界面。常用命令分成两类远端操作命令pwd——查看远端当前目录ls——列出远端目录文件支持-l等参数cd——切换远端目录get 远端文件 [本地路径]——下载文件put 本地文件 [远端路径]——上传文件mget/mput——批量下载/上传支持通配符mkdir/rmdir/rm/rename——执行远端目录文件管理ln——创建远端软链接本地操作命令需要在前面加l前缀lpwd——查看本地当前目录lls——列出本地目录lcd——切换本地目录! 命令——执行任意本地shell命令最常用的上传下载方式是这样的sftp lcd /home/user/deploy_pkg sftp put app_v2.0.tar.gz /data/backup/ sftp get /data/logs/error.log ./error_202405.log传大目录可以直接-R递归sftp get -R /data/www /home/user/www_backup这里要注意get和put都支持在命令里直接写目标端路径不用非得先cd过去再操作养成这个习惯能少敲很多命令。3.2 非交互与批处理模式脚本自动化的基础交互模式适合人工处理但要做定时传输的话就得用批处理文件。把要执行的sftp命令写进一个文件lcd /home/user/export cd /remote/data/import put daily_report.csv bye然后这样执行sftp -b batch_file usernameserver_ip有些版本对-b参数有约束批处理模式一旦出错就退出不会继续执行后面的命令。如果希望忽略单个错误继续跑可以加参数sftp -b - usernameserver_ip EOF put file1.txt put file2.txt quit EOF实测下来用heredoc方式比-b文件更灵活因为可以在shell脚本里动态拼接命令列表。但无论是哪种自动化方式密码处理都是个大坑。sftp不能直接在命令行里写密码指望脚本里echo密码进去是不行的。要自动化就必须配SSH密钥步骤在3.4节。3.3 图形化工具的配置要点内网用户不习惯命令行时我常用FileZilla这类SFTP客户端帮他们装好、配好连接。需要注意的坑是连接协议要选SFTP - SSH File Transfer Protocol不是普通的FTP。端口默认22。主机地址、用户名、密码填好第一次连会弹SSH主机指纹确认核对指纹无误后选择信任即可。如果连接报了服务器不支持的协议之类的错大概率是用户在客户端选了FTP协议。这个错我在帮人排查时遇到过好几次看着像服务器配置有问题实际就是客户端选错协议。另外FileZilla支持把私钥文件直接作为认证方式连接时选择密钥文件就能省去密码输入。使用Ed25519或RSA私钥都可以。3.4 密钥认证配置把密码这一步彻底去掉密钥登录是SFTP自动化的基础也是安全加固的重要一步。在客户端生成密钥对ssh-keygen -t ed25519 -C file-transfer-comment一路回车即可默认生成 ~/.ssh/id_ed25519私钥和 ~/.ssh/id_ed25519.pub公钥。然后把公钥推送到服务器上ssh-copy-id -i ~/.ssh/id_ed25519.pub filetransferserver_ip这条命令会自动把公钥追加到远端用户主目录的 ~/.ssh/authorized_keys 文件中。没有ssh-copy-id的时候可以手动创建mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys echo 公钥内容 ~/.ssh/authorized_keys权限是关键。SSH对密钥文件权限有硬性检查~/.ssh目录不能是其他用户可写authorized_keys文件权限不能超过600。如果配好密钥仍然提示要密码检查一下是不是authorized_keys的权限被改到了644这是一个非常容易踩的坑。4. 权限限制、日志审计与传输体验优化4.1 ChrootDirectory把用户关在指定目录里SFTP做文件交付时最有价值的安全功能是ChrootDirectory。它能将一个SFTP用户限制在指定目录中用户看不到目录之外的任何文件也不能跳出这个范围。在 /etc/ssh/sshd_config 的末尾加一段专属配置Match Group sftpusers ChrootDirectory /data/sftp/%u ForceCommand internal-sftp X11Forwarding no AllowTcpForwarding no PasswordAuthentication yes然后创建目录并设置权限sudo mkdir -p /data/sftp/uploader sudo chown root:root /data/sftp/uploader sudo mkdir -p /data/sftp/uploader/incoming sudo chown uploader:uploader /data/sftp/uploader/incoming sudo usermod -aG sftpusers uploader这里有个非常隐蔽的细节ChrootDirectory指向的目录本身必须属于root且权限为755否则用户会直接连不上日志里报 connection closed。因为chroot后用户需要进入这个目录但该目录的属主不能是普通用户自己否则存在越权风险。我在第一次配置时在这里卡了一个小时后来才发现是目录属主不对。目录架构建议建一个incoming子目录给用户写入这样对方能上传但看不到系统其它部分下载区单独放一个目录读写分离结构更清晰。4.2 日志怎么看追踪连接与传输痕迹排查SFTP问题最直接的办法是看SSH日志。Ubuntu系统使用systemd之后认证日志集中在 /var/log/auth.log可以直接看也可以用journalctlsudo journalctl -u ssh --since today常见的有用信息包括Accepted password for xxx——密码登录成功Accepted publickey for xxx——密钥登录成功Failed password——密码错误userauth request for invalid user——错误用户名尝试subsystem request for sftp by user xxx——用户已经请求SFTP子系统传输文件本身的路径、文件名、大小一般不会记录在auth.log里需要进一步配置SFTP自身的日志功能。但至少连接结果、认证方式、来源IP这些信息是有的。要做严格审计的可以配合auditd监控被传输目录的读写变化这个我后续有机会再单独写。4.3 传输速度与稳定性的实战调优SFTP传输慢很多人第一反应是加密算法拖累性能实际上绝大多数场景瓶颈不在这里。以下几个原因优先级更高机器之间网络质量差、延迟高时SFTP的单流传输会受限。传统TCP单连接在长肥网络高带宽高延迟环境里跑不满带宽这是TCP本身的窗口机制决定的。要跑满带宽要么调大缓冲区要么多开几个连接并发传输。在sftp命令行里能做的调整之一是加大读写缓冲sftp -B 65536 usernameserver_ip默认缓冲区较小加大到64KB以上对跨地域传输有帮助。如果传输的是大量小文件瓶颈更多在往返延迟和目录操作的每秒次数上这时候建议先把文件打包成tar.gz再传性能提升非常明显。还有一个隐藏点如果服务端CPU太老也会导致加解密慢。可以在sshd_config里限定使用更快的高性能算法组但我不建议为了性能牺牲安全性默认算法就够了。连接稳定性方面常遇到长时间挂着的SFTP会话被中间设备掐断。命令行参数有对应的保活手段sftp -o ServerAliveInterval30 -o ServerAliveCountMax3 usernameserver_ip意思是每30秒发一个心跳包连续3个回包没收到就断开。这样既保持会话不被闲置掐断又能在远端真死机时及时归还资源。5. 日常问题排查与避坑经验5.1 连不上连接被拒绝的排查顺序SFTP登录失败时的报错五花八门我按出现频率排一下排查顺序先确认SSH服务还活着systemctl status ssh服务没问题的话确认端口ss -lnpt | grep sshd接着确认防火墙有没有放行22端口sudo ufw status如果是云服务器还要检查安全组是否放行了对应端口。我经历过一次最诡异的情况本地nc测端口通但SFTP就是连接超时后来发现是安全组只放行了TCP 22但客户端走了IPv6地址IPv6的流量被安全组丢弃了。这种问题用套接字统计根本看不出来必须用tcpdump抓包或者关掉IPv6才能定位。5.2 登录成功但传不了文件目录权限问题登录进去get和ls都正常一put就报Permission denied这个问题十有八九是目标目录的写权限不对。解决思路ls -ld /data/sftp/uploader确认当前登录用户对该目录是否有写权限。注意设置属主和组权限时不要图省事直接chmod 777稍微规范一点的用法是把目录属主改为用户或者把用户加入目录所属的组sudo chown uploader:uploader /data/sftp/uploader/incoming sudo chmod 775 /data/sftp/uploader/incoming同样下载时提示Permission denied往往是因为文件权限是600且属主是root。要么用sudo下载要么通过ACL给用户增加读权限。我自己的习惯是给只读账号所在的组开放读权限既不让对方有写权限又保证能拿到文件。5.3 文件名和内容乱码的两类不同原因乱码问题得先分清是内容乱码还是文件名乱码。内容乱码多数是文本文件编码不一致比如远端生成的是GBK编码报表本地终端默认按UTF-8解析就会显示乱码。这个跟SFTP无关处理时用iconv转码即可iconv -f GBK -t UTF-8 error.log error_utf8.log它本质上是字节流不属于传输层面的问题。文件名乱码则可能跟字符集有关。Linux下文件名是UTF-8编码如果Windows客户端用本地字符集去显示就可能出现中文名变问号。这个属于客户端显示问题在FileZilla里设置强制UTF-8编码可以缓解。如果乱码发生在服务端检查一下文件系统挂载的字符集设置特别是老旧的FAT格式挂载磁盘传输文件时容易出问题。5.4 自动化脚本里常见的三个坑做定时SFTP传输时踩得最多的坑是这三个第一脚本里用echo密码或expect自动应答密码这是最不推荐的。明文密码会留在shell历史记录里换密码后脚本还会挂而且要额外装expect。正确做法是密钥配对5分钟能配完一劳永逸。第二批处理模式遇到单个文件失败就整体退出。例如远端磁盘满了某个文件传不进去整个脚本后面的文件全部不会执行。可以在脚本里给每个文件单独调用sftp或者用heredoc方式配合判断至少能让脚本在单个任务失败后仍能继续跑。第三服务器端ssh主机指纹变更后脚本会突然报Host key verification failed。这种情况多半是服务器重装过系统或sshd host key被重新生成。处理方式是更新客户端known_hosts中的记录ssh-keygen -R server_ip重新连接一次让指纹重新写入known_hosts即可。自动化任务里最好提前监控到这个错误不然半夜里任务断了都不知道。6. 再补几个实操中的小习惯使用SFTP久了之后我养成了一些固定习惯代码里敲起来也很顺需要传大量小文件时先tar打包再传速度差距可以在10倍以上。示例tar czf logs_archive.tar.gz /var/log/myapp/ sftp put logs_archive.tar.gz传输前先检查远端磁盘空间避免传一半发现磁盘满了sftp df -h这会显示远端的磁盘使用情况比登录进去再执行df快点。在脚本中使用sftp时尽量在命令尾部加退出状态判断。sftp自身返回值存在一定局限性一个稳健的做法是在远端执行文件大小核对命令把结果带回本地和源文件比对确保长度一致后再认为传输成功。最后再分享一个我在实际项目中反复调整后确定的方案给每个需要对外传输文件的用户建独立目录配合chroot锁死边界认证只用密钥密码认证全部禁用。这样运维我只需要知道服务器IP和用户的公钥不需要在群里传来传去换密码。这套模式我用下来文件传输这件事基本没有再来找过我了。