Configuring SFTP for the document root

How To Configure SFTP for a Web Server Document Root

Last updated on | 54 replies

SFTP is the favourite these days because of its robust security model and easier setup than traditional FTP and FTPS. In this guide we will configure SFTP to allow users to upload to the web server document root.

Introduction

This guide assumes your web document root is the default for Apache and Nginx in /var/www/html. If you followed one of our previous guides on setting up virtual hosts, your document root may be located in /var/www/example.com/public_html. Just make sure you have the correct document root and update commands in this guide to match.

This guide was tested on Ubuntu Server 20.04, 18.04 and 16.04, though it should also work with other Debian-based distributions without issue. If you are using CentOS, just substitute www-data in this guide for apache or nginx. Any issues, please let me know in the comments.

I have provided two different methods in this guide for setting up SFTP access to your document root:

  • Method One is a simple setup where you just add your SFTP user to the www-data group.
  • Method Two is far more secure and recommend if you want to limit where www-data has write access. I have included a special section for WordPress users and best security practises. See step 5.5: WordPress and www-data.

Regardless of the method you choose, Step 1, 2 and 3 below are the same.

This article also includes a section for WordPress users and best security practices.

1. Install SSH

SFTP is built upon the SSH transport layer and should be installed on most Linux server distributions by default. If it isn’t , you can install with:

sudo apt install ssh

2. Configure SSH

Change the Subsystem to internal-sftp in sshd_config.

sudo nano /etc/ssh/sshd_config

Scroll to the bottom of the file and comment out the line Subsystem sftp by adding # before it and then add Subsystem sftp internal-sftp below it.

/etc/ssh/sshd_config
#Subsystem sftp /usr/lib/openssh/sftp-server 
Subsystem sftp internal-sftp

This tells sshd to use SFTP server code built into sshd instead of running sftp-server, which is now redundant and only kept for a backward compatibility.

Restart the sshd service for changes to take affect.

sudo service sshd restart

3. Create SFTP User

It’s not recommended that you use the root account or any account with sudo privileges to upload files to the web server document root. For this reason, you should create a new user that only has SFTP access to the document root.

In this guide, we are calling the SFTP user webdev – you can call this whatever you want. If you plan to give SFTP access to multiple users across different document roots, consider a naming scheme like username_domain_com. For example john_devanswers_co. This will make it easier to keep track of all your SFTP users.

For the purposes of this guide, we will name the SFTP user webdev.

sudo adduser webdev

Generate a password and press enter to accept all defaults.

Two SFTP Configuration Methods

I am providing two different methods in this guide because there are some people who just want a quick and easy method to access the document root with SFTP, and others who want a more advanced security setup (which I use). It might be worth reading through both methods to see which one suits your needs.

  • Method One: Quick Setup
    • You just want a quick and simple method to give one or multiple SFTP users access to the document root by adding them to the www-data group.
    • You already have a live, busy site running on the document root and don’t want to risk accidentally taking it down by setting restrictive permissions in Method Two.
    • You need to install a CMS from scratch such as WordPress before setting up more restrictive permissions in Method Two.
  • Method Two: Better Security and SFTP User Management
    • You want the best security possible for your document root.
    • You already have your CMS such as WordPress installed and running, and now want to lock it down.
    • You want to restrict where www-data can write to.
    • You have multiple developers that need write access to multiple document roots hosted on your server.

4. Method One: Quick Setup

Using this method with the least amount of configuration, we will create a Match User directive in the SSH config and add your SFTP user to the www-data group.

4.1. Add Match User Directive in SSH Config

Restrict the user webdev to the document root and also disable their SSH access – we only want them to be able to log in over SFTP. We can do this by adding a Match User directive in the SSH config file.

Begin by opening sshd_config.

sudo nano /etc/ssh/sshd_config

Scroll down to the bottom of the SSH config file and add your new Match User directive.

Make sure that ChrootDirectory is the directory above your document root. For example, if your document root is /var/www/html/, then the ChrootDirectory is /var/www/.

If you followed one of our previous guides on hosting multiple domains on Apache or Nginx, your document root may be located in /var/www/mydomain.com/public_html, in that case, your ChrootDirectory would be /var/www/mydomain.com/.

Note you can add multiple users here separated by a comma, e.g. Match User webdev, webdev2, webdev3

Note: ForceCommand internal-sftp will force SFTP access only and not allow this SFTP user to log in via SSH.

