Jump to content

# SFTP chroot

2 languages

- 日本語
- 中文（简体）

From ArchWiki

Related articles

- SSHFS

OpenSSH 4.9+ includes a built-in chroot for SFTP, but requires a few tweaks to the normal install.

## Installation

Install and configure OpenSSH. Once running, make sure `sftp-server` has been set correctly:

    /etc/ssh/sshd_config

    Subsystem sftp /usr/lib/ssh/sftp-server

Access files with sftp or SSHFS. Many standard FTP clients should work as well.

## Configuration

### Setup the filesystem

Note:

- Readers may select a file access scheme on their own. For example, optionally create a subdirectory for an incoming (writable) space and/or a read-only space. This need not be done directly under `/srv/ssh/jail` - it can be accomplished on the live partition which will be mounted via a bind mount as well.
- It is also possible chrooting into `/home` directory thus skipping the usage of bind, however the desired user home directory should be owned by root:

    # chown root:root /home/<username>
    # chmod 0755 /home/<username>

Bind mount the live filesystem to be shared to this directory. In this example, `/mnt/data/share` is to be used, owned by user `root` and has octal permissions of `755`:

    # chown root:root /mnt/data/share
    # chmod 755 /mnt/data/share
    # mkdir -p /srv/ssh/jail
    # mount -o bind /mnt/data/share /srv/ssh/jail

Add entries to fstab to make the bind mount survive on a reboot:

    /etc/fstab

    /mnt/data/share /srv/ssh/jail  none   bind   0   0

### Create an unprivileged user

Note: You do not need to create a group, it is possible to use `Match User` instead of `Match Group`.

Create the `sftponly` user group:

    # groupadd sftponly

Create a user who is a member of the sftponly group and has shell login access denied:

    # useradd -G sftponly -s /usr/bin/nologin -d /srv/ssh/jail username

Note: SSH authentication by password (not recommended) only works if users have access to a login shell (i.e. `-s /usr/bin/nologin` not set).

Set a (complex) password to prevent `account is locked` error (may appear even with key authentication):

    # passwd username

### Configure OpenSSH

Note: You may want to use `Match User` instead of `Match Group` as been given in the previous step.

    /etc/ssh/sshd_config

    Subsystem sftp /usr/lib/ssh/sftp-server

    Match Group sftponly
      ChrootDirectory %h
      ForceCommand internal-sftp
      AllowTcpForwarding no
      X11Forwarding no
      PasswordAuthentication no

Restart `sshd.service` to confirm the changes.

#### Fixing path for authorized_keys

Tip: Use the debug mode of OpenSSH on the client and server in case of `(pre)auth` error(s).

With the standard path of AuthorizedKeysFile, the SSH keys authentication will fail for chrooted-users. To fix this, append a root-owned directory on AuthorizedKeysFile to `/etc/openssh/sshd_config` e.g. `/etc/ssh/authorized_keys`, as example:

    /etc/ssh/sshd_config

    AuthorizedKeysFile /etc/ssh/authorized_keys/%u .ssh/authorized_keys
    PermitRootLogin no
    PasswordAuthentication no
    PermitEmptyPasswords no
    Subsystem sftp /usr/lib/ssh/sftp-server

Create authorized_keys folder, generate a SSH-key on the client, copy the contents of the key to `/etc/ssh/authorized_keys` (or any other preferred method) of the server and set correct permissions:

    # mkdir /etc/ssh/authorized_keys
    # chown root:root /etc/ssh/authorized_keys
    # chmod 755 /etc/ssh/authorized_keys
    # echo 'ssh-rsa <key> <username@host>' >> /etc/ssh/authorized_keys/username
    # chmod 644 /etc/ssh/authorized_keys/username

Restart `sshd.service`.

## Tips and tricks

### Write permissions

The bind path needs to be fully owned by `root`, however files and/or subdirectories do not have to be. In the following example the user www-demo uses `/srv/ssh/www/demo` as the jail-directory:

    # mkdir /srv/ssh/www/demo/public_html
    # chown www-demo:sftponly /srv/ssh/www/demo/public_html
    # chmod 755 /srv/ssh/www/demo/public_html

The user should now be able to create files/subdirectories inside this directory. See File permissions and attributes for more information.

### Allow upload only

To allow only uploading files via sftp and deny downloading files, change the `ForceCommand internal-sftp` line:

    /etc/ssh/sshd_config

    Match Group sftponly
      ForceCommand internal-sftp -u 0666 -p realpath,open,write,close,lstat

### Logging

The user will not be able to access `/dev/log`. This can be seen by running `strace` on the process once the user connects and attempts to download a file.

#### Create sub directory

Create the sub-directory `dev` in the `ChrootDirectory`, for example:

    # mkdir /usr/local/chroot/user/dev
    # chmod 755 /usr/local/chroot/user/dev

Now you should create socket at `/usr/local/chroot/user/dev/log` which will be used by openssh. You may directly bind this socket to `/dev/log` (or `/run/systemd/journal/dev-log` in case you are using journald) or create using `syslog-ng`/`rsyslog`.

#### Bind to journald

    # touch /usr/local/chroot/user/dev/log
    # mount --bind /run/systemd/journal/dev-log /usr/local/chroot/user/dev/log

To make it permanent, add an entry to fstab:

    /etc/fstab

    /run/systemd/journal/dev-log /usr/local/chroot/user/dev/log none bind,nofail,x-systemd.requires=sshd.service

#### Syslog-ng configuration

Add to `/etc/syslog-ng/syslog-ng.conf` a new source for the log and add the configuration, for example change the section:

    source src {
      unix-dgram("/dev/log");
      internal();
      file("/proc/kmsg");
    };

to:

    source src {
      unix-dgram("/dev/log");
      internal();
      file("/proc/kmsg");
      unix-dgram("/usr/local/chroot/theuser/dev/log");
    };

and append:

    #sftp configuration
    destination sftp { file("/var/log/sftp.log"); };
    filter f_sftp { program("internal-sftp"); };
    log { source(src); filter(f_sftp); destination(sftp); };

(Optional) If you would like to similarly log SSH messages to its own file:

    #sshd configuration
    destination ssh { file("/var/log/ssh.log"); };
    filter f_ssh { program("sshd"); };
    log { source(src); filter(f_ssh); destination(ssh); };

(From Syslog-ng#Move log to another file)

#### OpenSSH configuration

Edit `/etc/ssh/sshd_config` to replace all instances of `internal-sftp` with `internal-sftp -f AUTH -l VERBOSE`.

#### Restart service

Restart service `syslog-ng` and `sshd`.

`/usr/local/chroot/theuser/dev/log` should now exist.

## Alternatives to SFTP

### Secure copy protocol (SCP)

Installing openssh provides the scp command to transfer files. SCP may be faster than using SFTP [1].

Install rsshAUR or scponly as alternative shell solutions.

#### Scponly

install scponly.

For existing users, simply set the user's shell to scponly:

    # usermod -s /usr/bin/scponly username

See the Scponly Wiki for more details.

#### Adding a chroot jail

The package comes with a script to create a chroot. To use it, run:

    # /usr/share/doc/scponly/setup_chroot.sh

- Provide answers.
- Check that `/path/to/chroot` has `root:root` owner and `r-x` for others.
- Change the shell for selected user to `/usr/bin/scponlyc`.
- sftp-server may require some libnss modules such as libnss_files. Copy them to chroot's `/lib` path.

## See also

- https://www.minstrel.org.uk/papers/sftp/
- sshd_config(5)

Categories:

- File Transfer Protocol
- Secure Shell