ChrootDirectory and User’s Home Directory – How They Work Together

chrootsshd

I need to create an SFTP only user on CentOS 7. I've read how to do it from various sources. The setup needs to only support a single user with SFTP only access to a single folder.

If the user's home directory is /home/user and in sshd_config I have ChrootDirectory as %h, given that sshd will change directory to /home/user AFTER the chroot:

ChrootDirectory
Specifies the pathname of a directory to chroot(2) to after authentication. All components of the pathname must be root-owned directories that are not writable by any other user or group. After the chroot, sshd(8) changes the working directory to the user's home directory.

How does this work? Surely it will fail, because sshd will try to CD to /home/user/home/user?

Supplementary question: Is there a best practise for whether to chroot such a user in the home directory as defined when creating the user, or having them chrooted outside of /home e.g. /var/sftp/user? When creating a chroot outside of /home, what is the purpose of the user's home directory? Is it still used to read ~/.ssh/authorized_keys?

Best Answer

You'll need to pay attention to the other restriction placed on the directory used as ChrootDirectory: All components of the pathname must be root-owned directories that are not writable by any other user or group. If the user needs to be able to write to their own home directory inside the chroot, then the home directory must not be the same as ChrootDirectory.

Historical note:

The chroot support of OpenSSH originated as a separate patch, and even after it was integrated to the main OpenSSH distribution the exact requirements placed on the directory used as ChrootDirectory have changed with different OpenSSH versions. Newer versions generally tend to enforce more strict requirements than older ones, in response to discovered security vulnerabilities in previous set-ups.

Also, the requirement to have the same home directory path be applicable both inside and outside the chroot is clearly suboptimal, and some distributions have applied patches to modify the behavior. Unfortunately, not all such patches in the past have included updates to the man pages and other relevant documentation.

But to satisfy the requirements as written, you could do this, for example:

mkdir -p /jail/username/home

# First, the chroot directory:
chown root:root /jail/username
chmod 755 /jail/username

# Then, the user's home directory:
chown username: /jail/username/home
chmod 750 /jail/username/home
usermod -d /jail/username/home username

# And here's the magic:
cd /jail/username
ln -s . jail       # this would normally be a silly thing to do
ln -s . username   # but with chroot it can be useful

Now, we can set ChrootDirectory /jail/%u.

When viewed outside the chroot, the user's home directory will be /jail/username/home. A bit of a departure from the normal naming convention, but otherwise nothing special.

And inside the chroot, the same home directory path will indeed refer to /jail/username/jail/username/home... but did you see those two silly symbolic links above? Dereference them, and you'll get /jail/username/././home, which ends up being exactly the same as /jail/username/home. And so the same path ends up pointing to the same place both inside and outside the chroot.

The user inside the chroot will see their home directory as /jail/username/home = /././home = /home, and they can use it as normal. They will be able to see one level above their home directory, /, but only root can write there.

This will also allow things like syslog-style logging: you can create a root-owned directory /jail/username/dev and tell rsyslog to create an extra /dev/log-style socket at /jail/username/dev/log... and now the chrooted user can produce log messages and have them handled by the regular syslog subsystem.

This is by no means the only way to arrange the chroot environment, although the above-style setup makes the user's home directory as normal (= no symlinks nor other weirdness) as possible for processes outside the chroot, if that is important.

If you instead want a chroot that is maximally sterile for the jailed user, you could do it this way instead:

mkdir -p /jail/username/username

# Prepare the chroot directory
chown root:root /jail /jail/username
chmod 755 /jail /jail/username

# Prepare the user's actual home directory
chown username: /jail/username/username
chmod 750 /jail/username/username

# Make it usable outside the chroot too
ln -s /jail/username/username /username

# And now it can be assigned to the user.
usermod -d /username username

Again, we set ChrootDirectory /jail/%u.

Outside the chroot, /username will be a symbolic link pointing to /jail/username/username, so the user's home directory will be valid.

For chrooted processes /username will be just a regular directory, perfectly usable as user's home directory.

Yes, the actual pathnames are a bit repetitive, and the symbolic links will clutter up the root directory of the system, but there will be nothing extraneous inside the chroot environment.

And if the only thing the chrooted user needs is SFTP, the accepted answer to this question describes an even simpler way.

Related Question