/etc/ssh/sshd_config
Match User webdev 
        ChrootDirectory /var/www/
        ForceCommand internal-sftp 
        X11Forwarding no 
        AllowTcpForwarding no 
        PasswordAuthentication yes

Save and exit (press CTRL + X, press Y and then press ENTER)

Test SSH config before restarting.

sudo sshd -t

If no errors, restart the sshd service for changes to take affect.

sudo service sshd restart

4.2. Add SFTP User to www-data

Add your SFTP user webdev to the www-data group.

sudo usermod -a -G www-data webdev

Note: Linux groups do not take affect until the user logs out and in again. If you are already logged in as this user in your FTP client, close the program completely and then log in again.

4.3. Set Directory Permissions

SFTP is very strict when it comes to chroot directory permissions and if they are not set correctly, you will not be able to log in, so please follow these instructions carefully.

The chroot is usually the directory above your document root. For example, by default /var/www/ is the chroot and /var/www/html is the document root. Another example, if your document root is one directory deeper such as /var/www/domain.com/public_html, then your chroot is /var/www/domain.com.

  • The chroot directory *and all of its parents* must not have group or world write capabilities, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both /var/ and /var/www/ are set to 755. (not 775, which gives group write permissions).
  • The chroot directory *and all of its parents* must be owned by root, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both /var/ and /var/www/ are both owned by root.
  • If your chroot directory is not /var/www but, for example, /var/www/domain.com, then you will need to apply these permissions and root ownership to that folder as well and all of its parents!

Firstly, let’s check permissions and ownership for /var/ – they should be 755 and root by default.

sudo ls -ld /var/

Output:

drwxr-xr-x 14 root root 4096 Jul 30 02:24 /var/

If they do not match above, set permissions and ownership below.

sudo chmod 755 /var/
sudo chown root:root /var/

Now let’s apply the same permissions and ownership for your chroot. Assuming that your chroot is /var/www/, ensure that the directory is set to 755.

sudo chmod 755 /var/www/

Ensure your chroot directory is owned by root.

sudo chown root:root /var/www/

To check permissions for this directory:

sudo ls -ld /var/www/

Output:

drwxr-xr-x 14 root root 4096 Jun  3 14:28 /var/www/

Make sure the document root is set to 775, which will allow groups write to this directory.

sudo chmod 775 /var/www/html

Make sure that your document root and all contents are owned by www-data.

sudo chown -R www-data:www-data /var/www/html*

Change all directories in the document root to 775. This will allow both the owner (www-data) and its group (which SFTP users belong to) to read, write and execute folders.

sudo find /var/www/html/ -type d -exec chmod 775 {} \;

Change all files in the document root to 664, this will allow both the owner and the group to read, write and execute files.

sudo find /var/www/html/ -type f -exec chmod 664 {} \;

Make sure that any new files or folders created by SFTP users inherit the www-data group.

sudo find /var/www/html -type d -exec chmod g+s {} \;

Now log into SFTP with you preferred FTP client and make sure you can create, edit and delete files and folders.

If you are not able to log in, check the auth log for the last 50 entries. Also try closing your FTP client and opening it again.

sudo tail -n 50 /var/log/auth.log

4.4. Adding More SFTP Users

If you need to provide other SFTP users write access to the document root, simply add their usernames separated by a comma, e.g. Match User webdev, webdev2, webdev3 in sshd_config (step 4.1) and then add the SFTP user to the www-data group (Step 4.2) .

5. Method Two: Better Security and SFTP User Management

In this method we will set up more restrictive permissions for your document root and use Linux user groups to manage SFTP users. This is the method I personally use for managing multiple virtual hosts, WordPress installs, and SFTP users on the one server.

Even if you are only hosting one website on your server, I strongly recommend this setup if you want the best security for your website’s document root.

This method removes www-data write access to the entire document root. Consider a scenario where a PHP script or WordPress plugin is hacked, the attacker could gain write access to your entire document root. To mitigate this, we need to only give www-data (and thus WordPress) write access to the directories where it only needs it to function properly.

If you haven’t installed your CMS yet, you should first carry out Method One, upload and install your CMS, then follow Method Two to lock down the CMS.

5.1. Create a New Linux User Group

With this method we will create a Linux User Group with the necessary access to the document root and then add our SFTP users to that group.

If you are hosting multiple websites on the one server with Apache or Nginx, you should name these groups so they correspond to your domain name. For example, sftp_example1_com and sftp_example2_org. This will make it a lot easier to keep track of your groups should they grow over time.

