Linux sftp restrict user to specific directory | setup sftp chroot jail

In this article I will share step by step guide on how to configure sftp server in Linux with examples covering the below topics in detail:

  • Install sftp on Linux
  • Configure sftp chroot
  • Create sftp user/Create sftp group
  • sftp restrict user to specific directory
  • sftp chroot multiple directories

sftp is a file transfer program, similar to ftp, which performs all operations over an encrypted ssh transport. It may also use many features of ssh, such as public key authentication and compression.

 

Lab Environment

I have created two Virtual Machines with CentOS 8 on Oracle VirtualBox in Linux server. I will use these two Virtual Machines to configure and verify sftp restrict user to specific directory and sftp chroot multiple directories with examples. Most of the steps from this article should also work on other Linux distributions such as SuSE, Ubuntu etc, if you face any issues do let me know using the comment section from this page.

We will configure sftp chroot jail on server2 and use server1 to connect to server2 using sftp user deepak

 server1server2
Hostnameserver1.example.comserver2.example.com
IP Address10.10.10.1410.10.10.13
Setup SFTPas client
(which initiates sftp connection)
as server
(sftp server)
OSCentOS 8CentOS 8

 

Step 1: Install sftp on Linux

On most Linux distributions sftp should be installed by default. On RHEL/CentOS 7 and 8 Linux you can use yum or dnf to install sftp which is provided as part of openssh-clients rpm in RHEL/CentOS distro.

[root@server1 ~]# which sftp
/usr/bin/sftp

 

Step 2: Create SFTP User

I will create sftp user (deepak) for sftp restrict user to specific directory in Linux on server2.

To create sftp user "deepak", use below command.

[root@server2 ~]# useradd deepak

Check user details:

[root@server2 ~]# id deepak
uid=1003(deepak) gid=1003(deepak) groups=1003(deepak)

The user's home directory is owned by deepak with 700 permission so that no other user (other than root) can access this directory.

[root@server2 ~]# ls -ld /home/deepak/
drwx------ 2 deepak deepak 4096 Mar 30 18:48 /home/deepak/

Assign password to sftp user deepak:

[root@server2 ~]# passwd deepak
Changing password for user deepak.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

Change the shell of the sftp user to /bin/false instead of /bin/bash. Without a valid shell the sftp user will not be allowed to login.

[root@server2 ~]# usermod --shell /bin/false deepak

Verify the user deepak's properties

[root@server2 ~]# grep deepak /etc/passwd
deepak:x:1003:1003::/home/deepak:/bin/false

 

Step 3: Create SFTP Group (Optional)

In this article to demonstrate sftp restrict user to specific directory, I will use sftp user instead of sftp group. But if you have a requirement to implement group level sftp chroot jail then you can also create sftp group using below steps:

[root@server2 ~]# groupadd sftpusers   <-- Here group name is "sftpusers"

Add your user to this sftpusers group

[root@server2 ~]# usermod --gid sftpusers deepak  <-- Adding user "deepak" to group "sftpusers"

Now you can use this group "sftpusers" for sftp restrict user to specific directory.

 

Step 4: Configure SFTP chroot jail

To configure SFTP chroot jail we will modify /etc/ssh/sshd_config

[root@server2 ~]# vim /etc/ssh/sshd_config
<output_trimmed>
#Comment sftp-server SubSystem and use internal-sftp
#Subsystem       sftp    /usr/libexec/openssh/sftp-server
Subsystem       sftp    internal-sftp

#Add this section to match for user "deepak"
Match user deepak
        ChrootDirectory /opt/sftp-jails/deepak    <-- Our sftp chroot jail directory
        X11Forwarding no
        AllowTcpForwarding no
        PermitTunnel no
        AllowAgentForwarding no
        ForceCommand internal-sftp

Here,

Match			Introduces a conditional block.  If all of the criteria on the Match line are satisfied, the keywords on the following lines 
			override those set in the global section of the config file, until either another Match line or the end of the file. If a 
			keyword appears in multiple Match blocks that are satisfied, only the first instance of the keyword is applied.
		
