Table of Contents
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.
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
|Setup SFTP||as client
(which initiates sftp connection)
|OS||CentOS 8||CentOS 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
To create sftp user "
deepak", use below command. Here we use
-m to also create user's home directory.
[root@server2 ~]# useradd -m 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
[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
[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
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
internal-sftpare part of OpenSSH.
sftp-serveris a standalone binary.
internal-sftpis just a configuration keyword that tells sshd to use SFTP server code built-into sshd, instead of running another process (typically the
sftp-serveris now redundant and is kept for a backward compatibility.
- The main advantage of
internal-sftpis, that it requires no support files when used with
- Administrator may rely on a login shell configuration to prevent certain users from logging in.
- Switching to the
internal-sftpwould bypass the restriction, as the login shell is no longer involved.
sftp-serverbinary (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
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
[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/
It is mandatory to have the user and group owner set to root:root with 755 permission for the chroot directory provided in
/etc/ssh/sshd_config. If you set any other permission, then chroot jail will fail.
From the man page https://man.openbsd.org/sshd_config
ChrootDirectory Specifies the pathname of a directory to chroot(2) to after authentication. At session startup sshd(8) checks that all components of the pathname are root-owned directories which are not writable by any other user or group.
User will have no write permission on
/opt/sftp-jails/<username>. There is another directory "
/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
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
[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
server1, first we try to do SSH to
deepak user to make sure this configuration is working as expected.
[root@server1 ~]# ssh email@example.com firstname.lastname@example.org'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".
Step 5.4: Assign SFTP umask (Optional but Important)
Most of us miss the umask configuration while setting up SFTP and this can become a big issue later and hard to debug. By default SFTP follows the umask of the server node for any PUT operation. So for example you are trying to upload a file with permission 644 on the source client while the SFTP server has umask 027. Once you perform PUT operation to this SFTP server, the file permission will automatically become 640 due to umask 027.
So, umask will trim down any additional permission from the files uploaded to the SFTP server.
If you wish to provide a custom umask for SFTP PUT operation then modify
ForceCommand internal-sftp -u <umask>
For Example to use sftp umask of 022 we can add:
ForceCommand internal-sftp -u 0022
Restart sshd service to activate the changes.
Now any file uploaded will have atleast 644 permission.
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 email@example.com firstname.lastname@example.org'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 but this is seen mostly due to permission issues.
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"
journalctl -f I found error "
fatal: bad ownership or modes for chroot directory"
Mar 30 18:54:17 server2.example.com systemd: Started User Manager for UID 1003. Mar 30 18:54:17 server2.example.com sshd: pam_unix(sshd:session): session opened for user deepak by (uid=0) Mar 30 18:54:17 server2.example.com sshd: fatal: bad ownership or modes for chroot directory "/opt/sftp-jails/deepak/exchange" [postauth] Mar 30 18:54:17 server2.example.com sshd: 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
[root@server1 ~]# sftp email@example.com firstname.lastname@example.org's password: Connected to email@example.com. <-- 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
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
[root@server2 ~]# cd /opt/sftp-jails/deepak
Create a hidden directory
.ssh where we will store our sftp
[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
[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@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 firstname.lastname@example.org 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
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
[root@server1 ~]# cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7qO/cXiw8UZtgGIoCiVWF24CW3hdIQ4w1hA6L+cHts1Sg6ecJRFXZJmrv23TSmD9Xrw4HHAxECJJKAk1zSWzBYykvqNM6xpvbg8pnIRSLyA06Erf+FdyfFjpDXorJ+eE309H9fJH4ke1aXBvF5LddA1N+1yDjuAllekjL/6kL7e58D4+E6gJa2wGs8OdQM7YFEgPbQJPBn03WQLHR3O59S+zURvrHSmUTVPJ7VWVdJ6nrcMlraOMc0yshTK4QWkmsBEa3+fhEBk0qFxJBhI0Fn775omaquhD3RzKyySXn9vgw3bb36k99KzD3F/w5hoK4sehDnEZs9lTYz8I8FUeV email@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 firstname.lastname@example.org
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
[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
[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
[root@server1 ~]# sftp email@example.com Connected to firstname.lastname@example.org. <-- 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
[amit@server1 ~]$ ssh email@example.com firstname.lastname@example.org'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
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
[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
[root@server1 ~]# su - amit [amit@server1 ~]$ sftp -i /tmp/sftp_keys/id_rsa email@example.com Connected to firstname.lastname@example.org. <-- 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
To achieve this we will create a config file under the home folder of
amit user (
/home/amit/.ssh), inside .
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