However, for the purposes of this guide, we will just call the group sftp_users and restrict this group to the default document root /var/www/html.

Add new group:

sudo groupadd sftp_users

5.2. Add SFTP User to Group

Add your SFTP user webdev (or whatever you called it) to the sftp_users group.

sudo usermod -a -G sftp_users webdev

Note: Groups do not take affect until the user logs out and in again. If you are already logged in as this user in your FTP client, close the program completely and then log in again.

5.3. Add Match Group Directive in SSH Config

In Method One we used the Match User directive, but by using the Match Group directive, you can manage multiple users and document roots far more effectively.

This allows you to restrict an entire group to a particular document root, and then you just add your SFTP users to that group with no additional configuration. This can save you a lot of time.

Begin by opening sshd_config

sudo nano /etc/ssh/sshd_config

Scroll down to the bottom of the SSH config file and add your new Match Group directive.

Make sure that ChrootDirectory is the directory above your document root. For example, if your document root is /var/www/html/, then the ChrootDirectory is /var/www/.

If you followed one of our previous guides on setting up multiple domains, your document root may be located in /var/www/mydomain.com/public_html, in that case, your ChrootDirectory would be /var/www/mydomain.com/.

Note: ForceCommand internal-sftp will force SFTP access only and not allow this SFTP user to log in via SSH.

/etc/ssh/sshd_config
Match Group sftp_users 
        ChrootDirectory /var/www/
        ForceCommand internal-sftp 
        X11Forwarding no 
        AllowTcpForwarding no 
        PasswordAuthentication yes

Save and exit (press CTRL + X, press Y and then press ENTER)

Test SSH config before restarting.

sudo sshd -t

If no errors, restart the sshd service for changes to take affect.

sudo service sshd restart

5.4. Set Directory Permissions

SFTP is very strict when it comes to chroot directory permissions and if they are not set correctly, you will not be able to log in, so please follow these instructions carefully.

The chroot is usually the directory above your document root. For example, by default /var/www/ is the chroot and /var/www/html is the document root. Another example, if your document root is one directory deeper such as /var/www/domain.com/public_html, then your chroot is /var/www/domain.com.

  • The chroot directory *and all of its parents* must not have group or world write capabilities, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both /var/ and /var/www/ are set to 755. (not 775, which gives group write permissions).
  • The chroot directory *and all of its parents* must be owned by root, otherwise SFTP log in will fail with fatal: bad ownership or modes for chroot directory component “/var/www/”. In other words, you must make sure both /var/ and /var/www/ are both owned by root.
  • If your chroot directory is not /var/www but, for example, /var/www/domain.com, then you will need to apply these permissions and root ownership to that folder as well and all of its parents!

Firstly, let’s check permissions and ownership for /var/ – they should be 755 and root by default.

sudo ls -ld /var/

Output:

drwxr-xr-x 14 root root 4096 Jul 30 02:24 /var/

If they do not match above, set permissions and ownership below.

sudo chmod 755 /var/
sudo chown root:root /var/

Now let’s apply the same permissions and ownership for your chroot. Assuming that your chroot is /var/www/, ensure that the directory is set to 755.

sudo chmod 755 /var/www/

Ensure your chroot directory is owned by root.

sudo chown root:root /var/www/

To check permissions for this directory:

sudo ls -ld /var/www/

Output:

drwxr-xr-x 14 root root 4096 Jun  3 14:28 /var/www/

Make sure the document root is set to 775, which will allow groups write to this directory.

sudo chmod 775 /var/www/html

Make sure that your document root and all contents are owned by root and the group sftp_users.

sudo chown -R root:sftp_users /var/www/html*

Change all directories in the document root to 775. This will allow the sftp_users group to read, write and execute folders.

sudo find /var/www/html/ -type d -exec chmod 775 {} \;

Change all files in the document root to 664, this will allow the sftp_users group to read, write and execute files.

sudo find /var/www/html/ -type f -exec chmod 664 {} \;

Make sure that any new files or folders created by the SFTP user inherit the group of the document root, in this example the sftp-users group.

sudo find /var/www/html -type d -exec chmod g+s {} \;

Now log into SFTP with you preferred FTP client and make sure you can create, edit and delete files and folders in the document root.

If you are not able to log in, check the auth log for the last 50 entries. Also try closing your FTP client and opening it again.