ChrootDirectory		The ChrootDirectory must contain the necessary files and directories to support the user's session. For an interactive session 
			this requires at least a shell, typically sh, and basic /dev nodes such as null, zero, stdin, stdout, stderr, and tty devices. 
			For file transfer sessions using SFTP no additional configuration of the environment is necessary if the in-process sftp-server
			is used, though sessions which use logging may require /dev/log inside the chroot directory on some operating systems.
					
X11Forwarding		This allows/denies the X11 forwarding. If set to yes, it can expose X11 to attacks; so this option must be taken with care. 
			Defaults to no. We prevent the forwarding to X11: there is no need for it and it can expose the system.
					
AllowTcpForwarding	This allow/denies TCP forwarding and can take as argument yes, all, no, or local and remote. The first two options allow the
			forwarding, the third denies it, and the local allows local forwarding only; remote enables remote forwarding only. There is 
			no shell or TCP forwarding for our example.
					
ForceCommand		Specifying a command of internal-sftp will force the use of an in-process SFTP server that requires no support files when 
			used with ChrootDirectory.

AllowAgentForwarding	Defines whether ssh-agent forwarding is allowed or not. Defaults to yes to increase the security, and since it is not really 
			needed for an sftp account, so we are going to disable it.
						
PermitTunnel	        This allows/denies the tunnel device forwarding. It takes yes, point-to-point , Ethernet, or no as arguments. Yes enables both 
			point-to-point and Ethernet forwarding. Defaults to no, and we want to be sure it is disabled since we do not need a tunnel for 
			an sftp account.					

SubSystem		The command sftp-server implements the SFTP file transfer subsystem. 
			Alternately the name internal-sftp implements an in-process SFTP server. This may simplify configurations using ChrootDirectory 
			to force a different filesystem root on clients.

 

Why we use internal-sftp instead of sftp-server for ChrootDirectory?

Collected from: OpenSSH: Difference between internal-sftp and sftp-server

  • Both sftp-server and internal-sftp are part of OpenSSH. sftp-server is a standalone binary.
  • internal-sftp is just a configuration keyword that tells sshd to use SFTP server code built-into sshd, instead of running another process (typically the sftp-server).
  • sftp-server is now redundant and is kept for a backward compatibility.
  • The main advantage of internal-sftp is, that it requires no support files when used with ChrootDirectory directive.
  • Administrator may rely on a login shell configuration to prevent certain users from logging in.
  • Switching to the internal-sftp would bypass the restriction, as the login shell is no longer involved.
  • Using sftp-server binary (being a standalone process) you can use some hacks, like running the SFTP under sudo.
  • For SSH-1 (if anyone is still using it), Subsystem directive is not involved at all.
  • An SFTP client using SSH-1 tells the server explicitly, what binary the server should run. So legacy SSH-1 SFTP clients have sftp-server name hard-coded.

Next restart sshd service to activate sftp chroot jail configuration.

[root@server2 ~]# systemctl restart sshd

 

Step 5: SFTP restrict user to specific directory (with password authentication)

Step 5.1: Create sftp chroot jail directories

If you wish to sftp restrict user home directory then you can ignore these steps and only use /home/<user> as chroot jail. But to cover sftp restrict user to specific directory, we need a directory structure.

In this article we will implement sftp chroot jail on /opt/sftp-jails/<username>/exchange.

[root@server2 ~]# mkdir -p /opt/sftp-jails

 

Step 5.2: Assign permissions on chroot jail directories

Our sftp user deepak will login to /opt/sftp-jails/<username> using sftp

[root@server2 ~]# ls -ld /opt/sftp-jails/
drwxr-xr-x 3 root root 4096 Mar 30 18:29 /opt/sftp-jails/

User will have no write permission on /opt/sftp-jails/<username>. There is another directory "exchange" under /opt/sftp-jails/<username>/ where sftp user will perform write operation.

[root@server2 ~]# ls -ld /opt/sftp-jails/deepak/
drwxr-xr-x 3 root root 4096 Mar 30 18:29 /opt/sftp-jails/deepak/

As you see the user directory is owned by root with 755 permission to allow user deepak to login and sftp restrict user to specific directory /opt/sftp-jails/deepak

