Easily setup a secure FTP server with vsftpd and letsencrypt

I recently had to set up a FTP server for some designers to upload their work (unfortunately they couldn’t use SFTP otherwise it would have been much simpler!). I’ve not had to set up vsftpd for a while, and when I last did it I didn’t much worry about needing to use encryption. So here are some notes on how to set up vsftpd with letsencrypt on ubuntu 14.04 / 16.04 so that only a specific user or two are permitted access.

First, install vsftpd:

apt install -y vsftpd

Next, you need to make sure you have installed letsencrypt. If not, you can do so using the instructions here – fortunately letsencrypt installation has got a lot easier since my last blog post about letsencrypt almost 2 years ago.

I’m assuming you are running this on the same server as the website, and you’re wanting to set it up as ftp on the same domain or similar subdomain as the website (eg ftp access direct to example.org, or via something like ftp.example.org). If not, you can do a manual install of the certificate but then you will need to redo this every 3 months.

Assuming you’re running the site on apache get the certificate like:

certbot --apache -d example.org,www.example.org

You should now have the necessary certificates in the /etc/letsencrypt/live/example.org/ folder, and your site should be accessible nicely via https.

Now, create a user for FTP using the useradd command. If you want to just create a user that only has access to the server via FTP but not a regular account you can modify the PAM configuration file /etc/pam.d/vsftpd and comment out the following line:

# Not required to be allowed normal login to box
#auth   required        pam_shells.so

This lets you keep nologin as the shell so the user cannot login normally but can log in via vsftpd’s PAM layer.

Now open up /etc/vsftpd.conf

pam_service_name=vsftpd

# Paths to your letsencrypt files
rsa_cert_file=/etc/letsencrypt/live/example.org/fullchain.pem
rsa_private_key_file=/etc/letsencrypt/live/example.org/privkey.pem
ssl_enable=YES
allow_anon_ssl=NO

# Options to force all communications over SSL - why would you want to
# allow clear these days? Comment them out if you don't want to force
# SSL though
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO

require_ssl_reuse=NO
ssl_ciphers=HIGH

Because we’re running behind a firewall we want to specify which port range to open up for the connections (as well as port 21 for FTP of course):

pasv_min_port=40000
pasv_max_port=41000

If you want to make it even more secure by only allowing users listed in /etc/vsftpd.userlist to be able to log in, add some usernames in that file and then add the following to the /etc/vsftpd.conf configuration file:

userlist_enable=YES
userlist_file=/etc/vsftpd.userlist
userlist_deny=NO

You can test using the excellent lftp command:

lftp -u user,pass -e 'set ftp:ssl-force true' example.org/

If the cert is giving errors or is self-signed, you can do the following to connect ignoring them:

lftp -u user,pass -e 'set ssl:verify-certificate false; set ftp:ssl-force true' example.org/