Sshd

為什麼 SFTP 客戶端不能在其 NFS 掛載的主目錄中重命名文件?

  • February 23, 2018

我有一個執行 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 文件儲存網關)應該沒問題。我很茫然,任何幫助表示讚賞。

感謝@Kenster 的提示,我發現了這個問題。我錯誤地假設 AWS 文件儲存網關 NFS 掛載支持硬連結,因為文件明確指出它不支持。

我非常確定我最終使用 strace 跟踪系統呼叫就是這種情況。如果在 ssh 連接到伺服器時將 sftp 客戶端附加到伺服器,則使用 .d 獲取目前 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 重命名與實現供應商特定CMD_EXTENDED協議的某些客戶端(如 Paramiko)一起使用,正如Paramiko 所做的那樣:

   oldpath = self._adjust_cwd(oldpath)
   newpath = self._adjust_cwd(newpath)
   self._log(DEBUG, 'posix_rename({!r}, {!r})'.format(oldpath, newpath))
   self._request(
       CMD_EXTENDED, "posix-rename@openssh.com", oldpath, newpath
   )

似乎沒有任何方法可以強制posix-rename所有客戶端使用該選項,但至少我們知道發生了什麼以及為什麼。

引用自:https://unix.stackexchange.com/questions/425973