We will change user owner to deepak to allow him write in this sftp chroot jail directory

[root@server2 ~]# chown deepak:root /opt/sftp-jails/deepak/exchange

Also change the permission to 750 to restrict others from writing in this exchange directory

[root@server2 ~]# chmod 750 /opt/sftp-jails/deepak/exchange

Verify the permission:

[root@server2 ~]# ls -ld /opt/sftp-jails/deepak/exchange/
drwxr-x--- 2 deepak root 4096 Mar 30 18:29 /opt/sftp-jails/deepak/exchange/

Below is the tree structure of the directories we have created to configure sftp restrict user to specific directory

[root@server2 ~]# tree -paug /opt/
/opt/
└── [drwxr-xr-x root     root    ]  sftp-jails
    └── [drwxr-xr-x root     root    ]  deepak
        ├── [drwxr-x--- deepak   root    ]  exchange

 

Step 5.3: Verify SSH and SFTP connectivity and permissions

Since we have blocked ssh access for our sftp user deepak, from server1, first we try to do SSH to server2 using deepak user to make sure this configuration is working as expected.

[root@server1 ~]# ssh deepak@10.10.10.13
deepak@10.10.10.13's password:
This service allows sftp connections only.
Connection to 10.10.10.13 closed.

As expected we are getting "This service allows sftp connections only".

 

How to fix packet_write_wait: Connection to X.X.X.X port 22: Broken pipe?

It is possible that if your configuration has some issues, you will get "packet_write_wait: Connection to X.X.X.X port 22: Broken pipe" error instead of "This service allows sftp connections only."

# ssh deepak@10.10.10.13
deepak@10.10.10.13's password:
packet_write_wait: Connection to 10.10.10.13 port 22: Broken pipe

Now this error does not gives much detail of the underlying problem. So in such case we must check /var/log/messages on server node which you are trying to connect which for us is server2. We will use journalctl to analyse the error "packet_write_wait: Connection to X.X.X.X port 22: Broken pipe"

Using journalctl -f I found error "fatal: bad ownership or modes for chroot directory"

Mar 30 18:54:17 server2.example.com systemd[1]: Started User Manager for UID 1003.
Mar 30 18:54:17 server2.example.com sshd[10232]: pam_unix(sshd:session): session opened for user deepak by (uid=0)
Mar 30 18:54:17 server2.example.com sshd[10232]: fatal: bad ownership or modes for chroot directory "/opt/sftp-jails/deepak/exchange" [postauth]
Mar 30 18:54:17 server2.example.com sshd[10232]: pam_unix(sshd:session): session closed for user deepak

Now this error "fatal: bad ownership or modes for chroot directory" itself tells you that the permission on your chroot directory provided under /etc/ssh/sshd_config is incorrect.

From the man page of sshd_config for ChrootDirectory, All components of the pathname must be root-owned directories that are not writable by any other user or group

So you can validate the permission you have provided for your chroot directory to fix "fatal: bad ownership or modes for chroot directory" and re-attempt the ssh.

 

Next attempt SFTP communication from server1 to server2 using deepak user

[root@server1 ~]# sftp deepak@10.10.10.13
deepak@10.10.10.13's password:
Connected to deepak@10.10.10.13. <-- The sftp connection is successful
sftp> pwd <-- Check your present working directory
Remote working directory: /
sftp> ls -lah		<-- List the directories and files in current directory
drwxr-xr-x    ? 0        0            4.0K Mar 30  2020 .
drwxr-xr-x    ? 0        0            4.0K Mar 30  2020 ..
drwxr-x---    ? 1003     0            4.0K Mar 30  2020 exchange

sftp> mkdir test <-- Create a directory named "test"
Couldn't create directory: Permission denied <-- Since there is no write permission for deepak user
                                                 in his home folder, he gets permission denied

sftp> cd exchange/ <-- Navigate to exchange directory

sftp> mkdir deepak_dir <-- Create directory named deepak_dir

sftp> ls -l <-- List the directories and files in current directory
drwx------ 2 deepak deepak 4096 Mar 30 13:38 deepak_dir <-- Directory creation was successful

