Vs
Storage Azure

vsftpd on Ubuntu 24.04 on Azure User Guide

| Product: vsftpd 3.0 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of vsftpd on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. vsftpd, the Very Secure FTP Daemon, is one of the most widely deployed FTP servers and the default FTP daemon of most Linux distributions. This image runs it in an FTPS first posture: explicit TLS is required on both the control channel and the data channel, so credentials and file contents are never sent in the clear.

The image installs vsftpd 3.0 from the official Ubuntu 24.04 apt archive, run under systemd as vsftpd.service. The server listens for the FTP control channel on tcp port 21 and uses a passive data port range of 40000 to 40009. There is no web interface and no database in this image: the single file /etc/vsftpd.conf is the whole configuration.

Secure by default, no known bootstrap credential. The stock package ships no usable login in this image. On the first boot of every VM a one shot service creates a unique FTP user with a strong random password, generates a unique self signed TLS certificate for that specific machine, and sets the passive mode address to the instance address, then writes the user, password and host to a root owned credentials file. Every user is chrooted to its own home directory, anonymous access is refused, and the FTP user is a virtual user whose password lives in a stand alone file rather than the system password database, so no two instances share a credential and no default login is ever baked into the image.

What is included:

  • vsftpd 3.0 from the Ubuntu 24.04 apt archive, run under systemd as vsftpd.service

  • FTPS (explicit TLS) required on the control and data channels (force_local_logins_ssl and force_local_data_ssl), so a plaintext FTP login is refused

  • A per instance unique FTP user and password and a unique self signed TLS certificate, generated on first boot and written to /root/vsftpd-credentials.txt (mode 0600)

  • Every user chrooted to its own home directory under /srv/vsftp, so an account sees only its own files

  • No anonymous access, and virtual user authentication only, so no system account can log in over FTP

  • A defined passive port range (40000 to 40009) and a passive address set to this VM's address for passive mode behind network address translation

Prerequisites

  • A VM launched from this cloudimg vsftpd image on Azure.
  • An Azure Network Security Group that allows inbound tcp 21 (FTP control) and tcp 40000 to 40009 (FTPS passive data), scoped to your client addresses only. Do not expose FTP to the whole internet.
  • SSH access to the VM as azureuser.

FTPS uses one connection for control and a separate connection for each transfer, so both port 21 and the passive range must be open to your clients. The passive range is small by design; widen it in /etc/vsftpd.conf and the NSG if you expect many concurrent transfers.

Verify the FTPS server is running

Connect over SSH as azureuser, then confirm vsftpd.service is active, that forced TLS is set in the configuration, and that the server is listening on port 21.

sudo systemctl is-active vsftpd
grep -E '^ssl_enable|^force_local_(logins|data)_ssl' /etc/vsftpd.conf
sudo ss -tlnH | grep -E ':21 '

You should see active, the three forced TLS directives set to YES, and a listener bound to 0.0.0.0:21 for the FTP control channel:

active
ssl_enable=YES
force_local_logins_ssl=YES
force_local_data_ssl=YES
LISTEN 0      32           0.0.0.0:21 0.0.0.0:*

The vsftpd service reports active under systemd, the configuration shows ssl_enable and both force_local_logins_ssl and force_local_data_ssl set to YES for forced FTPS, and ss shows the server listening on tcp port 21 for the FTP control channel on all interfaces

Retrieve the per instance credentials and test a secure transfer

On the first boot of your VM, vsftpd generated a unique FTP user, a unique password and a unique TLS certificate. The user, password and host are stored in the root owned credentials file:

sudo cat /root/vsftpd-credentials.txt

The file contains FTP_USER, FTP_PASSWORD and FTP_HOST, each unique to this VM. Test a secure transfer end to end with curl, which negotiates explicit TLS with --ssl-reqd and accepts the self signed certificate with --insecure. Because the server advertises this VM's public address for passive mode, --ftp-skip-pasv-ip tells curl to reuse the loopback address for the local test. The block below reads the generated user and password, uploads a file over FTPS and downloads it back, then confirms the contents match:

FTP_USER=$(sudo grep '^FTP_USER=' /root/vsftpd-credentials.txt | cut -d= -f2-)
FTP_PASSWORD=$(sudo grep '^FTP_PASSWORD=' /root/vsftpd-credentials.txt | cut -d= -f2-)
echo "cloudimg FTPS round trip $(date)" > /tmp/upload.txt
curl --ssl-reqd --insecure --ftp-skip-pasv-ip -u "$FTP_USER:$FTP_PASSWORD" -T /tmp/upload.txt ftp://127.0.0.1/upload.txt
curl --ssl-reqd --insecure --ftp-skip-pasv-ip -u "$FTP_USER:$FTP_PASSWORD" ftp://127.0.0.1/upload.txt -o /tmp/download.txt
cat /tmp/download.txt

The upload and download both succeed over explicit TLS and the downloaded contents match what you uploaded, proving the FTPS pipeline (TLS negotiation, the per instance virtual user and password, and the chrooted home directory) works end to end:

cloudimg FTPS round trip Mon Jul 27 19:03:40 UTC 2026

