记录一次SSH和SFTP服务分离后文件上传权限问题
开门见山
因服务器安全需求,需要将ssh和sftp服务分离,并创建一个用户组sftpuser::sftp,根目录权限均正常。用户sftpuser仅能通过sftp访问服务器,不能通过ssh访问服务器。但是,ssh应用用户appuser::sftp通过sftp建立链接并put文件后发现权限为600,导致所属组应用权限不足,应该为660可读写。
原因是因为umask默认为002,所以需要调整umask,又因为sftptuser为sftp登录并不是ssh登录,所以这里修改/ect/profile或/ect/bashrc并不生效,所以需要修改对应的sftpd_config配置。
sftpd_config部分配置如下:
cat /etc/ssh/sftpd_config
# override default of no subsystems
#Subsystem sftp /usr/libexec/openssh/sftp-server
Subsystem sftp internal-sftp
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
Match Group sftp
ChrootDirectory /app/sftpFile
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
寻觅答案
尝试ChatGPT寻找答案,给出的是:
Subsystem sftp internal-sftp -u 0006
发现并不起作用,而且全站给出的基本都是这个答案。
研究了很长时间后,又问了DeepSeek给出的答案是:
ForceCommand internal-sftp -u 0006
这次正确了,sftpuser访问sftp上传的文件权限为660
答疑解惑
于是我又问了一下原因:
为什么在Subsystem sftp internal-sftp后面加 -u 0006 不起作用,反而在ForceCommand
internal-sftp -u 0006 起作用?
在 OpenSSH 的配置中,Subsystem sftp 和 Match 块中的 ForceCommand 对 -u 参数的处理方式不同,这是由 OpenSSH 的设计机制决定的。以下是具体原因和解决方案:
1. 根本原因分析
(1) Subsystem 指令的限制
语法规则:
Subsystem sftp 的完整语法是:
Subsystem sftp
但 internal-sftp 是 OpenSSH 内置的 SFTP 实现,直接跟在 Subsystem 后的参数会被忽略(这是历史遗留设计)。
实际行为:
即使您配置:
Subsystem sftp internal-sftp -u 0006
OpenSSH 仍会忽略 -u 0006,仅启用 internal-sftp 的默认行为(通常 umask 077)。
(2) ForceCommand 的正确用法
设计意图:
ForceCommand 会覆盖用户登录后的默认 Shell,其参数会完整传递给目标命令(包括 internal-sftp)。
生效逻辑:
ForceCommand internal-sftp -u 0006
这里的 -u 0006 会直接传递给 internal-sftp 进程,因此能正确设置 umask。
2. 技术细节对比
配置位置 参数传递机制 是否支持 -u 参数
Subsystem 参数被 OpenSSH 主进程忽略 ❌
ForceCommand 参数完整传递给子进程 ✅
3. 其他注意事项
目录权限要求:
ChrootDirectory(如 /app/sftpFile)必须满足:
属主为 root。
权限为 755(drwxr-xr-x)。
不可被属主或属组写入(安全限制)。
总结
为什么 Subsystem 中 -u 无效:OpenSSH 的设计限制,参数被忽略。
为什么 ForceCommand 有效:参数直接传递给 SFTP 子进程。
最终方案:在 Match 块的 ForceCommand 中指定 -u 0006,这是官方推荐的做法