如何将仅限SFTP的SSH用户chroot到他们的主目录中?

我想给一个客户访问我的服务器的权限,但是我希望限制这些用户只能访问他们的家目录。我会通过绑定挂载的方式,将我希望他们能够看到的文件加入进去。
我创建了一个名为"bob"的用户,并将其添加到一个名为"sftponly"的新组中。他们的家目录位于"/home/bob"。我将他们的shell更改为"/bin/false"以阻止SSH登录。以下是他们的"/etc/passwd"行:
bob:x:1001:1002::/home/bob:/bin/false

我还修改了/etc/ssh/sshd_config文件,添加了以下内容:
Match Group sftponly
        ChrootDirectory /home/%u
        ForceCommand internal-sftp
        AllowTcpForwarding no

当我尝试以他们的身份登录时,这是我看到的内容。
$ sftp bob@server
bob@server's password: 
Write failed: Broken pipe
Couldn't read packet: Connection reset by peer

如果我将ChrootDirectory行注释掉,我就可以通过SFTP登录,但这样他们就对服务器有了完全自由。我发现ChrootDirectory /home是有效的,但它仍然允许他们访问任何家目录。我明确尝试过ChrootDirectory /home/bob,但也不起作用。

我做错了什么?如何限制Bob只能访问/home/bob/

----编辑-----

好的,我刚刚查看了/var/log/auth.log,看到了以下内容:

May  9 14:45:48 nj sshd[5074]: pam_unix(sshd:session): session opened for user bob by (uid=0)
May  9 14:45:48 nj sshd[5091]: fatal: bad ownership or modes for chroot directory component "/home/bob/"
May  9 14:45:48 nj sshd[5074]: pam_unix(sshd:session): session closed for user bob

我不完全确定那里发生了什么事,但它暗示用户目录出现了问题。这是ls -h /home的输出结果:
drwxr-xr-x 26 oli      oli      4096 2012-01-19 17:19 oli
drwxr-xr-x  3 bob      bob      4096 2012-05-09 14:11 bob

4我相信ChrootDirectory /home/%u可以被替换为ChrootDirectory %h - Franck Dernoncourt
5个回答

所有这些痛苦都源于几个安全问题,如在此处描述。基本上,chroot目录必须归root所有,并且不能有任何组写入权限。太好了。因此,您基本上需要将您的chroot变成一个拘留单元,在其中可以放置可编辑内容。

sudo chown root /home/bob
sudo chmod go-w /home/bob
sudo mkdir /home/bob/writable
sudo chown bob:sftponly /home/bob/writable
sudo chmod ug+rwX /home/bob/writable

然后,你就可以登录并在/writable中写入内容。

4非常感谢,真的很有用。不过有两个问题。1)虽然我不能写文件,但仍然可以浏览整个文件系统。2)将shell更改为/bin/false会完全阻止SFTP。我做错了什么吗? - kim3er
2谢谢!许多其他关于这个主题的文章都忽略了这个细节,有些甚至导致服务器无法接受SSH连接(当你在EC2上时,这真的很糟糕,因为那是唯一的方式)。 - Tom Harrison Jr
@kim3er 你把用户组设置为sftponly了吗?如果没有,SSH服务器将无法匹配并chroot它。尝试使用命令"usermod sftponly $USER"。另外,/bin/false只是移除了SSH访问权限。如果你想让一个被chroot的用户拥有SSH访问权限,还需要进行更多步骤。 - Evan Plaice
4kim3er: 这是因为你需要将用户的shell设置为/sbin/nologin -- /bin/false会禁止任何形式的访问。 - user162889
11我还想指出的是,所有通往您的chroot文件夹的文件夹都应该由“root”拥有。在这个例子中,“/home”也应该由root拥有。 - Shiki
4在14.04版本中,我还必须将Subsystem sftp /usr/lib/openssh/sftp-server这一行更改为Subsystem sftp internal-sftp -f AUTH -l VERBOSE,然后才能使其正常工作。 - partofthething
这是我在花了大约一个小时的时间尝试和谷歌搜索后找到的对这个问题最好的描述。 - Kelderic
1这意味着无法将用户chroot到一个可写目录中:你必须拥有一个具有写权限的子文件夹? - baramuse
2@baramuse,你可以在用户登录后通过在/etc/ssh/sshd_config中使用ForceCommand internal-sftp -d /writable来将用户“移动”到可写目录。然而,他们仍然可以浏览回只读的chroot目录。来源 - warbi
这是将用户bob添加到组sftponly的方法:adduser bob sftponly - rubo77
完成后,您可以使用mkdir /home/bob/localdir/; mount --bind /original/path /home/bob/localdir/将其他目录绑定到监狱用户的主文件夹中。 - rubo77
不确定,但感觉这个答案似乎漏掉了chmod 750 /home/bob,因为连接后Bob仍然需要读取权限,否则如果设置了ChrootDirectory /home/%u,他将看不到任何内容。 - Torxed

