Recently I had a need to setup ftp access so that other users can manage a website. There is a broad acceptance that whenever you get a website on a *nix box you will have an FTP login. Well this is only the case if the whole eco system has been setup that way. Here I've documented my learnings on the process an at the end you should hopefully be able to

  • Setup VSFTPD
    • Enable SSL
    • Support Virtual Users (users with no ssh/shell login)

No guarantee to this, but you might grow a new patch of linux beardyness.

Install VSFTPD

This is pretty standard ubuntu setup stuff

sudo apt-get install vsftpd

Create a no privledge user

VSFTPD needs a no privledge user to do it's unprivledged things. This shouldn't be root or any account with access. But it needs to be a physical account on your machine

useradd --home /home/vsftpd --gid nogroup \
        -m --shell /bin/false vsftpd


Here is a commented conf file for VSFTPD. The tricky part is is that configuration values have slightly different meanings if other flags are enabled. This is probably one of the places which can send you down a rabbit hole of WHY IS THIS BROKEN?!?!

# If enabled, vsftpd will run in standalone mode. This means that 
# vsftpd must not be run from an inetd of some kind. Instead, the 
# vsftpd executable is run once directly. vsftpd itself will then 
# take care of listening for and handling incoming connections.
# Default: YES

# Controls whether anonymous logins are permitted or not. If
# enabled, both the usernames ftp and anonymous are recognised
# as anonymous logins.
# Default: YES

# Controls whether local logins are permitted or not. If
# enabled, normal user accounts in /etc/passwd (or wherever your
# PAM config references) may be used to log in. This must be
# enable for any non-anonymous login to work, including virtual
# users.
# Default: NO

# This controls whether any FTP commands which change the
# filesystem are allowed or not. These commands are: STOR, DELE,
# Default: NO

# The value that the umask for file creation is set to for local
# users. NOTE! If you want to specify octal values, remember the
# "0" prefix otherwise the value will be treated as a base 10 #integer!
# Default: 077

# If enabled, users of the FTP server can be shown messages when
# they first enter a new directory. By default, a directory is
# scanned for the file .message, but that may be overridden with
# the configuration setting message_file.
# Default: NO (but the sample config file enables it)

# If enabled, vsftpd will display directory listings with the 
# time in your local time zone. The default is to display GMT. 
# The times returned by the MDTM FTP command are also affected by 
# this option.
# Default: NO

# If enabled, a log file will be maintained detailling uploads 
# and downloads. By default, this file will be placed at 
# /var/log/vsftpd.log, but this location may be overridden using
# the configuration setting vsftpd_log_file.
# Default: NO (but the sample config file enables it)

# This option is the name of the file to which we write the wu-ftpd
# style transfer log. The transfer log is only written if the 
# option xferlog_enable is set, along with xferlog_std_format. 
# Alternatively, it is written if you have set the option 
# dual_log_enable.
# Default: /var/log/xferlog 

# This controls whether PORT style data connections use port 20
# (ftp-data) on the server machine. For security reasons, some 
# clients may insist that this is the case. Conversely, disabling 
# this option enables vsftpd to run with slightly less privilege.
# Default: NO (but the sample config file enables it)

# If set to YES, local users will be (by default) placed in a 
# chroot() jail in their home directory after login. Warning: This 
# option has security implications, especially if the users have 
# upload permission, or shell access. Only enable if you know 
# what you are doing. Note that these security implications are 
# not vsftpd specific. They apply to all FTP daemons which offer 
# to put local users in chroot() jails.
# Default: NO

# If activated, you may provide a list of local users who are 
# placed in a chroot() jail in their home directory upon login. 
# The meaning is slightly different if chroot_local_user is set to 
# YES. In this case, the list becomes a list of users which are 
# NOT to be placed in a chroot() jail. By default, the file 
# containing this list is /etc/vsftpd.chroot_list, but you may 
# override this with the chroot_list_file setting.
# Default: NO

