* http://www.debiansec.com/linux/services/ftp.html, 2004/9

contents
This tutorial is split up into the following topics: Appendixes:
introduction

This tutorial has actually being written because more and more people are trying to setup a ftp service, but mainly choose software with a bad security history like wu-ftpd for that task. My personal suggestion for a ftp server is vsftpd because of it's security, performance and stability.

We will be using virtual users here since they do not have real privileges - unlike real system users. For additional information please consult the faq.

installation

Before we can start with the real topic of this tutorial, we need to install vsftpd of course. Since we want to run vsftpd with virtual users and a per-user configuration we require at least version 1.1.0 of vsftpd. I have been using a backport of vsftpd 1.2.1-1 when writing this tutorial.

If your distribution is Debian/GNU Linux, you need to either backport it yourself or use my backport of vsftpd, since currently vsftpd 1.0.0-2 is in stable and 1.2.1-1 in testing. For other distributions you should check with your distribution if you can rely on a pre-built vsftpd.

base configuration

We will not start using a bloated standard configuration and adopt that to our needs - which is the way most people set up their services - no, we will configure from scratch changing the default values only where required:

/etc/vsftpd.conf (without chmod capabilities)

# =========================================================================
# base configuration
# -------------------------------------------------------------------------
anon_world_readable_only=NO
anonymous_enable=NO
chroot_local_user=YES
guest_enable=YES
guest_username=ftp
hide_ids=YES
listen=YES
listen_address=192.168.0.1
local_enable=YES
max_clients=100
max_per_ip=1
nopriv_user=ftp
pam_service_name=ftp
pasv_max_port=65535
pasv_min_port=64000
session_support=NO
use_localtime=YES
user_config_dir=/etc/vsftpd/users
userlist_enable=YES
userlist_file=/etc/vsftpd/denied_users
xferlog_enable=YES
# -------------------------------------------------------------------------


# =========================================================================
# ftp settings
# -------------------------------------------------------------------------
anon_umask=0027
async_abor_enable=YES
connect_from_port_20=YES
dirlist_enable=NO
download_enable=NO
# =========================================================================

The above configuration in combination with the default values of vsftpd provides a pretty secure default configuration, which we will then override on a per-user basis.

However if you require the capability to chmod then the above configuration will not work since this is not allowed for anonymous users. You should only use the configuration file below if you do really require chmod capabilites. You would require chmod capabilites for instance when the users should be able to change the permissions of "sensitive" information from the default umask you have specified in the per-user configuration.

/etc/vsftpd.conf (with chmod capabilities)

# =========================================================================
# base configuration
# -------------------------------------------------------------------------
anonymous_enable=NO
chroot_local_user=YES
guest_enable=YES
guest_username=ftp
hide_ids=YES
listen=YES
listen_address=192.168.0.1
local_enable=YES
max_clients=100
max_per_ip=1
nopriv_user=ftp
pam_service_name=ftp
pasv_max_port=65535
pasv_min_port=64000
session_support=NO
use_localtime=YES
user_config_dir=/etc/vsftpd/users
userlist_enable=YES
userlist_file=/etc/vsftpd/denied_users
virtual_use_local_privs=YES
xferlog_enable=YES
# -------------------------------------------------------------------------


# =========================================================================
# ftp settings
# -------------------------------------------------------------------------
async_abor_enable=YES
connect_from_port_20=YES
dirlist_enable=NO
download_enable=NO
local_umask=0027
# =========================================================================

Now we need to:

  • create directory /etc/vsftpd
  • create directory /etc/vsftpd/users
  • write list of denied users to /etc/vsftpd/denied_users

I suggest adding every system user in /etc/vsftpd/denied_users so no system user is asked for submisson of the password in plaintext. Use cat /etc/passwd | cut -d ":" -f 1 | sort > /etc/vsftpd/denied_users for creating that file.

Now when a user, who is listed in /etc/vsftpd/denied_users attempts to login, the session will be terminated before prompting for the password like illustrated below:

example ftp session for denied user

