FTP virtual users are a way to grant access to your FTP server without creating actual operating system accounts for each user. This is great for security and manageability, especially when you need to give temporary or limited access to external parties.
Let’s see this in action. We’ll use vsftpd, a popular and secure FTP server, for this example.
Imagine you have a web server and you want to allow a client to upload new images to a specific directory, say /var/www/html/images. Instead of creating a user like client_uploader on the server’s OS, we’ll set up a virtual user client_uploader that only exists within vsftpd.
First, we need a way for vsftpd to authenticate these virtual users. The most common way is using a PAM module that reads user credentials from a flat file. We’ll use pam_userdb.
Here’s how you set up the user database:
-
Create a text file with usernames and passwords. Let’s call it
vuser_pass.txt. Each line will beusername:password.client_uploader:S3cureP@ssw0rd! another_user:AnotherSecret -
Generate the database files.
vsftpdneeds these in a binary format.sudo db4o_load -T -f /etc/vsftpd/vuser_pass.txt -u 0 -g 0 /etc/vsftpd/vuser_pass.dbdb4o_loadis the tool to create Berkeley DB files.-Ttells it to read from a text file.-f /etc/vsftpd/vuser_pass.txtspecifies your input text file.-u 0 -g 0sets the user and group ID of the database owner to root. This is important for permissions./etc/vsftpd/vuser_pass.dbis the output binary database file.
-
Configure
vsftpdto use virtual users. Edit yourvsftpd.conffile (usually located at/etc/vsftpd.conf).# Enable local users (even though we're using virtual ones, this is a dependency) local_enable=YES # Guest login support guest_enable=YES guest_username=ftpuser # Use pam_userdb for authentication pam_service_name=vsftpd_virtual # Allow anonymous access (needed for guest login by default, but we'll restrict it) anonymous_enable=NO # Hide the real user's home directory (if guest_username is used) local_umask=077 write_enable=YES chroot_local_user=YES allow_writeable_chroot=YESguest_enable=YESandguest_username=ftpuser: This tellsvsftpdto treat all authenticated users as "guests" and map them to a single real system user (ftpuserin this case). Thisftpuserneeds to exist on the system, but it doesn’t need a shell or a home directory that’s directly accessible. Its primary purpose is to own the files that virtual users will access.pam_service_name=vsftpd_virtual: This points to the PAM configuration file we’ll create next.
-
Configure PAM. Create a new PAM configuration file, for example,
/etc/pam.d/vsftpd_virtual.auth required pam_userdb.so db=/etc/vsftpd/vuser_pass account required pam_userdb.so db=/etc/vsftpd/vuser_passpam_userdb.so: This is the module that reads from our.dbfile.db=/etc/vsftpd/vuser_pass: This tellspam_userdbto use thevuser_pass.dbfile (it assumes the.dbextension).
-
Create the system user for guest mapping.
sudo useradd -m -d /home/ftpuser -s /sbin/nologin ftpuser-m: Creates the home directory.-d /home/ftpuser: Sets the home directory.-s /sbin/nologin: Prevents this user from logging in directly via SSH or other shell-based methods.
-
Set up directory permissions. The
ftpuserneeds to own the directories that virtual users will interact with. We also need to ensure thatvsftpdcan chroot users into their designated directories.Let’s say you want
client_uploaderto only access/var/www/html/images.sudo mkdir -p /var/www/html/images sudo chown ftpuser:ftpuser /var/www/html/images sudo chmod 755 /var/www/html/imagesNow, in
vsftpd.conf, you’ll need to map the virtual user to this directory.vsftpddoesn’t have a direct per-user directory mapping in the main config for virtual users. Instead, you typically usechroot_local_user=YESand then potentially alocal_rootdirective if you want to control which directory theguest_usernamemaps to, or you rely on the system’s understanding of theguest_username’s home directory.A more granular approach involves
user_config_dir. Create a directory for user-specific configurations:user_config_dir=/etc/vsftpd/user_confThen, create a file inside this directory named after your virtual username:
/etc/vsftpd/user_conf/client_uploaderlocal_root=/var/www/html/images write_enable=YES anon_world_readable_only=NOlocal_root=/var/www/html/images: This is the key. It tellsvsftpdthat whenclient_uploaderlogs in, their "root" directory should be/var/www/html/images.write_enable=YES: Allows uploads for this specific user.anon_world_readable_only=NO: Ensures that the files uploaded are not restricted to world-readable iflocal_umaskis set to a restrictive value.
-
Restart
vsftpd.sudo systemctl restart vsftpd
Now, when you try to connect with ftp://client_uploader:S3cureP@ssw0rd!@your_server_ip, you will be authenticated using the vuser_pass.db file, mapped to the ftpuser system account, and then chrooted into /var/www/html/images where you can upload files.
The surprising thing about virtual users is how they leverage existing system mechanisms (like PAM and a single, non-login system user) to create a highly isolated and manageable user base without cluttering your /etc/passwd. The local_root directive within user-specific configuration files is what truly isolates each virtual user to their designated directory, making it feel like their own private space.
The next step you’ll likely encounter is managing different permissions or access levels for various virtual users, which often involves diving deeper into vsftpd’s more advanced configuration options or even exploring more sophisticated authentication backends.