sftp> pwd <-- Present working directory
Remote working directory: /exchange

sftp> exit <-- To exit your session

 

Step 6: Setup passwordless sftp authorized_keys

In the above example we configured sftp restrict user to specific directory where user authenticates itself using password. You may face issues when you try to use sftp chroot jail in script to automate some tasks as every time sftp communication will prompt for user password.

In this example we will setup passwordless sftp authorized_keys between our servers to sftp restrict user to specific directory.

 

Step 6.1: Create sftp authorized_keys file

On server2 create sftp authorized_keys file which will store the public key content from server1. Here I have created a hidden folder .ssh inside which I will create authorized_keys file

[root@server2 ~]# cd /opt/sftp-jails/deepak

Create a hidden directory .ssh where we will store our sftp authorized_keys file

[root@server2 deepak]# mkdir .ssh

The .ssh directory must be owned by deepak user and must not be accessible by world

[root@server2 deepak]# chown deepak:root .ssh
[root@server2 deepak]# chmod 700 .ssh

[root@server2 deepak]# ls -al
total 16
drwxr-xr-x 4 root   root 4096 Mar 30 19:20 .
drwxr-xr-x 3 root   root 4096 Mar 30 18:29 ..
drwxr-x--- 3 deepak root 4096 Mar 30 19:08 exchange
drwx------ 2 deepak root 4096 Mar 30 19:20 .ssh

Create sftp authorized_keys file

[root@server2 deepak]# touch .ssh/authorized_keys

Change ownership and permission of this file

[root@server2 ~]# chown deepak:root .ssh/authorized_keys
[root@server2 ~]# chmod 600 .ssh/authorized_keys

Verify the permissions:

[root@server2 .ssh]# ls -al
total 12
drwxr-xr-x 2 deepak root 4096 Mar 30 19:20 .
drwxr-xr-x 4 root   root 4096 Mar 30 19:20 ..
-rw------- 1 deepak root  410 Mar 30 19:17 authorized_keys

 

Step 6.2: Generate SSH key pair to setup passwordless sftp

We are creating key pair using root user without using any password to sftp restrict user to specific directory. The private public key pair will be created under the home folder of root user inside /root/.ssh

[root@server1 ~]# ssh-keygen -t rsa -P ""
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:waurDGhB1PvXepoJKcTADzia1qDZilHvC1BV+UnZnic root@server1.example.com
The key's randomart image is:
+---[RSA 2048]----+
| .. .... o       |
|+  o  ..o .      |
|o*o .  ooo .     |
|+BBo    ooE .    |
|Bo.=o   S  o     |
|o=o. ..o .       |
|+.o..oo .        |
|.  +...oo.       |
|    +..+o        |
+----[SHA256]-----+

Copy the content of your public key id_rsa.pub to server2 and place it in /opt/sftp-jails/deepak/exchange/.ssh/authorized_keys which we created under Create sftp authorized_keys file. Below as you see I have appended my id_rsa.pub content to /opt/sftp-jails/deepak/exchange/.ssh/authorized_keys

[root@server1 ~]# cat .ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7qO/cXiw8UZtgGIoCiVWF24CW3hdIQ4w1hA6L+cHts1Sg6ecJRFXZJmrv23TSmD9Xrw4HHAxECJJKAk1zSWzBYykvqNM6xpvbg8pnIRSLyA06Erf+FdyfFjpDXorJ+eE309H9fJH4ke1aXBvF5LddA1N+1yDjuAllekjL/6kL7e58D4+E6gJa2wGs8OdQM7YFEgPbQJPBn03WQLHR3O59S+zURvrHSmUTVPJ7VWVdJ6nrcMlraOMc0yshTK4QWkmsBEa3+fhEBk0qFxJBhI0Fn775omaquhD3RzKyySXn9vgw3bb36k99KzD3F/w5hoK4sehDnEZs9lTYz8I8FUeV root@server1.example.com

