Storage Azure

ProFTPD on Ubuntu 24.04 on Azure User Guide

| Product: ProFTPD 1.3 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of ProFTPD on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. ProFTPD is a highly configurable, widely deployed FTP server for Unix and Linux, and 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 ProFTPD 1.3 from the official Ubuntu 24.04 apt archive with the mod_tls module, run under systemd as proftpd.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 configuration tree under /etc/proftpd 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 masquerade 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 only members of a dedicated login group may authenticate, so no two instances share a credential.

What is included:

  • ProFTPD 1.3 from the Ubuntu 24.04 apt archive, run under systemd as proftpd.service

  • FTPS (explicit TLS) required on the control and data channels (TLSRequired on), 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/proftpd-credentials.txt (mode 0600)

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

  • No anonymous access, and login restricted to members of the dedicated cloudimgftp group

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

Prerequisites

  • A VM launched from this cloudimg ProFTPD 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/proftpd/conf.d/cloudimg.conf and the NSG if you expect many concurrent transfers.

Verify the FTPS server is running

Connect over SSH as azureuser, then confirm proftpd.service is active, the mod_tls module is loaded and the server is listening on port 21.

sudo systemctl is-active proftpd
proftpd -vv 2>/dev/null | grep -i mod_tls | head -1
sudo ss -tlnH | grep -E ':21 '

You should see active, the mod_tls module listed, and a listener bound to *:21 for the FTP control channel.

The proftpd service reports active under systemd, the ProFTPD binary reports Version 1.3.8, the mod_tls module is loaded for 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, ProFTPD 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/proftpd-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 uses the self signed certificate with --insecure. 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/proftpd-credentials.txt | cut -d= -f2-)
FTP_PASSWORD=$(sudo grep '^FTP_PASSWORD=' /root/proftpd-credentials.txt | cut -d= -f2-)
echo "cloudimg FTPS round trip $(date)" > /tmp/upload.txt
curl --ssl-reqd --insecure -u "$FTP_USER:$FTP_PASSWORD" -T /tmp/upload.txt ftp://127.0.0.1/upload.txt
curl --ssl-reqd --insecure -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 user and password, and the chrooted home directory) works end to end.

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 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/proftpd-credentials.txt | cut -d= -f2-)
FTP_PASSWORD=$(sudo grep '^FTP_PASSWORD=' /root/proftpd-credentials.txt | cut -d= -f2-)
curl -sS -u "$FTP_USER:$FTP_PASSWORD" ftp://127.0.0.1/ -o /dev/null || echo "plaintext FTP refused (TLS required)"
curl -sS --ssl-reqd --insecure -u "anonymous:x@x" ftp://127.0.0.1/ -o /dev/null || echo "anonymous login refused"
curl -sS --ssl-reqd --insecure -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: the plaintext login is refused because TLS is required, the anonymous login is refused because only members of the cloudimgftp group may authenticate, 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 with access denied, an anonymous login is refused with access denied, 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 drop in file, /etc/proftpd/conf.d/cloudimg.conf, which is included by the main proftpd.conf:

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

The drop in enforces TLSEngine on and TLSRequired on (explicit TLS on control and data), DefaultRoot ~ (chroot every user to its home), a global <Limit LOGIN> that allows only the cloudimgftp group and denies everything else, TLSOptions NoSessionReuseRequired for broad client interoperability, and the PassivePorts 40000 40009 range. The passive mode address for this specific VM is written to /etc/proftpd/conf.d/cloudimg-masq.conf on first boot. Edit either file and reload with sudo systemctl restart proftpd after changes.

The cloudimg hardening drop in showing TLSEngine on, TLSRequired on, DefaultRoot chroot, the Limit LOGIN block allowing only the cloudimgftp group, the passive port range, and a count of zero active anonymous blocks in the configuration

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/proftpd-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 normal system user in the cloudimgftp group with a home directory under /srv/ftp and a nologin shell. To add another isolated FTP account, create a user in that group, give it a strong password and its own home, then it can log in over FTPS chrooted to that directory. To use a certificate signed by a certificate authority instead of the per instance self signed one, replace /etc/proftpd/tls/proftpd.crt and /etc/proftpd/tls/proftpd.key and reload the service. For larger deployments you can move to virtual users backed by a file, LDAP or SQL, and add quotas and bandwidth controls, all within the ProFTPD configuration tree.

Support

cloudimg provides 24/7 technical support for this ProFTPD image by email (support@cloudimg.co.uk) and live chat. We help with connecting FTPS clients, managing users and directories, configuring chroot and quotas, opening the control port and passive range in your network security group, setting the masquerade address behind network address translation, moving to virtual users with LDAP or SQL backends, 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.