Set up an FTP server to use with your friends

I've been itching to set up an FTP server on my CentOS VPS, so that whenever one of my friend asks for some files, I have a quick way of meeting their needs. I ended up setting up a vsftpd server. As inspiration I used this great tutorial from digitalocean. However the problem with it is that it was set up for a separate directory for each user. Not what I wanted. So I dived deep.

First things first - install the FTP server. It's a lot easier to set this whole thing up if you become root.

sudo -i
dnf install vsftpd

Next the firewall. Don't ever forget the firewall. 😂 Now here, you have some choices to make. Do you want to expose port 21, so that every bot on the planet will try and violate your port or you choose a custom one. I went with the latter. You will need a port for FTP commands (default 21) and a port range for actual data transfer in passive mode (server decides the port). If confused, click.

The default firewall (called firewalld) on RedHat and the like ... really whips the llama's ass. Not only that the control commands make a lot of sense, but you can create your own services to easily add to the rules. Which we are going to do now, since we are here. To set up a service definition for firewalld all you have to do is edit an *.xml in a certain location.

nano /usr/lib/firewalld/services/vsftpd-custom.xml

Add the following lines and save.

<service>
  	<short>VSFTPD-custom</short>
  	<description>VSFTPD custom port settings</description>
  	<port port="33021" protocol="tcp"/>
  	<port port="33030-33050" protocol="tcp"/>
</service>

All you have to do afterwards is to reload the firewall so it recognizes the freshly defined service, add the service as permanent and reload firewalld again.

firewall-cmd --reload
firewall-cmd --add-service=vsftpd-custom --permanent
firewall-cmd --reload
firewall-cmd --list-all

Running the last command should reveal all your active firewall entries and you should see VSFTPD-custom among services. I think it's nice that definitions have a name so you don't have to remember the reason for each of the 4937 ports you opened.

Okay, now with the firewall out of the way, let's get on with the FTP server. First we back up the default configuration (just in case) and create our own.

mv /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bkp
nano /etc/vsftpd/vsftpd.conf

Next thing, paste the below configuration. The default has some pretty clear comments and I also added my own, so I won't repeat myself here.

# Config guide: https://web.mit.edu/rhel-doc/5/RHEL-5-manual/Deployment_Guide-en-US/s1-ftp-vsftpd-conf.html
#
# Allow anonymous FTP? (Beware - allowed by default if you comment this out).
anonymous_enable=NO
#
# Uncomment this to allow local users to log in.
local_enable=YES
#
# Uncomment this to enable any form of FTP write command.
write_enable=YES
#
# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
#022=755; 002=775 (to calculate 777-what you need)
local_umask=022
#local_umask=002
#
# Activate directory messages - messages given to remote users when they
# go into a certain directory.
dirmessage_enable=YES
#
# Activate logging of uploads/downloads.
xferlog_enable=YES
#
# Make sure PORT transfer connections originate from port 20 (ftp-data).
connect_from_port_20=YES
#
# You may override where the log file goes if you like. The default is shown
# below.
#xferlog_file=/var/log/xferlog
#
# If you want, you can have your log file in standard ftpd xferlog format.
# Note that the default log file location is /var/log/xferlog in this case.
xferlog_std_format=YES
#
# You may change the default value for timing out an idle session.
#idle_session_timeout=600
#
# You may change the default value for timing out a data connection.
#data_connection_timeout=120
#
# You may fully customise the login banner string:
#ftpd_banner=Welcome to blah FTP service.
#
# When "listen" directive is enabled, vsftpd runs in standalone mode and
# listens on IPv4 sockets. This directive cannot be used in conjunction
# with the listen_ipv6 directive.
listen=NO
# with ipv6 it listens also to ipv4
listen_ipv6=YES
#
# Port for FTP server to listen on; comment to use default 21
listen_port=33021
#
pam_service_name=vsftpd
#
# Only allow users on list
userlist_enable=YES
userlist_file=/etc/vsftpd/vsftpd.userlist
# However, userlist_deny=NO alters the setting, meaning that only users explicitly listed in userlist_file=/etc/vsftpd/vsftpd.userlist will be permitted to login.
userlist_deny=NO
#
# (Warning! chroot'ing can be very dangerous. If using chroot, make sure that
# the user does not have write access to the top level directory within the
# chroot)
#
# Jail ftp users to ftp root
chroot_local_user=YES
#
# writeable local root
#allow_writeable_chroot=YES
#
# define local root
local_root=/var/ftp/pub
#
# Ports used for PASsiVe mode
pasv_min_port=33030
pasv_max_port=33050
#
#for users to have ftp go to their home dir
#user_sub_token=$USER
#local_root=/home/$USER/ftp
#
# Securing with TLS/SSL (uncomment all bellow)
#rsa_cert_file=/etc/pki/tls/private/vsftpd.pem
#rsa_private_key_file=/etc/pki/tls/private/vsftpd.pem
#ssl_enable=YES
#allow_anon_ssl=NO
#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

As you can see I also left the settings for running the server secured with TLS in and also the settings for  running the server with the FTP root defaulting to the user's home folder. Lines after pasv_max_port are safe to delete if you don't care. To securing with TLS I'll get back later.

Back to our setup. Paranoia sets in and I create a user that I specially use only for FTP. The digitalocean article had a nice tip on doing this by defining a custom shell for FTP users. Here is how to set it up:

nano /bin/ftponly

Add lines:

#!/bin/sh
echo "This account is limited to FTP access only."

Make it xecutable:

chmod a+x /bin/ftponly

Then, edit /etc/shells and add /bin/ftponly at the end.

Now let's create the user, add him to the FTP group (your choice) and give him no shell:

useradd -g ftp -s /bin/ftponly ftpuser
passwd ftpuser

After creating the user, we have to add it to the file referenced in vsftpd.conf. Only users included in this file will be able to access the server.

echo "ftpuser" | tee -a /etc/vsftpd/vsftpd.userlist

In order to allow people and the recently created user to upload files to our FTP server, we need to define a writable folder, because the way we secured our local root ftp directory, nobody is able to write it. And that is the way it should be.

chmod a-w /var/ftp/pub
mkdir /var/ftp/pub/uploads
chown ftpuser:ftp /var/ftp/pub/uploads
chmod g+w /var/ftp/pub/uploads

The only thing that is left to do is to start the vsftpd service and then enable it so it starts automatically when the server reboots.

systemctl start vsftpd
systemctl enable vsftpd

After this is done you should be able to access your own FTP server and put files in the uploads folder. Great success 😎.

Giving access to your FTP to a friend is easy as:

sudo -i
useradd -g ftp -s /bin/ftponly newftpuser
passwd newftpuser
echo "newftpuser" | tee -a /etc/vsftpd/vsftpd.userlist

A note on SELinux: this is a topic that I am no expert in, the only way I got my server working correctly was by modifying it like this - maybe you know a more secure way:

setsebool ftpd_full_access on

Optional: If you want to secure your server with TLS, you have to generate certificates (these will be valid for 1 year if you don't change the days)

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/pki/tls/private/vsftpd.pem -out /etc/pki/tls/private/vsftpd.pem

With the certificates generated, it's time to comment out the related lines in vsftpd.conf and restart the FTP server. You should be good to go!

And one more thing ...

Keep away from people who try to belittle your ambitions. Small people always do that, but the really great make you feel that you, too, can become great. - Twain