[root@server2 ~]# cat /opt/sftp-jails/deepak/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7qO/cXiw8UZtgGIoCiVWF24CW3hdIQ4w1hA6L+cHts1Sg6ecJRFXZJmrv23TSmD9Xrw4HHAxECJJKAk1zSWzBYykvqNM6xpvbg8pnIRSLyA06Erf+FdyfFjpDXorJ+eE309H9fJH4ke1aXBvF5LddA1N+1yDjuAllekjL/6kL7e58D4+E6gJa2wGs8OdQM7YFEgPbQJPBn03WQLHR3O59S+zURvrHSmUTVPJ7VWVdJ6nrcMlraOMc0yshTK4QWkmsBEa3+fhEBk0qFxJBhI0Fn775omaquhD3RzKyySXn9vgw3bb36k99KzD3F/w5hoK4sehDnEZs9lTYz8I8FUeV root@server1.example.com

 

Step 6.3: Setup sftp chroot jail with authorized_keys

In your existing sftp chroot jail configuration of sshd_config, we will add one more line as highlighted with the location of sftp authorized_keys

[root@server2 ~]# cat /etc/ssh/sshd_config

Match user deepak
        ChrootDirectory /opt/sftp-jails/deepak/
        AuthorizedKeysFile /opt/sftp-jails/deepak/.ssh/authorized_keys
        X11Forwarding no
        AllowTcpForwarding no
        PermitTunnel no
        AllowAgentForwarding no
        ForceCommand internal-sftp

Restart sshd service to activate the sftp authorized_keys changes

[root@server2 ~]# systemctl restart sshd

Below is a tree structure of our sftp chroot jail directory with all the permissions:

[root@server2 ~]# tree -paug /opt/
/opt/
└── [drwxr-xr-x root     root    ]  sftp-jails
    └── [drwxr-xr-x root     root    ]  deepak
        ├── [drwxr-x--- deepak   root    ]  exchange
        │   └── [drwx------ deepak   deepak  ]  deepak_dir
        └── [drwxr-xr-x deepak   root    ]  .ssh
            └── [-rw------- deepak   root    ]  authorized_keys

5 directories, 1 file

 

Step 6.4: Verify SFTP connectivity and permissions

Perform sftp connection from server1 to server2.

[root@server1 ~]# sftp  deepak@10.10.10.13
Connected to deepak@10.10.10.13.  <-- There was no password prompt and the connection was established 
sftp> exit

So our passwordless sftp authorized_keys configuration is successful and is working as expected.

 

Step 7: Setup SSH client for passwordless sftp

By default when you do sftp, the tool looks under the home folder of the user for any available passphrase to perform passwordless sftp. In our case since the private key exists inside root's home folder the SFTP passwordless worked flawlessly.

But if you attempt to use any other user for sftp passwordless connection then it would fail. Below I try to do sftp using amit user on server1

[amit@server1 ~]$ ssh deepak@10.10.10.13
deepak@10.10.10.13's password:  <-- Prompting for password even when passwordless sftp is configured

As you see the sftp communication is prompting for password and passwordless sftp authorized_keys is not working.

To overcome this restriction for sftp restrict user to specific directory without password we have two solutions:

 

Solution 1: Perform passowrdless sftp with private key

You must define the private key you want to use for performing sftp communication to perform passwordless sftp. For example:

I will copy the private key I generated under a path which is accessible by user amit

I created a temporary directory /tmp/sftp_keys which will be accessible by all the users

[root@server1 ~]# mkdir /tmp/sftp_keys
[root@server1 ~]# chmod 755 /tmp/sftp_keys/

Copy the private key from to this location and make it readable by all users of server1

[root@server1 ~]# cp .ssh/id_rsa /tmp/sftp_keys/
[root@server1 ~]# chmod 644 /tmp/sftp_keys/id_rsa

Next attempt to perform passwordless sftp to server2

[root@server1 ~]# su - amit
[amit@server1 ~]$ sftp -i /tmp/sftp_keys/id_rsa deepak@10.10.10.13
Connected to deepak@10.10.10.13. <-- Passwordless sftp is working using sftp authorized_keys from sshd_config 
sftp> exit

 

Solution 2: Create ssh config file for individual user