Connected to 192.168.0.1.
220 (vsFTPd 1.2.0)
Name (192.168.0.1:root): root
530 Permission denied.
Login failed.
ftp> quit
221 Goodbye.
xinetd vs. standalone

If you like to use the power of xinetd, for instance to restrict the usage of the ftp server to a specified time range or a couple of ip addresses, you can launch vsftpd from xinetd.

For that purpose you will require to change the base configuration, in detail remove the listen and listen_address configuration option and configure your xinetd service:

/etc/xinetd.d/ftp

service ftp
{
	banner_fail	= /etc/vsftpd/busy_banner
	disable		= no
	instances		= 100
	log_on_failure	+= HOST
	log_on_success	+= PID HOST DURATION
	no_access		= 192.168.0.3
	only_from		= 192.168.0.0/28
	per_source	= 2
	server		= /usr/sbin/vsftpd
	socket_type	= stream
	user		= root
	wait		= no
}

The above configuration will of course need to be adjusted for your needs, like you probably want to limit the number of concurrent sessions (instances) even more or ban a couple of subnetworks (no_access).

The banner_fail file could look like:

/etc/vsftpd/busy_banner
421 Server busy, please try again later!
PAM configuration

After providing the username and verifiying it is not contained in /etc/vsftpd/denied_users, we still can not login since we have nothing left to authentificate against left - assuming our /etc/vsftpd/denied_users always contains all usernames from /etc/passwd.

Therefore we now need to configure our real authentification which will be based upon PAM. As example we can authentificate against a username/password file in common database format:

/etc/pam.d/ftp

auth    required /lib/security/pam_userdb.so db=/etc/vsftpd/accounts
account required /lib/security/pam_userdb.so db=/etc/vsftpd/accounts
creating virtual users (PAM)

Before being able to login, we need to create a valid user. Depending on the PAM authentification backend this steps could vary. For instance when using a database as authentification backend you would require addind that user to the specified table.

If you would like to follow from the above PAM sample configuration, you will need the db_load program for creating the file in common database format. When using Debian just apt-get install libdb3-util. Afterwards you need to create a file which contains the login and on the next line the password:

sample accounts.tmp (for building accounts.db)
user1
password_for_user1
user2
password_for_user2

After creating the accounts.tmp, which is just a list of usernames and passwords, you need to build the database with db3_load -T -t hash -f accounts.tmp /etc/vsftpd/accounts.db. Afterwards you can erase your accounts.tmp since it is no longer required - until you upgrade your username/password database. You should now set pretty restrictive permissions to the database: chmod 600 /etc/vsftpd/accounts.db

virtual user configuration

Depending on your base configuration, you have a different per-user configuration:

/etc/vsftpd/users/user1 (without chmod capabilites)

anon_mkdir_write_enable=YES
anon_other_write_enable=YES
anon_upload_enable=YES
dirlist_enable=YES
download_enable=YES
local_root=/home/user1
write_enable=YES

If you require chmod capabilities and have specified that in your base configuration, you will go for the following:

/etc/vsftpd/users/user1 (with chmod capabilites)

dirlist_enable=YES
download_enable=YES
local_root=/home/user1
write_enable=YES
appendix a: vsftpd configuration options

The configuration file takes a couple of options, which are partly explained shortly below. For more information please refer to the vsftpd.conf man page, where this information has been stripped off.

