Linux · Server

Implement a SFTP Service for Ubuntu/Debian with a chroot’ed, isolated file directory.

In this entry, I will explain how to install and setup an SFTP service in an Ubuntu or Debian Linux server. SFTP (Secure File Transfer Protocol) is an extension of the SSH (Secure SHell protocol) which is for secure remote access into systems.

Despite its name, SFTP is not an extension to the 1985 FTP (File Transfer Protocol – RFC 959) which was in frequent use until the late 1990s to share files online. FTP usage has fallen out of favour for Internet use in the 2000’s due to the protocol’s inherent insecurities in addition to competition from a newer protocol such as the bandwidth-friendly BitTorrent protocol. The most glaring vulnerability with FTP is that it requires log-in usernames and passwords communicated between the client and the server using unencrypted plain-text.

Fortunately, in recent years many of the more popular FTP clients have implemented complete support for SFTP making the end-user transition from insecure to secure transfers seamlessly. Older, now redundant stop gaps for secure FTP such as FTPS/FTP-SSL which is now referred to as FTP with TLS (RFC 4217) were confusing to use and difficult to set up correctly.

It is now at the point where people probably would not know the difference between browsing a secure SFTP site to browsing an open, insecure FTP site. Personally, my favourite FTP/SFTP client is the multi-platform, open-sourced FileZilla but there are many others such as SmartFTP, WinSCP or FireFTP (for Firefox).

Okay to the task at hand we will do everything in CLI (command line interface) aka shell mode.

First, make sure your repository is up to date.

sudo apt-get update

Now install OpenSSH server software.

sudo apt-get install openssh-server

We then create a user group for SFTP access; I will be calling it sftponly. For security, I think it is best practice not to allow accounts with SFTP access additional admission to the server using secure shell (SSH) remote login.

sudo groupadd sftponly

Run the following to display your new group, shown as the last entry.

cat /etc/group

Cat allows you to quickly display a text file while /etc/group is the file that defines the groups on the server. You should see something like this.

Output of cat /etc/group.

Each line is an individual group, and you can see the name, the password which is set to x which means none, the numeric group id and users who are associated with the group. For sftponly there are currently no assigned users.

Take note of the group id, in this screenshot; it is the value 1001.

We now add a new user that we will use exclusively for SFTP access.

sudo useradd [user name] -d / -g [sftponly group id] -M -N -o -u [sftponly group id]
sudo passwd [user name]

The arguments we used.

  • -d is the user home directory which needs to be set to / (root).
  • -g is the user group id to assign which in our example needs to be assigned to sftponly.
  • -M stops the useradd command creating a home directory.
  • -N useradd by default creates a group with the same name as the new user, this disables that behaviour.
  • -u is the user id, which in our case needs to be the same id value as sftponly.
  • -o allows duplicate, non-unique user ids.
  • The passwd command sets an encrypted user password.

Add a user of your choice; I will use ben_example. To display your users.

cat /etc/passwd
Output cat /etc/passwd.

We now back up and edit the SSH Daemon configuration file.

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano +76 /etc/ssh/sshd_config

The line

Subsystem sftp /usr/lib/openssh/sftp-server

Needs to be replaced with

Subsystem sftp internal-sftp

Now go to the end of the document, the key combination Alt / should take you there or you could just use the Page Down key. After UsePAM Yes add the following lines to configure our sftponly group permissions and settings. The ChrootDirectory setting will confine all sftponly users to this directory. Otherwise, sftponly will have access to your server root which you do not want. /var/www is often the default Debian/Ubuntu location for web servers to place their assets such as HTML, CSS files and images. Though you can use a different directory for ChrootDirectory such as /var/sftp.

Match group sftponly
ChrootDirectory /var/www
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp

Once finished use the key combination Ctrl O to save and then Ctrl X exit.

Output of sudo nano /etc/ssh/sshd_config.

Now make sure the directory you assigned to ChrootDirectory exists and if it does not then create it. Also, the directory group and owner need to be root which it should by default if you use the command.

sudo mkdir /var/www
Output of SFTP root directory.
A reminder: the directory and its parent directories you assign to ChrootDirectory MUST be owned by root and assigned the group root. Otherwise, SFTP clients will not be able to upload or modify files and directories.

Now for later testing create the first of 3 directories within your ChrootDirectory.

cd /var/www
sudo mkdir test_readonly
sudo chmod 755 test_readonly

If you do not understand the chmod command shown? We will quickly go through it, but it is beyond the scope of this article. Change mode (chmod) parameter 755 is a permission code in octal notation. You break it up into three parts, 7/5/5.

  1. 1st part is root permission for this directory.
  2. 2nd is group permission.
  3. 3rd is everyone else.