要对SFTP目录进行chroot,您必须:
  1. 创建一个用户并强制将其所有者设置为root

    sudo mkdir /home/john
    useradd -d /home/john -M -N -g users john
    sudo chown root:root /home/john
    sudo chmod 755 /home/john
    
  2. 更改/etc/ssh/sshd_config中的子系统位置:

    #Subsystem sftp /usr/lib/openssh/sftp-server
    Subsystem sftp internal-sftp
    

    并在文件末尾创建一个用户部分(如果放在Subsystem行之后,ssh可能会死循环重启):

    Match User john
        ChrootDirectory %h
        ForceCommand internal-sftp
        AllowTCPForwarding no
        X11Forwarding no
    

4这似乎不起作用。在你的例子中,用户john被锁定在/home/john目录下。你已经授予该目录755权限。所以所有者拥有读取(4)、写入(2)和执行(1)权限,而组拥有读取(4)和执行(1)权限。你还将所有者和组设置为root,所以john属于其他人。他也具有读取和执行权限(4+1=5)。因此,你将john锁定在一个没有写入权限的目录中。如何修复这个问题呢?将权限更改为757甚至777会破坏登录。将组或所有者更改为john也会破坏登录。 - Daniel
还要注意的是:在/etc/ssh/sshd_config文件中,配置按顺序读取。因此,如果您对一般用户有一个规则,并且想要覆盖其中的某个规则,只需将特定用户的规则放在顶部即可。 - rwenz3l
我想在其他用户的主文件夹中将SFTP目录设置为chroot。我有一个名为userx的用户,位于/home/userx/sftproot/uploads,还有一个名为sftpuser的用户,位于/home/sftpuser。我已经将/home/userx/sftproot的chroot设置为root:root所有,并且chmod 755。/home/userx/sftproot/uploads的所有者是sftpuser:sftpuser。我遇到了与上述初始问题相同的错误。难道为了使sftp正常工作,chroot和所有父文件夹都需要由root:root拥有吗? - Primoz Rome
1+1 对于 %h............. - leonheess
你如何添加约翰的密码? - user1034912
@user1034912 "John" 是一个经常使用 Linux 的用户。sudo passwd john - josircg

我花了一整天的时间试图在我的树莓派上建立一个网络共享。我想要锁定用户,使其无法浏览整个文件系统,无法通过ssh登录,并且我想要对网络共享拥有写入权限。
以下是我成功实现的步骤:
首先,我创建了一个用户:
sudo useradd netdrive

然后编辑了/etc/passwd,确保用户的行中有/bin/false,所以行变成了:
netdrive:x:1001:1004:Net Drive User,,,:/home/netdrive:/bin/false

我编辑了/etc/ssh/sshd_config文件,添加了以下内容:
Match User netdrive
  ChrootDirectory /home/netdrive
  ForceCommand internal-sftp
  AllowTcpForwarding no
  X11Forwarding no