We can also create a local ssh config file for individual user of the node to perform passwordless sftp to server2 using sftp authrorized_keys.
To achieve this we will create a config file under the home folder of amit user (/home/amit/.ssh), inside .ssh directory

You can check the permissions and ownership I have assigned for all the files and directories under amit's home folder:

[amit@server1 ~]$ tree -paug .
.
├── [-rw------- amit     amit    ]  .bash_history
├── [-rw-r--r-- amit     amit    ]  .bash_logout
├── [-rw-r--r-- amit     amit    ]  .bash_profile
├── [-rw-r--r-- amit     amit    ]  .bashrc
├── [drwx------ amit     amit    ]  .ssh
│   ├── [-rw------- amit     amit    ]  config
│   ├── [-rw------- amit     amit    ]  id_rsa
│   └── [-rw-r--r-- amit     amit    ]  known_hosts
└── [-rw------- amit     amit    ]  .viminfo

1 directory, 8 files

Below is the content of /home/amit/.ssh/config, which you can modify based on your requirement to perform passwordless sftp. I have copied the private key inside /home/amit/.ssh which we created earlier.

[amit@server1 ~]$ cat .ssh/config
Host server2
AddressFamily inet
ConnectionAttempts 10
ForwardAgent no
ForwardX11 no
ForwardX11Trusted no
GatewayPorts no
HostBasedAuthentication no
HostKeyAlias sftp-alias
HostName 10.10.10.13
IdentityFile ~/.ssh/id_rsa
PasswordAuthentication no
Protocol 2
Compression yes
ServerAliveCountMax 3
ServerAliveInterval 15
TCPKeepAlive no
User deepak

Next verify the passwordless sftp communication

[amit@server1 ~]$ sftp server2
Connected to server2. <-- Passwordless sftp is working using sftp authorized_keys from sshd_config 
sftp> exit

 

Step 8: SFTP chroot multiple directories

Similar to the sshd_config configuration file from sftp restrict user to specific directory, we will add more templates with match block for any number of users or groups to implement sftp chroot jail for multiple directories in Linux.

For example in the below configuration from /etc/ssh/sshd_config, we do sftp chroot multiple directories for different users and groups

# sftp chroot jail only user deepak to folder /opt/sftp-jails/deepak
Match User deepak
   ChrootDirectory /opt/sftp-jails/deepak
   AllowTCPForwarding no
   X11Forwarding no
   ForceCommand internal-sftp

# sftp chroot jail only user amit to folder /opt/sftp-jails/amit
Match User amit
   ChrootDirectory /opt/sftp-jails/amit
   AllowTCPForwarding no
   X11Forwarding no
   ForceCommand internal-sftp

# sftp chroot jail users that are in group sftpusers to the folder /opt/sftp-jails/sftpusers
Match Group sftpusers
   ChrootDirectory /opt/sftp-jails/sftpusers
   AllowTCPForwarding no
   X11Forwarding no
   ForceCommand internal-sftp

All other steps from this article would be the same to sftp chroot multiple directories, you just have to take care of user and group permission on individual sftp chroot jail directories

 

Lastly I hope the steps from the article to configure sftp and setup sftp restrict user to specific directory, sftp chroot multiple directories on RHEL/CentOS 7/8 Linux was helpful. So, let me know your suggestions and feedback using the comment section.

 

Related Searches: could not chdir to home directory, configure sftp centos, sftp user permissions, sctp chroot tutorial, how to configure sftp server in linux step by step, create linux user with limited access to one folder only, sftp server linux redhat
 

3 thoughts on “Linux sftp restrict user to specific directory | setup sftp chroot jail”

  1. You might mention SELinux issues in your article.
    dnf -y install policycoreutils-python-utils
    semanage fcontext -a -e /home/foo/.ssh /var/www/html/bar/.ssh
    restorecon -R -v /var/www/html/bar/.ssh

    Reply
    • Yes thank you for highlighting this, I don't use SELinux and somehow I forget to mention about this. I will try to add this info in my next articles.

      Reply

Leave a Comment

Please use shortcodes <pre class=comments>your code</pre> for syntax highlighting when adding code.