# If enabled, vsftpd will load a list of usernames, from the 
# filename given by userlist_file. If a user tries to log in using 
# a name in this file, they will be denied before they are asked 
# for a password. This may be useful in preventing cleartext 
# passwords being transmitted. See also userlist_deny.
# Default: NO

# This option is examined if userlist_enable is activated. If 
# you set this setting to NO, then users will be denied login 
# unless they are explicitly listed in the file specified by 
# userlist_file. When login is denied, the denial is issued before 
# the user is asked for a password.
# Default: YES

# This option is the name of the file loaded when the 
# userlist_enable option is active.
# Default: /etc/vsftpd.user_list

# If enabled, virtual users will use the same privileges as 
# local users. By default, virtual users will use the same
# privileges as anonymous users, which tends to be more
# restrictive (especially in terms of write access).
# Default: NO

# If enabled, all non-anonymous logins are classed as "guest" 
# logins. A guest login is remapped to the user specified in the 
# guest_username setting.
# This has be to enabled to support virtual users
# Default: NO

# This option is useful is conjunction with virtual users. It is 
# used to automatically generate a home directory for each 
# virtual user, based on a template. For example, if the home 
# directory of the real user specified via guest_username is 
# /home/virtual/$USER, and user_sub_token is set to $USER, then 
# when virtual user fred logs in, he will end up (usually 
# chroot()'ed) in the directory /home/virtual/fred. This option 
# also takes affect if local_root contains user_sub_token.
# Default: (none)

# This powerful option allows the override of any config option 
# specified in the manual page, on a per-user basis. Usage is
# simple, and is best illustrated with an example. If you set 
# user_config_dir to be /etc/vsftpd_user_conf and then log on as 
# the user "chris", then vsftpd will apply the settings in the 
# file /etc/vsftpd_user_conf/chris for the duration of the 
# session. The format of this file is as detailed in this manual 
# page! PLEASE NOTE that not all settings are effective on a 
# per-user basis. For example, many settings only prior to the 
# user's session being started. Examples of settings which will 
# not affect any behviour on a per-user basis include 
# listen_address, banner_file, max_per_ip, max_clients, 
# xferlog_file, etc.
# Default: (none)

# This is the name of the user that is used by vsftpd when it 
# wants to be totally unprivileged. Note that this should be a 
# dedicated user, rather than nobody. The user nobody tends to be 
# used for rather a lot of important things on most machines.
# Default: nobody

# This option represents a directory which vsftpd will try to 
# change into after a local (i.e. non-anonymous) login. Failure 
# is silently ignored.
# Default: (none)

# If enabled, all user and group information in directory
# listings will be displayed as "ftp".
# Default: NO

# This string is the name of the PAM service vsftpd will use.
# Default: ftp


Choosing the right PAM service

Ok if you are like WTF is PAM, here it is in an overly small nutshell, it provideds a common way for local unix processes to establish lookups against various passwd / userdb etc.. type user stores.

The neat thing here is that you can use PAM to tie together authentication databases across multiple processes. You can specify that one daemon should use the system passwd file /etc/passwd for local accounts, or in the case below, we use it to specify "virtual" users that we don't want to create local accounts for.

This gets a little tricky because depending on the VSTPD example you find out there some people use the userdb format, some folks use the passwd format, and some others want to use something else.

When a conf file points to a pam service as for example in the vsftp.conf below there is this line


This is just a reference to /etc/pamd.d/vsftpd.virtual which is a file that holds PAM mapping details

auth  required  /lib/x86_64-linux-gnu/security/ db=/etc/vsftpd/virtualusers
account  required  /lib/x86_64-linux-gnu/security/ db=/etc/vsftpd/virtualusers

The above file tells the PAM system that auth and account information comes from /etc/vsftpd/virtualusers file. This file is created via whatever userdb/passwd file format you are using. In this case I'm using userdb.

Poke around in /etc/pam.d the files in there are all text files that show various ways to cascade lookup details from other providers by the use of includes so you can create authentication chains that are custom (or common)

A wikipedia has a bunch of links about it here: Pluggable Authentication Module

This will set get you configured. I've broken the post up into a series.

Next up Adding users to your virtual VSFTPD setup