TakeHost
← All tutorials

Server Management

How to Set Up Secure File Transfers (SFTP and FTPS)

Intermediate16 minSFTPFTPSOpenSSHFile TransferSecurity

Plain FTP sends your username, password, and files in cleartext, so anyone on the network can read them; it must not be used. The right answer is SFTP, which rides on the OpenSSH you already have, is fully encrypted, and needs nothing installed. This guide sets up a dedicated chrooted SFTP user, and covers FTPS with forced TLS only as an alternative when a legacy client cannot speak SFTP.

/01

Prefer SFTP: Nothing to Install

If OpenSSH is running, SFTP already works over the same encrypted channel as SSH on port 22. Confirm the server is up.

sudo systemctl status ssh --no-pager   # OpenSSH provides SFTP automatically, no extra package needed
/02

Create a Dedicated, Restricted SFTP User

Make a user that can transfer files but cannot get a shell, and add it to an sftp-only group.

sudo groupadd sftponly
# --shell /usr/sbin/nologin denies an interactive shell; this account is for file transfer only
sudo useradd -m -g sftponly -s /usr/sbin/nologin sftpuser
sudo passwd sftpuser   # or, better, install an SSH key for this user
/03

Set Up the Chroot Directory Ownership

For chroot to work, the chroot root must be owned by root and not writable by the user. Put writable content in a subdirectory the user owns.

# The chroot root itself MUST be root-owned and not group/other writable
sudo chown root:root /home/sftpuser
sudo chmod 755 /home/sftpuser
# Give the user a writable upload area inside the jail
sudo mkdir -p /home/sftpuser/upload
sudo chown sftpuser:sftponly /home/sftpuser/upload
/04

Lock the User Into an SFTP Chroot

Add a Match block so this user (and the sftponly group) is forced into internal-sftp, chrooted to their home, with no shell, TCP forwarding, or tunneling.

sudo tee /etc/ssh/sshd_config.d/10-sftp.conf > /dev/null <<EOF
Match Group sftponly
    ChrootDirectory %h            # jail the user to their home directory
    ForceCommand internal-sftp    # allow only SFTP, never a shell
    AllowTcpForwarding no
    X11Forwarding no
EOF
sudo sshd -t && sudo systemctl reload ssh   # validate config, then reload
/05

Verify the SFTP Connection

Connect with the sftp client. You should land inside the jail and only be able to write under upload.

sftp sftpuser@your_server_ip
# At the sftp> prompt:
#   pwd        -> shows / (the chroot, which is really their home)
#   cd upload  -> the only writable place
#   put localfile.txt
#   bye
/06

Alternative: FTPS with Forced TLS (vsftpd)

Only if a legacy client truly cannot use SFTP, set up vsftpd with TLS REQUIRED, anonymous access off, and users chrooted. Never enable plain FTP without ssl_enable.

sudo apt update && sudo apt install -y vsftpd
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.backup
# Generate a self-signed (or use your real) certificate for the TLS layer
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/vsftpd.pem -out /etc/ssl/private/vsftpd.pem
sudo tee -a /etc/vsftpd.conf > /dev/null <<EOF
anonymous_enable=NO        # no anonymous logins, ever
local_enable=YES
write_enable=YES
chroot_local_user=YES      # jail each user to their home
ssl_enable=YES             # turn on TLS
force_local_data_ssl=YES   # REQUIRE TLS for data transfers (no cleartext fallback)
force_local_logins_ssl=YES # REQUIRE TLS for the login/credentials
rsa_cert_file=/etc/ssl/private/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.pem
EOF
sudo systemctl restart vsftpd
# Firewall: control + passive data ports
sudo ufw allow 21/tcp
sudo ufw allow 990/tcp
sudo ufw allow 40000:50000/tcp

Use SFTP: it is encrypted, already included with OpenSSH, and here it is locked to a chrooted, shell-less user. Plain FTP transmits credentials in cleartext and must never be used; if a legacy client forces your hand, only FTPS with TLS required is acceptable.

Ready when you are

Deploy it on TakeHost.