optiondescription
anon_umask The value that the umask for file creation is set to for anonymous users.
anon_mkdir_write_enable If set to YES, anonymous users will be permitted to create new directories under certain conditions. For this to work, the option write_enable must be activated, and the anonymous ftp user must have write permission on the parent directory.
anon_other_write_enable If set to YES, anonymous users will be permitted to perform write operations other than upload and create directory, such as deletion and renaming. This is generally not recommended but included for completeness.
anon_upload_enable If set to YES, anonymous users will be permitted to upload files under certain conditions. For this to work, the option write_enable must be activated, and the anonymous ftp user must have write permission on desired upload locations.
anon_world_readable_only When enabled, anonymous users will only be allowed to download files which are world readable. This is recognising that the ftp user may own files, especially in the presence of uploads.
anonymous_enable Controls whether anonymous logins are permitted or not.
async_abor_enable When enabled, a special FTP command known as "async ABOR" will be enabled. Only ill advised FTP clients will use this feature. Addtionally, this feature is awkward to handle, so it is disabled by default. Unfortunately, some FTP clients will hang when cancelling a transfer unless this feature is available, so you may wish to enable it.
chroot_local_user If set to YES, local users will be placed in a chroot() jail in their home directory after login.
connect_from_port_20 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.
dirlist_enable If set to NO, all directory list commands will give permission denied.
download_enable If set to NO, all download requests will give permission denied.
guest_enable 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.
guest_username This setting is the real username which guest users are mapped to.
hide_ids If enabled, all user and group information in directory listings will be displayed as "ftp".
listen 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.
listen_address If vsftpd is in standalone mode, the default listen address (of all local interfaces) may be overridden by this setting. Provide a numeric IP address.
local_enable Controls whether local logins are permitted or not. If enabled, normal user accounts in /etc/passwd may be used to log in.
local_root This option represents a directory which vsftpd will try to change into after a local (i.e. non-anonymous) login. Failure is silently ignored.
local_umask The value that the umask for file creation is set to for local users.
max_clients If vsftpd is in standalone mode, this is the maximum number of clients which may be connected. Any additional clients connecting will get an error message.
max_per_ip If vsftpd is in standalone mode, this is the maximum number of clients which may be connected from the same source internet address. A client will get an error message if they go over this limit.
nopriv_user This is the name of the user that is used by vsftpd when it want 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.
pam_service_name This string is the name of the PAM service vsftpd will use.
pasv_max_port The maximum port to allocate for PASV style data connections. Can be used to specify a narrow port range to assist firewalling.
pasv_min_port The minimum port to allocate for PASV style data connections. Can be used to specify a narrow port range to assist firewalling.
session_support This controls whether vsftpd attempts to maintain sessions for logins. If vsftpd is maintaining sessions, it will try and update utmp and wtmp. It will also open a pam_session if using PAM to authenticate, and only close this upon logout. You may wish to disable this if you do not need session logging, and you wish to give vsftpd more opportunity to run with less processes and / or less privilege.
use_localtime If enabled, vsftpd will display directory listings with the 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.
user_config_dir 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=/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.
userlist_enable 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.
userlist_file This option is the name of the file loaded when the userlist_enable option is active.
virtual_use_local_privs 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).
write_enable This controls whether any FTP commands which change the filesystem are allowed or not. These commands are: STOR, DELE, RNFR, RNTO, MKD, RMD, APPE and SITE.
xferlog_enable If enabled, a log file will be maintained detailling uploads and downloads.
appendix b: xinetd configuration options

The xinetd configuration file takes a couple of different options, which are explained shortly below. For more information please refer to the xinetd.conf man page, where this information has been stripped off.

optiondescription
banner_fail

Takes the name of a file to be splatted at the remote host when a connection to that service is denied. This banner is printed immediately upon denial of access.

This is useful for informing your users that they are doing something bad and they shouldn't be doing it anymore.

disable This is boolean "yes" or "no".
This will result in the service being disabled and not starting.
instances

Determines the number of servers that can be simultaneously active for a service (the default is no limit). The value of this attribute can be either a number or UNLIMITED which means that there is no limit.

log_on_failure

Determines what information is logged when a server cannot be started (either because of a lack of resources or because of access control restrictions). The service id is always included in the log entry along with the reason for failure.

log_on_success

Determines what information is logged when a server is started and when that server exits (the service id is always included in the log entry).

no_access

Determines the remote hosts to which the particular service is unavailable. Its value can be specified in the same way as the value of the only_from attribute. These two attributes determine the location access control enforced by xinetd. If none of the two is specified for a service, the service is available to anyone. If both are specified for a service, the one that is the better match for the address of the remote host determines if the service is available to that host (for example, if the only_from list contains 128.138.209.0 and the no_access list contains 128.138.209.10 then the host with the address 128.138.209.10 can not access the service).