The value of the individual part grants permissions for its user. A value of 0 grants no permission, a value of 1 grants execute permissions, 2 grants write and 4 grants read. These values can be summed to create multiple permissions. So 1 (execute) + 2 (write) + 4 (read) equals 7 which grants execute, write and read aka total access. chmod 755 test_readonly means the root has total access. While users associated with the directory’s group and everyone else only have execute and read access, 4 (read) + 1 (execute) = 5. More can be read about Linux permissions here

sudo mkdir test_readwrite
sudo chown root:sftponly test_readwrite
sudo chmod 775 test_readwrite

The above commands creates a test_readwrite directory whose owner is root and group is sftponly. Both root and sftponly members have full access permissions within test_readwrite, which allows the creation and deletion of files and sub-directories.

To remove browsing access to a directory you remove the read permission as shown below. 1 (execute) + 2 (write) = 3, execute and write access but critically no read which leaves the other two permissions redundant.

sudo mkdir test_noaccess
sudo chmod 733 test_noaccess

Restart the SSH server.

sudo /etc/init.d/ssh restart

If you don’t know your server IP address.

ip -o -f inet addr
Discover your server’s IP address.

lo is your local host, eth0 is your Ethernet cable connected address, wlan0 is probably wireless.

Connect to your SSH server using an SFTP client such as FileZilla. Make sure you use the correct IP address and port number which by default is 22.

Filezilla site manager connecting to my example SFTP server.

You should be connected to the directory assigned to ChRootDirectory (var/www) in the  SSHD configuration, and that doubles as the SFTP client root folder. Also listed should be the test_readonly, test_readwrite and test_noaccess directories that we created earlier. Play or navigate around; hopefully, you can upload files and create/delete directories within test_readwrite. While test_noaccess should be displayed but limited to browsing or download access.

Browsing your SFTP root in Filezilla.

In the screen capture below we have Filezilla’s message log with 3 sections highlighted. The orange section shows my failed attempt at accessing test_noaccess. The purple is the successful attempt at accessing test_readonly, but a failure in creating a New directory sub-folder within. While the green section shows access into test_readwrite, as well as being able to create a New directory sub-folder and its subsequent removal.

Colour-coded message log from browsing our SFTP service in FileZilla.

Congratulations you now have a working example of a SFTP service running on your server. A number of these instructions are from the blog post SFTP on Ubuntu and Debian in 9 easy steps and the reader comments.

