第9章 SFTP 文件传输
第9章 SFTP 文件传输
9.1 SFTP 概述
SFTP(SSH File Transfer Protocol)是基于 SSH 的安全文件传输协议。它不是 FTP over SSH,而是 SSH 协议的子系统。
SFTP vs SCP vs FTP
| 特性 | SFTP | SCP | FTP/S | rsync |
|---|---|---|---|---|
| 加密传输 | ✅ | ✅ | FTPS ✅ | over SSH ✅ |
| 断点续传 | ✅ | ❌ | 部分 | ✅ |
| 目录操作 | ✅ | ❌ | ✅ | ✅ |
| 权限管理 | ✅ | 有限 | ✅ | ✅ |
| 交互式 | ✅ | ❌ | ✅ | ❌ |
| chroot 支持 | ✅ | ❌ | ❌ | 需配置 |
| 性能 | 中 | 高 | 中 | 最高(增量) |
| 推荐程度 | ⭐⭐⭐ | ⭐ | ⭐⭐ | ⭐⭐⭐ |
9.2 SFTP 服务端配置
基本配置
# /etc/ssh/sshd_config
# 启用 SFTP 子系统
Subsystem sftp /usr/lib/openssh/sftp-server
# 或使用内部 SFTP(推荐,性能更好)
Subsystem sftp internal-sftp
# 日志级别
Subsystem sftp internal-sftp -l INFO
internal-sftp vs sftp-server 的区别:
| 特性 | internal-sftp | sftp-server |
|---|---|---|
| 性能 | 更好 | 稍差 |
| chroot 支持 | ✅ | 需要额外配置 |
| 二进制依赖 | 无 | 需要二进制文件在 chroot 内 |
| 推荐程度 | ⭐⭐⭐ | ⭐⭐ |
仅 SFTP 用户配置
# /etc/ssh/sshd_config
# 创建 sftponly 组
Match Group sftponly
# 只允许 SFTP,不允许 Shell
ForceCommand internal-sftp -l INFO
# 禁用所有不需要的功能
AllowTcpForwarding no
X11Forwarding no
AllowAgentForwarding no
PermitTunnel no
# 不分配伪终端
PermitTTY no
# 禁止 root
PermitRootLogin no
9.3 chroot 限制
什么是 chroot?
chroot(Change Root)将用户的根目录限制在指定目录下,用户无法访问该目录之外的文件系统。
用户看到的根目录
/
├── uploads/
├── data/
└── readme.txt
实际文件系统
/home/sftpuser/ ← chroot 目录
├── uploads/
├── data/
└── readme.txt
/etc/ ← 用户不可见
/var/ ← 用户不可见
/root/ ← 用户不可见
chroot 配置
# /etc/ssh/sshd_config
Match Group sftponly
ChrootDirectory /home/%u
ForceCommand internal-sftp -l INFO
AllowTcpForwarding no
X11Forwarding no
PermitTTY no
占位符说明:
| 占位符 | 说明 |
|---|---|
%u | 用户名 |
%h | 用户主目录 |
%l | 本地主机名 |
%L | 本地短主机名 |
%i | 本地用户 ID |
chroot 目录权限要求
SSH 对 chroot 目录有严格的权限要求:
/home/sftpuser/ ← root:root, 755 (chroot 目录)
├── uploads/ ← sftpuser:sftponly, 755 (用户可写目录)
├── data/ ← sftpuser:sftponly, 755
└── download/ ← root:root, 755 (只读目录)
关键规则:
- chroot 目录及其所有父目录必须由 root 拥有
- chroot 目录不能被非 root 用户写入
- 用户的可写目录在 chroot 内部创建
# 创建 chroot 目录结构
sudo mkdir -p /home/sftpuser/uploads
sudo mkdir -p /home/sftpuser/download
# 设置权限
sudo chown root:root /home/sftpuser
sudo chmod 755 /home/sftpuser
sudo chown sftpuser:sftponly /home/sftpuser/uploads
sudo chmod 755 /home/sftpuser/uploads
sudo chown root:root /home/sftpuser/download
sudo chmod 755 /home/sftpuser/download
批量创建 SFTP 用户
#!/bin/bash
# create-sftp-user.sh
USERNAME=$1
GROUP="sftponly"
if [ -z "$USERNAME" ]; then
echo "Usage: $0 <username>"
exit 1
fi
# 创建组(如果不存在)
getent group $GROUP > /dev/null || sudo groupadd $GROUP
# 创建用户
sudo useradd -m -g $GROUP -s /usr/sbin/nologin "$USERNAME"
# 设置密码
sudo passwd "$USERNAME"
# 创建 chroot 目录结构
sudo mkdir -p /home/$USERNAME/{uploads,download}
sudo chown root:root /home/$USERNAME
sudo chmod 755 /home/$USERNAME
sudo chown $USERNAME:$GROUP /home/$USERNAME/uploads
sudo chmod 755 /home/$USERNAME/uploads
sudo chown root:root /home/$USERNAME/download
sudo chmod 755 /home/$USERNAME/download
echo "SFTP user $USERNAME created successfully"
echo "Chroot: /home/$USERNAME"
echo "Writable: /home/$USERNAME/uploads"
echo "Read-only: /home/$USERNAME/download"
9.4 SFTP 客户端使用
交互式命令
# 连接到 SFTP 服务器
sftp user@server
# 指定端口
sftp -P 2222 user@server
# 使用 SSH 别名
sftp production
常用命令:
| 命令 | 说明 |
|---|---|
ls | 列出远程文件 |
lls | 列出本地文件 |
cd | 切换远程目录 |
lcd | 切换本地目录 |
put localfile | 上传文件 |
get remotefile | 下载文件 |
put -r localdir | 上传目录 |
get -r remotedir | 下载目录 |
mkdir | 创建远程目录 |
rmdir | 删除远程目录 |
rm | 删除远程文件 |
chmod | 修改远程文件权限 |
chown | 修改远程文件所有者 |
rename | 重命名远程文件 |
df -h | 查看远程磁盘空间 |
!command | 执行本地命令 |
bye / exit | 退出 |
非交互式使用
# 上传文件
sftp user@server << EOF
put localfile.txt /remote/path/
bye
EOF
# 下载文件
sftp user@server << EOF
get /remote/file.txt ./local/
bye
EOF
# 使用 -b 批处理文件
echo "put upload.tar.gz /backups/
bye" > sftp_batch.txt
sftp -b sftp_batch.txt user@server
9.5 权限控制
基于公钥选项的权限控制
# ~/.ssh/authorized_keys
# 只允许 SFTP,不允许 Shell 或端口转发
command="internal-sftp",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3... sftp-client
# 只允许上传到特定目录
command="internal-sftp -d /uploads",no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3... uploader
基于文件系统的权限控制
# 推荐的目录权限结构
/sftp-root/ root:root, 711
├── user1/ root:root, 755
│ ├── incoming/ user1:sftponly, 755 (可写)
│ ├── outgoing/ user1:sftponly, 750 (受限)
│ └── archive/ root:root, 755 (只读)
├── user2/ root:root, 755
│ ├── incoming/ user2:sftponly, 755
│ └── outgoing/ user2:sftponly, 750
使用 ACL(访问控制列表)
# 安装 ACL 工具
sudo apt install acl
# 设置 ACL 权限
sudo setfacl -R -m g:sftponly:rwx /sftp/uploads
sudo setfacl -R -m g:sftponly:rx /sftp/downloads
# 设置默认 ACL(新建文件自动继承)
sudo setfacl -d -m g:sftponly:rwx /sftp/uploads
# 查看 ACL
getfacl /sftp/uploads
使用 quota 限制磁盘配额
# 安装 quota
sudo apt install quota
# 编辑 /etc/fstab,为 /home 分区启用 quota
# /dev/sda1 /home ext4 defaults,usrquota,grpquota 0 2
# 重新挂载
sudo mount -o remount /home
# 初始化 quota 数据库
sudo quotacheck -cugm /home
sudo quotaon /home
# 设置用户 quota
sudo edquota -u sftpuser
# 设置 soft limit 和 hard limit
9.6 SFTP 日志与审计
# /etc/ssh/sshd_config
Subsystem sftp internal-sftp -l INFO
# 日志位置
# /var/log/auth.log (Debian/Ubuntu)
# /var/log/secure (RHEL/CentOS)
# 查看 SFTP 日志
sudo journalctl -u sshd | grep sftp
sudo grep sftp-server /var/log/auth.log
# 日志级别说明:
# QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3
# INFO:记录文件上传/下载操作
# DEBUG+:记录详细的 SFTP 命令
使用 auditd 记录 SFTP 操作
# 安装 auditd
sudo apt install auditd
# 监控 SFTP 目录
sudo auditctl -w /sftp -p rwxa -k sftp-access
# 查看审计日志
sudo ausearch -k sftp-access
# 永久规则
# /etc/audit/rules.d/sftp.rules
-w /sftp -p rwxa -k sftp-access
9.7 使用场景
场景一:客户文件上传
# 创建专用上传账户
sudo useradd -m -g sftponly -s /usr/sbin/nologin client-upload
sudo passwd client-upload
# 配置 chroot
sudo mkdir -p /home/client-upload/incoming
sudo chown root:root /home/client-upload
sudo chmod 755 /home/client-upload
sudo chown client-upload:sftponly /home/client-upload/incoming
# 客户端使用
sftp client-upload@server
# put file.zip /incoming/
# bye
场景二:备份文件传输
#!/bin/bash
# backup-sftp.sh
HOST="backup-server"
USER="backup"
BACKUP_DIR="/backups/$(hostname)/$(date +%Y%m%d)"
# 创建远程目录
sftp $USER@$HOST << EOF
mkdir $BACKUP_DIR
bye
EOF
# 传输备份文件
sftp $USER@$HOST << EOF
put /tmp/backup.tar.gz $BACKUP_DIR/
put /tmp/db-dump.sql $BACKUP_DIR/
bye
EOF
echo "Backup uploaded to $HOST:$BACKUP_DIR"
场景三:自动化部署
# 使用 rsync over SSH(比 sftp 更高效)
rsync -avz -e ssh \
--delete \
--exclude='.git' \
--exclude='node_modules' \
./dist/ deploy@server:/var/www/html/
# 或使用 sftp 批处理
sftp -b - deploy@server << EOF
cd /var/www/html
put -r ./dist/*
bye
EOF
扩展阅读
下一章: 第10章 安全加固实战 → 学习禁用密码、Fail2Ban、端口敲门等安全加固技术。