only_from

Determines the remote hosts to which the particular service is available. Its value is a list of IP addresses which can be specified in any combination of the following ways:

  • a numeric address in the form of %d.%d.%d.%d. If the rightmost components are 0, they are treated as wildcards (for example, 128.138.12.0 matches all hosts on the 128.138.12 subnet). 0.0.0.0 matches all Internet addresses.
  • a factorized address in the form of %d.%d.%d.{%d,%d,...}. There is no need for all 4 components (i.e. %d.%d.{%d,%d,...%d} is also ok). However, the factorized part must be at the end of the address.
  • a network name (from /etc/networks).
  • a host name. When a connection is made to xinetd, a reverse lookup is performed, and the canonical name returned is compared to the specified host name. You may also use domain names in the form of .domain.com. If the reverse lookup of the client's IP is within .domain.com, a match occurs.
  • an ip address/netmask range in the form of 1.2.3.4/32.
per_source

Takes an integer or "UNLIMITED" as an argument. This specifies the maximum instances of this service per source IP address.

server Determines the program to execute for this service
server_args Determines the arguments passed to the server.
socket_type

Possible values for this attribute include:

dgram datagram-based service
raw service that requires direct access to IP
seqpacket service that requires reliable sequential datagram transmission
stream stream-based service
user

Determines the uid for the server process. The user name must exist in /etc/passwd. This attribute is ineffective if the effective user ID of xinetd is not super-user.

wait

This attribute determines if the service is single-threaded or multi-threaded. If its value is yes the service is single-threaded; this means that xinetd will start the server and then it will stop handling requests for the service until the server dies. If the attribute value is no, the service is multi-threaded and xinetd will keep handling new service requests.

appendix c: faq

If you have a question regarding vsftpd which has not been answered in any section of this tutorial, feel free to email your question to website@linux-corner.net.

Frequently asked questions which have already been answered:

Firewalling information
The configuration options pasv_max_port and pasv_min_port assist you in firewalling:
  • INPUT chain:
    • tcp
      new/established
      source port: 1024 - 65535
      destination port: 21
    • tcp
      new/established/related
      destination port: pasv_min_port - pasv_max_port

  • OUTPUT chain:
    • tcp
      related/established
      source port: 20
      destination port: 1024 - 65535
    • tcp
      established
      source port: 21
      destination port: 1024 - 65535
    • tcp
      established
      source port: pasv_min_port - pasv_maxport
      destination port: 1024 - 65535

With the above information (protocol, connection status, ports) you should be able to write the iptables ruleset. Your kernel needs to support connection tracking though; additionally you will require to use the ip_conntrack_ftp module of netfilter.

Where can I find binary packages for other distributions than Debian?
If you are not using Debian you are probably looking for RPM packages. I suggest you take a look at rpmseek.com. Here you can probably also find the db_load program used for creating virtual users with the common database format.
Why do you prefer vsftpd?
I could quote that mostly from the vsftpd website. I am using vsftpd for it's excellent points in the following areas:
  • security
  • stability
  • performance
I am not really quoting it, I have verified all of the above points and I would not be using vsftpd if it would not perform that excellent. I know that there are other ftp servers out that are said to be secure, however until now I have not verified others.
Why should one disallow system users?

The typical system user has a lot of more privileges than required for the standard FTP user - like shell access. Granting each ftp user the privileges of a system user will definately affect system security, also since FTP transmitts passwords in cleartext. You can limit your real system users to not being able to login, however in that case there is no reason why you should not go for virtual users.

Immagine the root user logs in at the ftp server, the password is transmitted in cleartext and it is PRETTY easy to sniff out the password with standard tools. Do you really want to share passwords for users with probably unneccessary privileges with the whole world?


top
Make payments with PayPal - its fast, free and secure!  Valid XHTML 1.0! Valid CSS!
Last update: 2004/09/19
Copyright © 2003, 2004 by Markus Welsch. Distribution requires written permission of the author!