OpenSSH/Cookbook/使用 SFTP 传输文件
基本 SFTP 服务不需要额外的设置,它是 OpenSSH 服务器的内置部分,并且是子系统 sftp-server(8),然后它实现了一个 SFTP 文件传输。请参阅 sftp-server(8) 的手册页。或者,子系统 internal-sftp 可以实现一个进程内 SFTP 服务器,这可能简化使用 **ChrootDirectory** 强制对客户端使用不同的文件系统根目录的配置。
在客户端上,对于 SFTP,与常规 SSH 客户端一样,可以使用相同的选项和技巧。但是,一些客户端选项可能需要使用 **-o** 参数使用完整的选项名称指定。对于许多专用的图形化 SFTP 客户端,可以使用常规 URL 来指向目标。现在许多文件管理器都内置了对 SFTP 的支持。请参阅上面的“GUI 客户端”部分。
SFTP 为访问远程系统提供了一个非常易于使用和配置的选项。再说一次,常规 SFTP 访问不需要对默认配置进行任何更改。可以使用常用的客户端,也可以使用特殊的客户端,例如 sshfs(1)。
SFTP 上传或下载可以自动化。前提是 基于密钥的认证。一旦基于密钥的认证正常工作,就可以使用批处理文件通过 SFTP 执行活动。请参阅 sftp(1) 中的 batchfile 选项 **-b**,以获取详细信息。
$ sftp -b /home/fred/cmds.batch -i /home/fred/.ssh/foo_key_rsa server.example.org:/home/fred/logs/
如果使用连字符 (**-**) 作为批处理文件名,则 SFTP 命令将从 **stdin** 读取。
$ echo "put /var/log/foobar.log" | sftp -b - -i /home/fred/.ssh/foo_key_rsa server.example.org:/home/fred/logs/
可以发送多个 SFTP 命令,但最好使用批处理文件模式。
$ echo -e "put /var/log/foobar.log\nput /var/log/munged.log" | sftp -b - -i /home/fred/.ssh/foo_key_rsa server.example.org:/home/fred/logs/
批处理文件模式在 cron 作业和脚本中非常有用。
使用 sshd_config(5) 中的 **Match** 指令,可以限制特定组的成员只能使用 SFTP 与服务器交互。
Subsystem sftp internal-sftp
Match Group sftp-only
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp
请注意,禁用 TCP 转发不会提高安全性,除非用户也被拒绝 shell 访问权限,因为他们原则上可以安装自己的转发器。
请参阅 ssh_config(5) 中的 PATTERNS,以获取有关 **Match** 可用模式的更多信息。
通常,一组帐户需要读取和写入服务器上其主目录中的文件,而很少或根本没有理由访问文件系统的其他部分。SFTP 提供了一个非常易于使用和配置的 chroot。在某些情况下,将用户 chroot 到他们的主目录就足够了。这可能并不像看起来那样简单,因为在大多数情况下,主目录不归 root 所有,至少允许一个用户写入。但是,由于 SFTP chroot 要求 chroot 目标目录和所有父目录都归 root 所有,并且任何其他用户都不可写,因此这会导致一些困难,但这是必要的。如果没有所有权限制,则完全可以逃避 chroot。 [1]
解决此限制带来的困难的一种方法是让主目录归 root 所有,并让它包含一些由普通帐户拥有的其他目录和文件,该用户实际上可以写入这些目录和文件。
Match Group sftp-only
ChrootDirectory %h
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp
在这种情况下,root 用户必须使用所需的目录和子目录填充目标目录,然后将它们的所有权更改为非特权帐户。
至少还有三种方法可以为 chrooted 仅 SFTP 帐户设置主目录的权限。它们各自具有强大的优势和一些缺点,因此适合某些特定情况,但不适合其他情况。
如果让各种主目录归 root 所有不切实际,则可以进行妥协。**ChrootDirectory** 可以指向 **/home**,它必须归 root 所有,然后 **ForceCommand** 可以使用 **-d** 选项将用户的 home 目录指定为启动目录。
Match Group sftp-only
ChrootDirectory /home/
ForceCommand internal-sftp -d %u
在 chroot 下嵌套另一个目录是另一种方法。子目录将可由非特权帐户写入,chroot 目标将不可写。
Match Group sftp-only
ChrootDirectory /home/%u
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp -d %u
如果需要隐藏主目录的内容,以免其他用户看到,可以使用 chmod(1)。**/home** 的权限可以为 0111,而 home 目录的权限可以为 0750、0700、0770 或 2770,等等,务必检查组成员资格。
或者,为了获得类似的效果,但具有更高的隔离性,可以将主目录嵌套到更深一层以供 chrooted 帐户使用。请注意以下目录的所有权和权限
$ ls -lhd /home/ /home/*/ /home/*/*/
drwxr-xr-x 4 root root 4.0K Aug 4 20:47 /home/
drwxr-x--- 3 root user1 4.0K Aug 4 20:47 /home/user1/
drwxr-x--- 3 root user2 4.0K Aug 4 20:47 /home/user2/
drwxr-x--- 14 user1 user1 4.0K Aug 4 20:47 /home/user1/user1/
drwxr-x--- 14 user2 user2 4.0K Aug 4 20:47 /home/user2/user2/
然后,**ChrootDirectory** 指令可以将用户锁定在他们 home 目录的上级目录中,而 **ForceCommand** 指令可以使用 **-d** 选项将用户放到他们自己的 home 目录中。登录后,他们只能看到自己的文件。这种安排也有助于后来更容易添加 chrooted shell 访问权限,因为系统目录可以添加到 chroot 中,而其他帐户却无法使用。
另一种常见情况是 chroot 访问 Web 服务器的文档根目录或服务器根目录。如果每个站点在 **/var/www/** 下都有自己的层次结构,例如 **/var/www/site1/**,那么 chroot 可以按如下方式使用
Match Group team1
ChrootDirectory /var/www/
ForceCommand internal-sftp -d site1
Match Group team2
ChrootDirectory /var/www/
ForceCommand internal-sftp -d site2
然后站点目录可以由组写入,而父目录 **/var/www/** 对于非 root 用户保持只读。
如上所示,有几种方法可以处理 chrooted SFTP 服务。第三种方法是将目录设置为由 root 所有,在另一个组中,但该组不可写。因此,使用以下设置,帐户“fred”可以照常登录并使用任何预制的子目录或文件,但不能向 chroot 目标本身添加任何内容。
$ ls -lhd /home/ /home/fred/ /home/fred/*
drwxr-xr-x 68 root root 4.0K Sep 4 15:40 /home/
drwxr-xr-x 21 root fred 4.0K Sep 4 15:41 /home/fred/
drwxr-xr-x 8 fred fred 4.0K Sep 4 15:44 /home/fred/Documents
drwxr-xr-x 9 fred fred 4.0K Sep 4 15:41 /home/fred/Music
drwxr-xr-x 145 fred fred 4.0K Sep 4 15:41 /home/fred/Pictures
drwxr-xr-x 5 fred fred 4.0K Sep 4 15:41 /home/fred/Videos
drwxr-xr-x 98 fred fred 4.0K Sep 4 15:41 /home/fred/WWW
相应的 ssh_config(5) 行将如下所示,其中帐户“fred”是“team1”组的成员
Match Group team1
ChrootDirectory /home/%u
ForceCommand internal-sftp
这种方法设置起来很快,但缺点是每次向主目录添加新目录或文件时都需要系统管理员干预。即使新文件或目录更改为由非特权帐户拥有,也需要 root 权限才能执行此操作。
从 OpenSSH 5.4 开始,sftp-server(8) 可以设置 umask 来覆盖用户帐户设置的默认 umask。进程内 SFTP 服务器 internal-sftp 接受与外部 SFTP 子系统相同的选项。
Subsystem sftp internal-sftp -u 0022
但是,务必记住,umask 只能限制权限,而不能放松权限。
早期版本可以通过使用辅助脚本完成相同操作,但这会使 chroot 目录变得非常复杂。辅助脚本可以是常规脚本,也可以嵌入配置文 件中,但两种方法在 chroot 监狱中都不易使用。通常,获取支持 umask 作为服务器配置一部分的较新版本的 sshd(8) 更容易。以下是在 OpenSSH 5.3 及更早版本中使用的 umask 内联辅助脚本,该脚本基于 gilles@ 提供的脚本。
Subsystem sftp /bin/sh -c 'umask 0022; /usr/libexec/openssh/sftp-server'
无论哪种方式,此 umask 都仅在服务器端生效。客户端上的原始文件权限通常(但并非始终)会在计算服务器上的最终文件权限时使用。这取决于客户端本身。大多数客户端将文件权限传递给服务器,FileZilla 是一个值得注意的例外。因此,权限通常可以收紧,但不能放松。例如,在客户端上模式为 600 的文件不会自动变为 664 或任何小于原始 600 的值,无论服务器端的 umask 是什么。除非客户端不转发权限,在这种情况下,将只使用服务器的 umask。因此,对于大多数客户端,如果您希望上传文件的权限更宽松,请在上传之前更改客户端上的权限。
在 chroot 访问进一步限制的几种常见情况下。
另一种常见情况是将一组用户 chroot 到他们负责的 Web 服务器的不同级别。出于显而易见的原因,从监狱内部指向 chroot 监狱外部的文件系统的符号链接对于 chroot 用户不可访问。因此,如果存在特殊访问组合,则必须更仔细地规划目录层次结构。请参阅前面有关 chroot SFTP 帐户的部分。
在这些类型的目录中,可能需要为不止一个组提供不同级别的访问权限。在这种情况下,可能需要 ACL。
可以执行更复杂的匹配。可以允许一组用户使用 SFTP,但不能使用 shell 登录,只有当他们从特定地址或地址范围登录时才可以使用。如果他们从正确的地址登录,则获得 SFTP 且仅获得 SFTP,但如果他们尝试从其他地址登录,则将完全拒绝访问。需要考虑这两种情况,即肯定匹配和否定匹配。
Subsystem sftp internal-sftp
Match Group sftp-only, Address 192.0.2.10
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp
ChrootDirectory /home/servers/
Match Group sftp-only, Address *,!192.0.2.10
DenyGroups sftp-only
请注意,对于否定,必须先指定通配符,然后指定要排除的地址或范围。注意空格或无空格。可以通过 CIDR 地址/掩码格式指定地址范围,例如 192.0.2.0/24。可以指定任意数量的标准,并且只有当所有标准都满足时,后续行中的指令才会生效。
第一个匹配的Match 块将生效,因此在构建条件块时必须小心,以使它们适合所需的确切情况。此外,任何不符合Match 条件块的情况都将被忽略。这些将获得一般配置设置,无论它们是什么。可以使用服务器的-T 和-C 选项使用配置测试特定用户和源地址组合,以获取更多选项。有关详细信息,请参阅部分调试服务器配置。
如果不使用internal-sftp 进程内 SFTP 服务器,则日志记录守护程序必须在 chroot 目录中建立套接字,以便 sftp-server(8) 子系统访问为/dev/log 请参阅有关日志记录的部分。
为交互式 Shell 创建 chroot 监狱很困难。chroot 及其所有组件必须是 root 拥有的目录,任何其他用户或组都不可写。ChrootDirectory 必须包含支持用户会话所需的文件和目录。对于交互式会话,这至少需要一个 shell,通常为 bash(1)、ksh(1) 或 sh(1),以及/dev 内部的基本设备节点,例如 null(4)、zero(4)、stdin(4)、stdout(4)、stderr(4)、arandom(4) 和 tty(4) 设备。路径可能包含以下在运行时扩展的令牌,一旦连接的用户经过身份验证:%% 将替换为字面“%”,%h 将替换为正在身份验证的用户的主目录,%u 将替换为该用户的用户名。
另一种来回传输文件甚至远程使用文件的方法是使用 sshfs(1)。它是一个基于 SFTP 的用户空间文件系统客户端,并利用服务器的 SFTP 子系统。它可以使远程服务器上的目录可访问,就好像它是本地文件系统上的目录一样,可以被任何程序访问。用户必须具有挂载点的读写权限才能使用 sshfs(1)。
以下操作将在主目录中创建挂载点mountpoint(如果不存在)。然后 sshfs(1) 挂载远程服务器。
$ test -d ~/mountpoint || mkdir --mode 700 ~/mountpoint
$ sshfs [email protected]:. ~/mountpoint
读写挂载点上的文件实际上是在向远程系统传输数据或从远程系统传输数据。使用压缩可以减少传输消耗的带宽量。如果网络连接有带宽上限或每单位费用,这可能很重要。但是,如果速度是唯一的问题,如果两端的处理器都很忙或功能不够强大,压缩可能会使传输速度变慢。确保唯一的方法是进行测试,看看哪种方法更快。下面,使用-C 指定压缩。
$ sshfs -C [email protected]:. ~/mountpoint
或者尝试使用调试输出
$ sshfs -o sshfs_debug [email protected]:. /home/fred/mountpoint
命名管道无法通过 sshfs(1) 工作。使用fusermount -u
卸载这些远程目录并关闭 SFTP 会话。
ssh_command 选项用于将参数传递给 ssh(1)。在此示例中,它用于让 ssh(1) 指向用于身份验证的密钥以将远程目录/usr/src 挂载到本地目录/home/fred/src。
$ sshfs -o ssh_command="ssh -i /home/fred/.ssh/id_rsa" [email protected]:/usr/src /home/fred/src/
如果代理中已加载可用密钥,则 ssh(1) 应该找到它并代表 sshfs(1) 使用它,无需人工干预。
- ↑ "Openssh SFTP Chroot Code Execution". halfdog.net. 2018-01-07. Retrieved 2018-01-09.