sudo tail -n 50 /var/log/auth.log

If you need to provide other SFTP users write access to the document root, simply add them to the sftp-users group (Step 5.2).

5.5. WordPress and www-data

www-data now has no write access to your document root, which is the preferred security setup. However, you may need to give www-data write access to certain files and directories in order for WordPress to function properly.

.htaccess and Apache

If you get an Apache error after setting permissions in the previous steps “You don’t have permission to access this resource. Server unable to read htaccess file, denying access to be safe”, you may need to give both www-data and the sftp-users group ownership of .htaccess.

sudo chown www-data:sftp_users /var/www/html/.htaccess

WordPress Uploads Directory

If your users need to upload files through the WordPress media library, you will need to give www-data write access to the uploads directory in /var/www/html/wp-content/uploads.

sudo chown -R www-data:sftp_users /var/www/html/wp-content/uploads*

This will give both WordPress and all your SFTP users write access to the uploads directory.

WordPress Cache Directory

If you are using a caching plugin such as W3 Total Cache plugin, you should give www-data write access to the cache directory in /var/www/html/wp-content/cache.

sudo chown -R www-data:sftp_users /var/www/html/wp-content/cache*

As well as the above, if you’re using W3 Total Cache, give www-data write access to the w3tc-config folder.

sudo chown -R www-data:sftp_users /var/www/html/wp-content/w3tc-config*

If installing W3 Total Cache for the first time, you may have to give www-data write access to wp-config.php and .htaccess, but you can revoke it later.

sudo chown www-data:sftp_users /var/www/html/wp-content/wp-config.php
sudo chown www-data:sftp_users /var/www/html/wp-content/.htaccess 

WordFence

If you are using WordFence, you should give www-data write access to /wp-content/wflogs/.

sudo chown -R www-data:sftp_users /var/www/html/wp-content/wflogs*

If you are setting up WordFence Web Application Firewall (WAF), you will need to give www-data write access to .htaccess and the document root folder. You can revoke write access after WAF is configured.

WordPress Updates and Installing/Updating Plugins

WordPress normally uses www-data in order to update itself and add/update plugins. This is the most common setup but it is not the most secure because any rogue plugin could compromise your entire document root.

You should instead upload the SSH SFTP Updater Support plugin to your WordPress plugins directory and then activate it in WordPress.

Once activated, if you need to update WordPress or add/update plugins, you will be prompted for your SFTP username and password/SSH key. You can save this password or SSH key in your browser password manager so you don’t have to type it every time.

If the SSH SFTP Updater Support plugin isn’t prompting you to enter password when you try to update WordPress or alter plugins, add an entry in your wp-config.php file for define('FS_METHOD', 'ssh2');. Make sure there are no other entries for FS_METHOD in the config file.

Auto update WordPress plugins

WordPress 5.5 (released Aug 2020) now has the capability of automatically updating plugins itself. If you have 20+ WordPress installs to maintain like I do, this can make your life a lot easier! If you want this feature, you would have to give www-data write access to the plugins directory.

However, this introduces a new problem. If you have installed SSH SFTP Updater Support plugin, WordPress will not be able to update plugins itself despite having write access to the plugins folder. It will try to auto update the plugin but will be waiting for you to enter your SFTP username and password.

In this case, you should add define('FS_METHOD', 'direct'); in wp-config.php to force WordPress not to prompt for SFTP details and then it will auto update plugins itself. However, now you will not be able to update WordPress core in the control panel when an update is available. In this case you would have to temporarily comment out define('FS_METHOD', 'direct'); and then the WordPress core update will run. I haven’t found an easier method for this just yet, but as WordPress core updates aren’t as frequent as plugin updates, it’s not a big deal for me.

To allow WordPress to auto update plugins, give www-data write access to the wp-content and plugins directory.

sudo chown www-data:sftp_users /var/www/html/wp-content/
sudo chown -R www-data:sftp_users /var/www/html/wp-content/plugins*

You may also need to give www-data write access to the wp-content/upgrade directory if it exists.

sudo chown -R www-data:sftp_users /var/www/html/wp-content/upgrade*

If you already have plugins in the plugins folder, make sure to update all directories and files.

sudo find /var/www/html/wp-content/plugins -type d -exec chmod 775 {} \;
sudo find /var/www/html/wp-content/plugins -type f -exec chmod 664 {} \;

And make sure any new files and directories inherit the correct permissions.