更改主目录的所有者和权限:
sudo chown root:root /home/netdrive/
sudo chmod 755 /home/netdrive/

好的,经过这一切之后,我能够使用sshfs连接,但只能以只读模式。为了获得可写入的文件夹,我需要做什么:
sudo mkdir -p /home/netdrive/home/netdrive/
sudo chown netdrive:netdrive /home/netdrive/home/netdrive/
sudo chmod 755 /home/netdrive/home/netdrive/

那就是了,它在没有任何进一步的更改的情况下工作。请注意,我只对用户具有可写权限,而不是像许多其他在线解决方案那样对具有权限。我能够创建/删除/编辑/重命名文件/文件夹而没有任何问题。
当使用sshfsnetdrive用户访问时,由于chroot配置,我只能看到存储在服务器的/home/netdrive/目录中的内容,非常完美。重复的/home/netdrive/home/netdrive/目录结构使得我能够拥有一个干净的chroot ssh可写解决方案。

现在我将在下面解释我遇到的问题:

您可能不应该执行以下段落

在查看了上述解决方案(以及许多其他甚至使用了acl(访问控制列表)的网络解决方案)之后,我仍然无法使其工作,因为接下来我所做的是:

以下对我来说没有起作用:

sudo mkdir /home/netdrive/writable/
sudo chown netdrive:netdrive /home/netdrive/writable/
sudo chmod 755 /home/netdrive/writable/

因为尽管拥有文件夹并具有权限,但“netdrive”用户仍无法在“/home/netdrive/writable/”目录中进行写入。然后我执行了以下操作: sudo chmod 775 /home/netdrive/writable/ 现在我可以创建和删除目录,但无法编辑它,因为它是以非组可写权限创建的。根据我在网络上看到的信息,人们使用“acl”来解决这个问题。但我对此不满意,因为我需要安装“acl”,然后配置挂载点等等。而且我不知道为什么我需要“group”权限来写入由同一用户拥有的文件夹。
似乎由于某种原因,创建“/home/netdrive/home/netdrive”并将所有权交给最后一个“netdrive”文件夹,我能够使一切正常工作,而无需干涉“group”权限。

我按照这篇文章的步骤操作,但是没有起作用。 在上面的回答中有人建议我做了这个改变后,它开始起作用了。
#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

Plus在根目录下创建了可拥有的个人主目录,其中包含了用户可写的子目录(如上所述)。
我想要在这个答案中添加的新而有用的内容是,你可以通过简单地指定%h作为用户主目录来简化配置。
ChrootDirectory %h

我是通过this链接发现它的。

我根据@Oli的回答创建了一个脚本。
#!/bin/bash

# 1. adds a user to the group sftponly, which have only access to their home directory
# 2. creates a writable folder in the users home directory
# 3. mounts a folder into the users home directory with bind
# see https://askubuntu.com/questions/134425/how-can-i-chroot-sftp-only-ssh-users-into-their-homes

if [ "$1" == "" ]; then
  echo "usage: $0 [username] [path to share] [local foldername]"
  exit 0
fi
U=$1

if [ "$2" == "" ]; then
  echo "no 2. param path to share given (without trailing slash)"
  exit 0
fi
PATH_TO_SHARE="$2"

if [ "$3" == "" ]; then
  echo "no 3. param local foldername given"
  exit 0
fi
LOCAL_FOLDER_NAME="$3"

set -ex

adduser $U
adduser $U sftponly
sudo chown root /home/$U
sudo chmod go-w /home/$U
sudo mkdir /home/$U/writable
sudo chown $U:sftponly /home/$U/writable
sudo chmod ug+rwX /home/$U/writable
mkdir -p "/home/$U/$LOCAL_FOLDER_NAME/"
mount --bind "$PATH_TO_SHARE/" "/home/$U/$LOCAL_FOLDER_NAME/"

echo "$PATH_TO_SHARE/ /home/$U/$LOCAL_FOLDER_NAME/ none defaults,bind 0 0">>/etc/fstab