为什么 SFTP 客户端无法重命名其 NFS 安装主目录中的文件?

为什么 SFTP 客户端无法重命名其 NFS 安装主目录中的文件?

我有一个运行 SSH 的 Amazon Linux 实例作为 SFTP 服务器。客户端登录并被 chroot 到 NFS 安装的目录中。用户可以读取、写入和删除文件,但重命名文件会因非特定“协议错误”而失败。

这是我的文件的副本sshd_config

Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
UsePrivilegeSeparation yes

KeyRegenerationInterval 3600
ServerKeyBits 1024

SyslogFacility AUTH
LogLevel INFO

LoginGraceTime 120
PermitRootLogin prohibit-password
StrictModes yes

RSAAuthentication yes
PubkeyAuthentication yes

IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no

PermitEmptyPasswords no

ChallengeResponseAuthentication no

PasswordAuthentication yes

X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
#UseLogin no

AcceptEnv LANG LC_*

# Subsystem sftp /usr/lib/openssh/sftp-server -u 0002
Subsystem sftp internal-sftp -l DEBUG -u 002 -d %u

UsePAM yes
Match Group sftpusers
    ChrootDirectory /autohome
    AllowTCPForwarding no
    X11Forwarding no
    ForceCommand internal-sftp -l DEBUG -u 002 -d %u

我看到当源和目标位于不同的文件系统上时,对 sftp rename 的引用不起作用,但这里的情况并非如此。我还看到 sftp rename 在不支持硬链接的文件系统上不起作用,但我认为我们的 NFS 服务器(AWS 文件存储网关)应该没问题。我很茫然,感谢任何帮助。

答案1

感谢@Kenster 的提示,我发现了这个问题。我错误地假设 AWS File Storage Gateway NFS 挂载支持硬链接,如文档所示明确指出但事实并非如此。

我非常确定情况就是这样,所以我用 strace 结束了跟踪系统调用。如果您在 ssh 进入服务器时将 sftp 客户端附加到服务器,请使用 获取当前 sftp 进程的 pid ps -eaf | grep sftp。然后,您可以使用 strace 跟踪系统调用,并使用以下命令将输出保存到文件中:strace -ff -p 2116 -o sftp_rename.log其中 -ff 跟踪子进程,-p 是 pid,-o 是输出文件。

这会给你一些看起来非常糟糕的输出,但我发现有趣的是这一点:

write(7, "\0\0\0L\0\0\0\3\0\0\0Drename old \"/testuse"..., 80) = 80
lstat("/testuser/test/asdfasdf.txt", {st_mode=S_IFREG|0664, st_size=159, ...}) = 0
link("/testuser/test/asdfasdf.txt", "/testuser/test/as.txt") = -1 ENOTSUPP (Unknown error 524)

然后我用一个简单的链接命令进行了测试以创建硬链接,但失败了。

# ln asdfasdf.txt link.txt
ln: failed to create hard link ‘link.txt’ => ‘asdfasdf.txt’: Unknown error 524
#

这让我回到了 AWS 的文档。但这还不是全部,显然是 SFTP 重命名将要与某些实现特定于供应商的客户(如 Paramiko)合作CMD_EXTENDED,例如帕拉米科确实:

    oldpath = self._adjust_cwd(oldpath)
    newpath = self._adjust_cwd(newpath)
    self._log(DEBUG, 'posix_rename({!r}, {!r})'.format(oldpath, newpath))
    self._request(
        CMD_EXTENDED, "[email protected]", oldpath, newpath
    )

似乎没有任何方法可以强制posix-rename所有客户端使用该选项,但至少我们知道发生了什么以及为什么。

相关内容