sudo find /var/www/html/wp-content/plugins -type d -exec chmod g+s {} \;

And don’t forget to add define('FS_METHOD', 'direct'); to wp-config.php.

Just bear in mind that if there is a security breach, the attacker will have write access to your entire plugins folder, but not your entire document root.

But can WordPress core update itself?

This is one caveat of restricting where www-data can write to. If there is a critical security patch released, WordPress will not have the appropriate permissions to apply this patch on its own. You would have to give www-data write access to the document root and all WordPress folders, which sort of defeats the purpose of restricting permissions.

However, if you are serious about WordPress security, you should be proactively maintaining and updating your WordPress install frequently anyway. And in such eventuality where there is a critical security hole discovered in WordPress core, a hacker will still not be able to gain write access to your entire document root.

You can be notified of critical WordPress and plugin security patches via email using a plugin such as WordFence and then update WordPress yourself using the SSH SFTP Updater Support plugin as previously mentioned. This is the method that I personally employ for all my WordPress installs.

Let me know if this helped. Follow me on Twitter, Facebook and YouTube, or 🍊 buy me a smoothie.

p.s. I increased my AdSense revenue by 200% using AI 🤖. Read my Ezoic review to find out how.

54 replies

Leave a reply

Your email address will not be published. Required fields are marked *

  1. Me again 🙂
    I figured it out. It was just my missunderstanding.
    I added the domain name as a folder and added html inside that folder, then I made the domain folder the chroot and added -d /html to ForceCommand internal-sftp

    Now it works as I thougt it would.
    Thanks for the great tutorial. Saved me a lot of time!

  2. For Method 2: I followed the steps and can login on the server, create, delete folders or files in the configured DocumentRoot.
    Maybe it’s a missunderstanding on my part, but when I log in I can see all DocumentRoots and I thought the sftp user will only see its DocumentRoot. I cannot create a new folder or delete one in the other DocumentRoots, but I can create files or look at the other files.
    Is there a way to prevent this and make sure that the sftp user has only access to its DocumentRoot
    I redid all the steps and I am sure I followed everything.
    Chroot: /var/www/
    DocumentRoot of sftp_user1: /var/www/domain1

    There are several other domains under /var/www/

    I hope it’s clear what I mean since English isn’t my first language.

    Kind regards,
    Markus

  3. Method Two (Section 5) did not work for me, until I added sftp_users to AllowGroups and yyyyy to AllowUsers in /etc/ssh/sshd_config. The omission of either prevents access; the inclusion of both gets me in. But I did not see any reference to this in the article.

  4. I’m using Method Two (Section 5), and yyyyy is my account name for my virtual host (/var/www/54plymouth.net). I’m using WinSCP from my desktop to access my server, but I’m getting “Access denied” after I supply the password. /var/log/auth.log reports “User yyyyy from x.x.x.x not allowed because not listed in AllowUsers”. I’ve defined my ssh login in /etc/ssh/sshd_config in AllowUsers, but I don’t see any mention in this article of a similar requirement for sftp users. Should I add yyyyy to AllowUsers, or is this an indication that I’ve missed a step somewhere?

  5. Thanks you SO MUCH for this tutorial. I spent hours searching for how to do this properly and yours is the only article I could find that got me through the process. I’ve also learned a lot about how to think about server security in the process. If, by some amazing chance, we ever meet, I owe you a beer.

  6. Dear DevAnswers. I have got VPS on Debian 9 with Apache2 and SSH. I set up two virtual hosts. One is, for example, /var/www/html/domain1.com/. I will install WordPress there. I have to add sftp user. My question is, is it benefitial (especially from security point of view) to add sftp user with home directory in /var/www/html/domain1.com/ ? Instead of creating normal user with home directory in /home/user_name. If this sftp user is only for maintaining WordPress, then also for sake of order, isn’t it better to make his home directory in /var/www/html/domain1.com/ ? Thanks in advance for your reply.

    1. My recommendation would be to leave the home folder in the default location /home/.

      The SFTP user doesn’t need access to their home folder and they can’t log in over SSH, that’s why we have ForceCommand internal-sftp; in the SSH config to force SFTP access only.

  7. Thanks for this great article. As I am learner and very new to web development and also on linux. After I successfully configured following Method One of this article I installed fileZilla on my ubuntu 18.04 LTS os but failed to connect to my web server. ( I used my Server’s IP, my SFTP username and password on FileZilla)
    After login attempt is reflects –
    Response: 530 Login authentication failed
    Error: Critical error: Could not connect to server

    1. I am getting this same error. However, the problem did not present itself until recenstly and I followed the changes in this article months ago.

      1. Is this preventing SFTP login or just SSH?

        You cannot log in through SSH using the SFTP user, that’s diabled with ForceCommand internal-sftp; in the SSH config.

  8. Great article, thank you. After I successfully configured this following Method Two, I can no longer open an SSH session using PuTTY from windows – the console closes right after the credentials are entered. Any idea what might have caused this?

  9. Thanks for your great website. I have used it many times sorting out my server. I have followed this guide to the letter and still can’t connect. I have multiple sites on the server running apache, all with wordpress installed. I have only tried the less secure method but am getting a Connection refused
    Error: Could not connect to server error message.
    Any ideas?

  10. I was so happy when I made yesterday everything work, yet I encountered a serious problem. I had SFTP with multiple virtual hosts, and I was using FileZilla to copy files on each one. Yet i forgot to add sftp_users as owner to one of virtual hosts, and couple dozens mkdir could not be executed, and the transfer took a long while.
    Meantime I was typing command on mu ubuntu server and keyboard suddenly stopped working. The underscore line was blinking whole time, but I wasn’t able to type anything. After few minutes I hit enter few times, typed shutdown (or reboot), press enter and wait… and the server executed the command!

    Ok, I tought that everything is fine, but now I’m unable to connevt to sftp. Filezilla returns FATAL ERROR: Network error: Software caused connection abort.
    I have no idea what to do now. I tried redo all the steps multiple times, checked virtual hosts settings and tried to redo them as well, nothing works. What can be the problem? I’d like to provide more informations or some logs, but I don’t know where could i get some. Can You help me?

    1. Sorry to hear you’re having issues.

      The FileZilla error sounds like it is just not seeing any SFTP service on your server, so you should start troubleshooting that issue first.

      Are you remotely connected to your Linux box via SSH or physically at the machine with keyboard?

      I’d firstly try a port scan of your IP to see if it’s seeing SSH port 22 (which SFTP uses). There are several tools online for that.

      Any errors in your SSHD config?

      sudo sshd -t

      Also make sure to completely close FileZilla and open it again after making any permissions changes in Linux. Sometimes this is the only way to force FileZilla to log out and in again.

      1. sudo sshd -t returns no error. Only without sudo it returns sshd: no hostkeys available -- exiting.

        I rebooted both server and my PC just to be sure, but problem stays. I’m trying to connect to server from windows PC.

        Online scan shows that port 22 is open on my IP.

        It’s not FileZilla-only problem, because I’m using as well Notepad++ with ftp addon and it does return such thing:
        Connecting [SFTP] Host key accepted [SFTP] Successfully authenticated [SFTP] Error initialising channel: Socket error: Unknown error Unable to connect Disconnected

        So it does look like it does connect and error shows up later. Are there any log files of that attempt on server that can help identify the issue? I’m almost sure that I messed something up while tryint to reconfigure virtual hosts, but I’m unable to find error.

        And it did totaly worked before… Yet I might mess up something while I was setting up virtual hosts and trying to redo the guides. However I’m unable to find error, even if it might be something obvious for more experienced user.

        Thank You in advance for Your time!

      2. Some additional info: it does look like ssh connection does not work after all. My ubuntu server local IP is 192.168.1.5, and my windows pc does have 192.168.1.3.

        First I tried cmd on windows:
        telnet 192.168.1.5 22, and such line occurs:
        SSH-2.0-OpenSSH_8.2p1 Ubuntu-4, but when I press enter, it returns Invalid SSH identification string., and Connection with host was interrupted (the second one is my translation, not original message).

        Than I tried on Ubuntu server:
        telnet 192.168.1.3 22, and it ends up on Trying 192.168.1.3... untill it is timed out.

        So it looks like after all the connection is not established at all. Windows does know that he is connecting to ubuntu, so there is “some” connection, but I know to little to judge that…
        I tried to repeat all instructions from points 1 and 2, but it does not fix issue. Reboot does not help as well. What could be the reason?

        1. Can you check your auth log to if there is any more info there? This will return the last 50 entries:

          sudo tail -n 50 /var/log/auth.log

          Also, I’m wondering could this be related to the sshd: no hostkeys available -- exiting error. Have you done a search on how to generate these hostkeys in Ubuntu?

          I would also download PuTTY for Windows and see if you can connect to the Ubuntu server using your root account. https://devanswers.co/log-linux-windows-using-putty/

          1. I was not able to find a solution for sshd: no hostkeys available — exiting. Yet it does work previously regardless of that error. I’d like to get rid of it just to be sure, but I wasn’t able to do so.

            Putty connection attempt ended with error:
            Network Error: Software caused connection abort

            The auth.log indeed gives additional information. This raport does look the same for every connection attempt for FileZilla, Notepad++, or Putty, and looks like this:

            sshd[13413]: Accepted password for ftp_username from 192.168.1.3 port 49728 ssh2
            sshd[13413]: pam_unix(sshd:session): session opened for user ftp_username by (uid=0)
            systemd-logind[842]: New session 10 of user ftp_username.
            sshd[13483]: fatal: bad ownership or modes for chroot directory component “/var/www/”
            sshd[13413]:pam_unix(sshd:session): session closed for user ftp_username

          2. (had some troubles with posting my answer, hope it didn’t spam You, heres the rest of my message):

            In sshd_config I set ChrootDirectory /var/www/example.com/, but I’m not sure how it should be set if I use as well /var/www/example2.lan/ and /var/www/example3.lan/? I don’t remember the setthings that I had when it worked for a while…

            Meantime tried to set ChrootDirectory /var/www/ so it could work for all at once, but guess it doesn’t work like that. since it’s not directly above public_html. I even and set privilages from 5.4. Set Document Root Permissions for /var/www/ – perhaps that was my mistake? If so – I don’t know how to fix it.

          3. PuTTY won’t work for your SFTP accounts, but can you log in using your Linux root account just to confirm SSH is working correctly?

            It really sounds like a permissions issue. It’s probably the most tedious part of setting this up as SFTP is really picky about directory permissions and ownership.

            I can give you the permissions for my own setup (Using Method Two). You should compare your ones and make sure they are similar.

            sudo ls -la /var/www
            drwxr-xr-x 15 root     root     4096 Mar  6 00:28 .
            drwxr-xr-x 15 root     root     4096 Feb 13  2019 ..
            drwxr-xr-x  3 root     root     4096 Feb 19  2019 devanswers.co
            sudo ls -la /var/www/devanswers.co
            drwxr-xr-x  3 root root                4096 Feb  8 20:26 .
            drwxr-xr-x 13 root root                4096 Mar 26 17:20 ..
            drwxrwsr-x  7 root sftp_devanswers_co  4096 May  7 06:04 public_html
            sudo ls -la /var/www/devanswers.co/public_html
            drwxrwsr-x  7 root         sftp_devanswers_co   4096 May  7 06:04 .
            drwxr-xr-x  3 root         root                 4096 Feb  8 20:26 ..
            -rw-rw-r--  1 root         sftp_devanswers_co   198 May 18 22:53 .htaccess
            -rw-rw-r--  1 root         sftp_devanswers_co   405 Apr  1 13:55 index.php

            SSHD config entry:

            Match Group sftp_devanswers_co
                    ChrootDirectory /var/www/devanswers.co/
                    ForceCommand internal-sftp
                    X11Forwarding no
                    AllowTcpForwarding no
                    PasswordAuthentication yes
          4. I just realized how retarded I was when I dive under my desk to server instead of using Putty in the first place… You helped my SOOOO MUCH! Anyway: Putty SSH connection worked and I was able to login as root.

            Finally after altering chmod and chown I made it! I wasn’t able to reproduce all Your permissions 100% the same, because there was something like this:

            sudo ls -la /var/www
            drwxr-sr-x  3 root root 4096 maj 30 13:36 .
            drwxr-xr-x 14 root root 4096 maj 26 20:57 ..
            drwxr-sr-x  3 root root 4096 maj 27 19:05 test.com

            I tried various things to change drwxr-sr-x into drwxr-xr-x, but after I tested my setting and ftp connected I decided not to touch it anymore for now. Now I’ll have to set all my virtual hosts again from scratch because I removed them to make things simple again. Guess that I’ll reconnect FTP after every step just to know when I mess up sumething just to be safe.

            Thank You so much!

          5. Thanks for the feedback as it helps me improve these guides.

            The error you mentioned when viewing the auth log sudo tail -n 50 /var/log/auth.log

            fatal: bad ownership or modes for chroot directory component “/var/www/”`

            I came across this error today when I was setting up SFTP.

            This is due to the permissions and ownership of the chroot directory, which SFTP is very picky about. So it should be noted that:

            • The chroot directory and all of its parents must not have group or world write capabilities. In other words, you must make sure /var/www/ is set to 755. (not 775, which gives group write permissions).
            • The chroot directory and all of its parents must be owned by root.

            I have updated the article to clarify this.

  11. It worked right up until I did the “match user” thing, then I started getting this message.

    ssh_init: Name or service not known
    Could not connect to server

    So I removed the code block from the config, but it still gives this error. I cannot get back to how it was before, even after complete reboot.

    1. That’s strange. Do you get any error when running sshd -t?

      Are you behind a firewall, can you connect ok via SSH?

      1. After sshd -t part i get sshd: no hostkeys available -- exiting.

        However i tried sudo sshd -t and there was no error.

        Is something wrong?

        1. There must be some permissions issue going on there but I think it can be ignored. I’ve updated the guide to include sudo.

  12. Could be a good guide but its “Generate a password and press enter to accept all defaults.” where it broke!

  13. Great!

    Thank you very much!

    This saves me a lot of time!

    I was looking for how to install vsftpd… this was easier and better.

  14. So, normally I have had Apache:Apache as ownership in my web directory. Now a test file transferred to the web root is user:user. Will this be accessible to the website? Did I miss something?

      1. Make sure that www-data can read and execute files in your doc root.

        I am currently re-writing this guide to clarify this and should have it up in a day or so.

  15. Great explanation. I’ve also seen some posts disabling login for the ftp user…

    usermod -s /usr/sbin/nologin myuser

  16. This is the finest LAMP tutorial I have ever read on the Internet. It actually works and it is clear that you spent a whole lot of time writing this. You are a master of LAMP. Thank you!

  17. Excellent guide. Finally I have found what I looking for (paraphrasing Its U2’s song…). You help me a lot!!!.
    Many thanks and greetings from Argentina,

    Hugo

  18. Something I don’t understand about your instructions, and I’m a learner so I don’t want to say YOU’VE GOT IT WRONG when it is much more likely that I’m misunderstanding something, so please correct me!
    (By the way, having to exclude code from my feedback because with code, getting blocked by cloudflare)

    But, as I understand it
    SFTP protocol explicitly depends on the user having SSH. To be denied an SSH login is to be denied an SFTP login.

    So Step 1.3 is to “(…) restrict the user (…) to the document root and also disable their SSH access – we only want them to be able to log in over SFTP.” and that contradicts what I understand about SFTP.

    Experience bears this out; If I include the line (code broken up to allow without cloudflare having a hissy fit) Fo rc eC om ma nd inte rn al-sf tp in my file /e tc/ss h/s shd _con fig , I can’t log in with FileZilla. If I comment it out and reload sshd then filezilla works over sftp.

  19. I first followed this to try a single user with access to an experimental directory. It worked fine. Then I wanted to try adding a user to a group, and setting up the group access like you described in part 2. I was able to get the user to log in and navigate all through the folders down from the chroot, but I get a permissions denied message when trying to upload files to any of the directories.

    1. So you should check the permissions for the directory and make sure they are set to the correct group. e.g:

      ls -ld /var/www/html
      
  20. I followed this tut yesterday, and everything was working fine, except none of the users I created had access to upload anything to the server.

    So after reading more about permissions and groups, I changed some permissions settings. Restarted the server, and now I can’t connect at all. Running FileZilla on the server computer, gives the error:
    Error: Connection reset by peer
    Error: Could not connect to server

    From any of my other computers, I get:
    Network Error: Software caused connection to abort.

    Any ideas, what happened, an/or how to fix it?

      1. I decided to double check the newest parts I added to the sshd config, and compared it to items listed in this tutorial, and discovered I had a couple errors. Basically, the chroot was wrong for my sites. I had it set for var www mydomain.com public_html. Once I removed the public_html, from the end of each entry, I was able to log in again, and upload files.

        However, in the process, I discovered one of my sites is not functioning at all. And giving a error 500: This page isn’t working
        masterbuddasartgallery.com is currently unable to handle this request.
        HTTP ERROR 500.

        I’m thinking it’s due to the old server it was hosted on was using Apache. and I’m now using Nginx. So looking into how to transfer it over. Already set the database up for it, so that part “should” work. LOL