93 thoughts on “Implement a SFTP Service for Ubuntu/Debian with a chroot’ed, isolated file directory.

  1. Followed everything on this tutorial almost exactly but I get this in filezilla

    Command: open “[email protected]” 22
    Command: Pass: ******
    Error: Network error: Software caused connection abort
    Error: Could not connect to server
    Status: Waiting to retry…
    Status: Connecting to x.x.x.x…
    Response: fzSftp started
    Command: open “[email protected]” 22
    Command: Pass: ******
    Error: Network error: Software caused connection abort
    Error: Could not connect to server

    I have no idea what this could be other than a permissions issue. I am using ubuntu server 12.04

    what command can i use to see the users who have permissions to /var/www?
    If I have only done a: chmod 770 /var/www
    could that be my issue? Do I have to create more folders first?

    plz halp? 😐

    1. Sorry for the late response, I have been away from the computer for the past couple of weeks.

      If your filezilla can not connect to your server it seems it believes there is a network connection error between your computer and the server. Is your server on a local network or on a remote network? Can you connect to it using other SSH tools like PSFTP that is included in Putty?

  2. How do you remove said user? I created this user and then realized I wanted a different name so I just created another account however this old one is still kicking around… When I try to remove account via sudo userdel it says he is already logged on … I goto pkill -KILL -u and it continues to next line as if it killed it… Then when I try to re userdel it says he is still logged on… Any help or suggestions on what I am missing would be VERY helpfull!!!

    1. Did you stop the SSH service and then try to delete the user account?

      sudo service ssh stop or sudo /etc/init.d/ssh stop

      Otherwise if you can not do that because you’re accessing the machine remotely try

      snice -u [user name]

      To see if the user is connected to your machine and if so run the following to disconnect it.

      skill -KILL -u [user name]

  3. This is a great post!

    I wanted to add that if you get permission denied errors trying to use a password instead of an SSH key, double check this config option in sshd_config:

    PasswordAuthentication no

    I also uncommented this one

    AuthorizedKeysFile %h/.ssh/authorized_keys

    Set it to yes, save it, restart ssh and you should be good.

  4. Thank you. This worked very well. I have a small query. Instead of /var/www I want to use /home/username. This is because I have a separate home partition and is much bigger in size than / partion. I tried to change in sss_config file, but did not had any success.

  5. post was very usefull. But i did as you posted above but at the client side clent console shows all folders in ” / ” folder.what i have o now to slove this

    1. I suggest trying again and make sure you follow each instruction. If you are serving root “/” as the base FTP directory there is something wrong with your set-up. You are not chroot to a sub-directory.

      1. thank you very much i have completed sftp configuration.but i have small doubt can i integrate ads users to ubuntu to login sftp server

      2. Hi Sir,

        I have a requirement for an HTTPS file transfer mechanism and am currently looking for software to do this function. I want the software to be ideally meet the following requirements:

        – free/open source
        – run as a service on either windows or Linux (preferably the latter)
        – allow people to send files to a other people
        – must be secure and use HTTPS through a standard web browser
        – be very simple for clients to use (somebody should be able to pretty much go to the web page, select the recipient, choose a file and click upload)

        The current mechanism accepts the file, and then emails the recipient with the https link to download it.

        Surely somebody must have had this requirement before for simple quick document exchanges from outside the company to inside or vice versa that don’t use fully blown sftp servers or the like which requires both sides IT departments getting involved to set up both ends.

        Does anyone have any recommendations for server software on either windows or Linux which can do this?

  6. Excellent post. Clear, concise, very well executed. Helped and educated me on setting up SFTP access on my Ubuntu server install. Thanks so much!

  7. Sorry. TABs not working in the post. Here are the no-tabs version 🙂 of the comparison

    using /home/not_working:____________________________________ using /opt/working:
    debug1: Authentication succeeded (password)____________________Authentication succeeded (password).
    debug1: channel 0: new [client-session]_________________________channel 0: new [client-session]
    debug1: Entering interactive session____________________________Entering interactive session.
    debug1: channel 0: free: client-session, nchannels 1_______________Sending environment.
    debug1: fd 0 clearing O_NONBLOCK___________________________Sending env LANG = es_ES.UTF-8
    Read from remote host localhost: Connection reset by peer__________sftp>
    debug1: Transferred: stdin 0, stdout 0, stderr 59 bytes in 0.0 seconds__sftp>
    debug1: Bytes per second: stdin 0.0, stdout 0.0, stderr 39414.0_______sftp>
    debug1: Exit status -1________________________________________sftp>
    Couldn’t read packet: Connection reset by peer____________________sftp>

    1. A quick read of the manual page seems to reveal an answer.
      or type man sshd_config

      Specifies a path to chroot(2) to after authentication. This
      path, and all its components, must be root-owned directories that
      are not writable by any other user or group

      The path may contain the following tokens that are expanded at
      runtime once the connecting user has been authenticated: %% is
      replaced by a literal ‘%’, %h is replaced by the home directory
      of the user being authenticated, and %u is replaced by the user-
      name of that user.

      The ChrootDirectory must contain the necessary files and directo-
      ries to support the users’ session. For an interactive session
      this requires at least a shell, typically sh(1), and basic /dev
      nodes such as null(4), zero(4), stdin(4), stdout(4), stderr(4),
      arandom(4) and tty(4) devices. For file transfer sessions using
      “sftp”, no additional configuration of the environment is neces-
      sary if the in-process sftp server is used (see “internal-sftp”
      will force the use of an in-process sftp server that requires no
      support files when used with ChrootDirectory. Subsystem for

      The default is not to chroot(2).

  8. I solved what I need, but I’ve curiosity about this.
    The problem appears to be with subdirectories of /home only. I created a subdirectory of /opt and everything worked fine. Here are the comparison of the debug lines.

    using /home/not_working: using /opt/working:
    debug1: Authentication succeeded (password). Authentication succeeded (password).
    debug1: channel 0: new [client-session] channel 0: new [client-session]
    debug1: Entering interactive session. Entering interactive session.
    debug1: channel 0: free: client-session, nchannels 1 Sending environment.
    debug1: fd 0 clearing O_NONBLOCK Sending env LANG = es_ES.UTF-8
    Read from remote host localhost: Connection reset by peer sftp>
    debug1: Transferred: stdin 0, stdout 0, stderr 59 bytes in 0.0 seconds sftp>
    debug1: Bytes per second: stdin 0.0, stdout 0.0, stderr 39414.0 sftp>
    debug1: Exit status -1 sftp>
    Couldn’t read packet: Connection reset by peer sftp>

  9. Thanks for answering.
    I’m sure that those are the lines that I already have in my sshd_config file. And so far so good. At this point everything works like a charm.
    The trouble starts when trying to use a directory other than /var/www. In my case /home/[another user]/jail, because I need that [another user] (member of sftponly group) make the administration of /home/[another user]/jail
    I followed every step of your tutorial, but I get that “… Connection reset by peer” error

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s