Cover image banner for restricting user to SCP and SFTP in chroot jail on Ubuntu, Manjaro, Arch Linux

A user can be defined as someone who has access to the system, albeit only to the resources for which permission has been granted. Fortunately, adding users on Linux is quite easy thanks to utilities such as useradd and adduser.

Nonetheless, creating a user using the useradd command without any extra arguments will grant that user access to the shell by default. And assuming there are no circumventions or firewall restrictions in place, this allows the user to login remotely if an SSH server is running on the system. Quite often, this behavior is undesirable.


On Monday, I had to provision an AWS Lightsail instance running Linux and create a user on the Linux machine. This would then be used by a customer to securely send their logs for us to inspect and troubleshoot.

Being a security enthusiast, it naturally occured to me that the user should

  1. only be allowed to upload and download content,
  2. inside a specific directory (and sub-directories) only,
  3. and not be able to access a shell (i.e disable SSH login for that user).

The rationale behind denying the user access to a shell is to prevent any form of escalation, potentially unauthorized operations, and a listing of the filesystem. Ultimately, all that we want is for the user to transfer files (read and write) within a certain directory.

After some digging, I found that the best approach is to assign a nologin shell and configure a chroot jail for the user.


Step 1: Create a Group for SFTP-Only Users

First, we create a group called sftp. You can use any group name that you want. Later on, we’ll configure SSH to impose certain restrictions to all the users of this group.

export GROUP_SFTP="sftp"
sudo groupadd $GROUP_SFTP

Step 2: Setup the User

We can now configure the user. Two variables, NEW_USER and CHROOT_BASE, are used to denote the username and the user’s personal directory respectively. In this example, the user we are adding is named alice.

export NEW_USER="alice"
export CHROOT_BASE="/home/$NEW_USER"

The useradd command is used to create the user. Please see below how to manage an existing user.

1. For a new user:

sudo useradd $NEW_USER \
-s /sbin/nologin


The no-login shell is /usr/sbin/nologin in Ubuntu and other Debian derivatives!

This automatically adds our user to the group we created above and sets /sbin/nologin as the user’s shell. Any user whose shell is /sbin/nologin is automatically denied access to a shell when a login is attempted via SSH.

It’s also important to set a password for the new user.

sudo passwd $NEW_USER

2. For an existing user:

If the user already exists, usermod should be used instead.

sudo usermod $NEW_USER \
-s /sbin/nologin


The no-login shell is /usr/sbin/nologin in Ubuntu and other Debian derivatives!

Step 3: Create a Chroot Jail

This step consists in creating the directory (chroot jail) where the user is confined to. We also need to set permission 755 on the chroot jail and give the root user exclusive ownership of the chroot jail.

sudo mkdir -p $CHROOT_BASE/uploads
sudo chmod 755 $CHROOT_BASE
sudo chown root:root $CHROOT_BASE

The uploads directory inside the chroot jail is where the user will have full read and write access. Therefore, we give ownership of this directory to the user.

sudo chown $NEW_USER $CHROOT_BASE/uploads

Step 4: Update SSH Configuration

Once the user has been restricted to a specific directory and denied access to a login shell, all that is left is to properly configure the SSH service by modifying some settings in the /etc/ssh/sshd_config file.

sudo nano /etc/ssh/sshd_config

In the /etc/ssh/sshd_config file, modify the line starting with Subsystem as follows:

Subsystem		sftp		internal-sftp

Next, we need to add this block at the end of the /etc/ssh/sshd_config file:

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

Below is a description of each directive:

Match Group sftp: This informs the SSH daemon (sshd) that the following rules should apply to all users belonging to the sftp group.

ChrootDirectory %h: Confine users to their HOME directory.

ForceCommand internal-sftp: Request the SSH daemon (sshd) to serve the server-side of the SFTP protocol to stdout and to expect client requests from stdin.

AllowTcpForwarding no: Disable TCP forwarding to improve security.

X11Forwarding no: Disallow X11 forwarding as it can be a source of vulnerabilities.

PasswordAuthentication yes: Allow users to login using password. Set to no if you want to use public-private key pairs instead.

Step 5: Restart SSH Service

Finally, the SSH service should be restarted to pick up the changes that were made to the sshd_config file.

sudo systemctl restart ssh

Transferring Files

There are 2 ways to connect to the SSH server: either through the sftp command or using scp. I have a penchant for scp as it is faster than sftp.

This is because unlike sftp, scp does not ACK (acknowledge) packets. However, one caveat of scp is that it does not allow interrupted file transfers to be resumed. With sftp, this is possible using the -a flag when downloading and using the reput command for uploads.

1. Using SCP (Secure Copy)

a) To upload a file using SCP:

scp ~/source.txt alice@

b) To download a file using SCP:

scp alice@ ~/Downloads

2. Using SFTP (SSH File Transfer Protocol)

To connect via SFTP:

sftp alice@
cd uploads

After a successful login, SFTP provides an interactive shell of its own. In order to accomplish anything with SFTP, specific commands are available.

a) Upload a file using SFTP:

put ~/source.txt

b) Resume uploading a file using SFTP:

reput ~/source.txt

c) Download a file using SFTP:

get uploaded.txt ~/Downloads

d) Resume downloading a file using SFTP:

get -a uploaded.txt ~/Downloads


This section of the manual page lists all the available interactive commands when using sftp.

# Footnotes