Reading the per VM credentials file with the password masked, then an FTPS upload and download round trip with curl using explicit TLS and the per VM virtual user; both transfers succeed and the downloaded content matches the uploaded content

Confirm plaintext, anonymous and out of jail access are refused

A secure FTP server must refuse anything less than TLS. This image requires explicit TLS, refuses anonymous logins, and chroots each user to its home directory. The block below tests all three: a plaintext login, an anonymous login and an attempt to read a file outside the user's home directory each fail.

FTP_USER=$(sudo grep '^FTP_USER=' /root/vsftpd-credentials.txt | cut -d= -f2-)
FTP_PASSWORD=$(sudo grep '^FTP_PASSWORD=' /root/vsftpd-credentials.txt | cut -d= -f2-)
curl -sS --ftp-skip-pasv-ip -u "$FTP_USER:$FTP_PASSWORD" ftp://127.0.0.1/ -o /dev/null || echo "plaintext FTP refused (TLS required)"
curl -sS --ssl-reqd --insecure --ftp-skip-pasv-ip -u "anonymous:x@x" ftp://127.0.0.1/ -o /dev/null || echo "anonymous login refused"
curl -sS --ssl-reqd --insecure --ftp-skip-pasv-ip -u "$FTP_USER:$FTP_PASSWORD" ftp://127.0.0.1/etc/passwd -o /dev/null || echo "chroot enforced: /etc/passwd is not reachable"

Each attempt is rejected, and the fallback message after each || prints:

plaintext FTP refused (TLS required)
anonymous login refused
chroot enforced: /etc/passwd is not reachable

The plaintext login is refused because TLS is required, the anonymous login is refused because anonymous_enable is off, and the request for /etc/passwd fails because the user is chrooted and its filesystem root is its own home directory.

A plaintext FTP login is refused because TLS is required, an anonymous login is refused, and an attempt to read /etc/passwd is refused because the FTP user is chrooted to its own home directory

Review the hardening configuration

All of the hardening lives in one readable file, /etc/vsftpd.conf. List the active (non comment) directives:

grep -vE '^[[:space:]]*#|^[[:space:]]*$' /etc/vsftpd.conf

The configuration enforces ssl_enable=YES with force_local_logins_ssl=YES and force_local_data_ssl=YES (explicit TLS required on control and data), anonymous_enable=NO (no anonymous access), chroot_local_user=YES with allow_writeable_chroot=YES and a per user local_root (chroot every user to its own home), guest_enable=YES with guest_username=vftp and virtual_use_local_privs=YES (virtual users only, so no system account can log in), TLS 1.2 with ssl_ciphers=HIGH, and the pasv_min_port / pasv_max_port range 40000 to 40009. The passive address for this specific VM is written to pasv_address on first boot. Edit the file and reload with sudo systemctl restart vsftpd after changes.

The active directives in /etc/vsftpd.conf showing ssl_enable and forced TLS on control and data, anonymous_enable set to NO, chroot_local_user with allow_writeable_chroot, guest virtual users mapped to vftp, TLS 1.2 with HIGH ciphers, and the passive port range 40000 to 40009

Connect from an FTPS client

From your own workstation, connect with any FTPS capable client using explicit TLS (also called FTP over TLS, or AUTH TLS). Use the FTP_USER, FTP_PASSWORD and FTP_HOST values from the credentials file, port 21, and enable explicit TLS. Because the certificate is self signed, accept it on first connection or replace it with your own certificate. For example, with curl from your workstation:

# Replace the host and password with the values from /root/vsftpd-credentials.txt.
curl --ssl-reqd --insecure -u FTP_USER:FTP_PASSWORD -T report.pdf ftp://FTP_HOST/report.pdf
curl --ssl-reqd --insecure -u FTP_USER:FTP_PASSWORD ftp://FTP_HOST/report.pdf -o report.pdf

Graphical clients such as FileZilla and WinSCP work the same way: choose the protocol FTP with Require explicit FTP over TLS, set the host to FTP_HOST, the port to 21, and passive mode. Ensure your Azure Network Security Group allows inbound tcp 21 and tcp 40000 to 40009 from your client address.

Add more users and replace the certificate

Each FTP account is a vsftpd virtual user: a line in /etc/vsftpd/virtual.passwd of the form username:hash, where the hash comes from openssl passwd -6, plus a home directory under /srv/vsftp/<username> owned by the vftp system account. To add another isolated FTP account, append a line to that file and create its home directory; it can then log in over FTPS chrooted to that directory, with no system account involved. To use a certificate signed by a certificate authority instead of the per instance self signed one, replace /etc/vsftpd/ssl/vsftpd.crt and /etc/vsftpd/ssl/vsftpd.key and reload the service. For larger deployments you can add quotas and bandwidth controls within the vsftpd configuration.

Support

cloudimg provides 24/7 technical support for this vsftpd image by email (support@cloudimg.co.uk) and live chat. We help with connecting FTPS clients, managing virtual users and directories, configuring chroot and quotas, opening the control port and passive range in your network security group, setting the passive address behind network address translation, TLS certificate management, version upgrades and troubleshooting. Critical issues receive a one hour average response time.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.