ProFTPD: Quotas

A busy FTP server handles hundreds to thousands of files belonging to hundreds to thousands of users. There is not an infinite capacity on the disks of the server, however, and eventually those files will take up too much space. Thus many sites have a pressing need to set limits on just how much can be stored on the server by their users.

Most Unix systems have support for OS- or fileystem-level quotas. These have the advantage of being transparent to applications like proftpd, which means that the applications need not worry about maintaining and enforcing quotas. The kernel/filesystem will handle that. One disadvantage these OS-level quotas have, though, is that they are strictly tied to OS and/or filesystem, and not all Unix kernels and filesystems are the same. They also rely on each user having their own separate user ID. In the case of virtual users for proftpd, it is possible for many users to have the same user ID, which causes problems for these traditional Unix quotas.

For these reasons, the mod_quotatab module was developed for ProFTPD. This module, being part of the application, applies to all the Unix kernels and filesystems which support ProFTPD, and easily handles virtual users. The mod_quotatab documentation covers how to configure proftpd for quotas.

Note: This howto is a work-in-progress. Please contact me with suggestions, questions, requests, etc for what you would like to see covered here. Thanks!

Example Configuration
Here is an example mod_quotatab configuration for supporting quotas via file tables and SQL tables. This is an example only.

  <IfModule mod_quotatab.c>
    QuotaEngine on
    QuotaLog /var/log/ftpd/quota.log

    # For more information on using files for storing the limit and tally
    # table quota data, please see the mod_quotatab_file documentation.
    <IfModule mod_quotatab_file.c>
      QuotaLimitTable file:/etc/ftpd/ftpquota.limittab
      QuotaTallyTable file:/etc/ftpd/ftpquota.tallytab

    # For more information on using a SQL database for storing the limit and
    # tally table quota data, please see the mod_quotatab_sql documentation
    <IfModule mod_quotatab_sql.c>
      SQLNamedQuery get-quota-limit SELECT "* FROM quotalimits WHERE name = '%{0}' AND quota_type = '%{1}'"
      SQLNamedQuery get-quota-tally SELECT "* FROM quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'"
      SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name = '%{6}' AND quota_type = '%{7}'" quotatallies
      SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" quotatallies

      QuotaLock /var/lock/ftpd.quotatab.lock
      QuotaLimitTable sql:/get-quota-limit
      QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

Frequently Asked Questions

Question: How do I set the disk space limit for a user?
Answer: The short answer is to use the "upload bytes limit" setting.

It's true that the various limits maintained by mod_quotatab, for either bytes or files (or both), are confusing. When designing the module, I anticipated administrators wanting to limit download as well as upload transfers; in reality, most administrators wish to limit the disk space for their users. The bytes uploaded limit doubles as the disk space limit, for there is no effective difference between them; any bytes uploaded via FTP are automatically stored on disk.

Question: mod_quotatab only tracks changes made using proftpd, and my users can add/remove files other ways. What am I supposed to do?
Answer: As the mod_quotatab documentation states, the module was designed only to track changes done via FTP; it made implementation of quotas simpler.

This question is also often posed as "Why can't mod_quotatab just calculate the amount of space used by a user?" The module could do this, but there are some considerations with this kind of approach.

The primary consideration is the time cost of having to recurse a directory. If the directory is relatively small, the time needed is small. For very large/deep directories, however, the time needed to do the scan could be long, possibly long enough for users to notice and complain. Another complication is the disk space used by a given user is not confined to their home directory. Many sites have home directories for users, and have a shared directory that all users can use. Somehow mod_quotatab would need to know to scan these shared directories.

As of proftpd-1.3.1rc1, the mod_quotatab supports this feature via the ScanOnLogin parameter of the QuotaOptions directive.

Question: If I use this ScanOnLogin option, how do I tell it to ignore certain files?
Answer: Use the new QuotaExcludeFilter directive. For example, if you wanted to exclude all files that ended in ".log" from the automatic quota calculation, you might use:

  <IfModule mod_quotatab.c>

     # Automatically calculate the used quota on login
     QuotaOptions ScanOnLogin

     # Do not include files ending in ".log" in the automatically
     # calculated quota
     QuotaExludeFilter \.log$

Question: If mod_quotatab does not automatically scan for disk space usage, how will it know about my existing users?
Answer: By default, mod_quotatab indeed knows nothing about what disk space is already occupied by your users; the tally table starts out blank.

To help address this, there is a Perl script:
which will display the number of bytes owned by a given user (or group) within a list of directories. The parameters needed for running are documented here.

Once you've run to find out the current usage for your users, you can enter those numbers into your tally table. Unfortunately cannot read your proftpd.conf file to know whether you are using a file- or SQL-based tally table, so it cannot automatically update your tally table.

Question: How can I set a default quota for all of my users?
Answer: For this, you can use the QuotaDefault directive, which first appeared in ProFTPD 1.3.5rc1.

Question: What is a "tally table"?
Answer: Tally tables, and limit tables, are covered in the mod_quotatab documentation.

Question: How do I construct the limit and tally files for file-based quotas?
Answer: There is a Perl script called ftpquota which can create the necessary files. This script can also be found under the contrib/ directory of the proftpd source distribution.

Question: Is there a SQL script for the SQL quota tables used by mod_quotatab_sql?
Answer: No. However, the mod_quotatab_sql documentation contains example schema for the necessary tables.

Question: How do I set a limit on the size of a directory?
Answer: Currently, you cannot.

Traditional Unix quotas are implemented in terms of ownership: the thing that counts is not where a file is located on the filesystem, but which user and/or group owns the file. Asking about directory quotas assumes a different basis for quotas, based on location rather than ownership (such quotas are often called tree-based quotas). The mod_quotatab module followed the example of traditional Unix quotas, but I have started designing how location-based quotas might be implemented.

Question: Why isn't mod_quotatab updating my tally table?
Answer: It depends.

One possibility is that the per session flag in the limit in effect is set to true, which means that the limits will only be applied to this session. When this happens, mod_quotatab will not update the tally table.

Another possibility is that your configured limits are "unlimited" (i.e. zero). As it states in the mod_quotatab documentation:

  For any quota limit that is set as "unlimited", mod_quotatab will not keep the tally.
  Many site administrators might want this ability, for accounting purposes. However,
  that ability is outside of the intended design of this module; other logging modules
  are much better suited for accounting purposes (e.g. mod_sql's SQLLog directive).

If the above cases are not applicable, then consider looking in the QuotaLog file for more information.

Question: Can I use mod_quotatab to set monthly quotas?
Answer: Sort of. mod_quotatab itself has no concept of time, so this sort of capability is not built-in. However, it can be approximated by having some other process, such as a cron job, periodically (such as once a month) clear the tally table, yielding the effect of monthly quotas.

Question: How can users see their current quota?
Answer: There are two ways to show the current quota to users. There is the SITE QUOTA command. And there are certain Display variables that are supported by the mod_quotatab module.

Question: What if I want to set limits on the size of individual files being transferred?
Answer: For this, you do not need the mod_quotatab module. ProFTPD has the MaxRetrieveFileSize and MaxStoreFileSize directives.

Question: Why do I see the following error?

  QuotaLimitTable: unsupported table source type: 'sql'
Answer: The mod_quotatab module acts as a general quota managing front-end; it relies on other backend modules to handle the specifics of storage formats. Every backend module (e.g. mod_quotatab_file, mod_quotatab_sql, mod_quotatab_ldap) registers a supported table type with the main mod_quotatab module. The error above indicates that the mod_quotatab_sql module has not been compiled/loaded into proftpd.

Question: How do group quotas work? If a user is in multiple groups, what happens?
Answer: The mod_quotatab module searches for applicable quotas, by type, in the following order:

Thus if there is a user-specific quota, it will be used; the search stops there. The mod_quotatab module does not combine multiple different quotas.

What happens, then, if a user belongs to multiple different groups, and there are different quotas set for those different groups? First, the primary group of the user is checked; this is the group that is included, for example, in the entry for the user in the /etc/passwd file. If no quota for the primary group is found, the module then checks against each of the user's supplemental groups. The order in which these groups are checked is dependent up on the order in which the groups are returned from the auth module providing them, e.g. mod_sql, mod_ldap, or perhaps mod_auth_unix.

Question: Does mod_quotatab handle the case where a client might append to/overwrite an existing file, in terms of tracking bytes?
Answer: Yes. The mod_quotatab module checks the size of the file before the upload/append starts, and then checks the size of the file again after the upload/append completes. The difference in file sizes is used for tracking the byte-related tallies.

Question: I configured limits for my users such that only the transfer bytes limit is set. On uploads, my transfer bytes tally is incremented properly. But when I delete a file, the transfer bytes tally is not decremented. Is this a bug?
Answer: No, it's not a bug.

Deleting a file does not count as transferred bytes (i.e. it does not pertain to the bytes_xfer limit); it counts as uploaded bytes (i.e. the bytes_in limit). And since there is only a limit, in this case, on the transferred bytes, the mod_quotatab module ends up not changing any tally value for a delete. More details on this can be found in Bug#2897.

Question: Why does the ftpquota tool treat limit values of zero as "unlimited"?

  $ ftpquota --create-table --type=limit
  $ ftpquota --add-record --type=limit --name=tj --quota-type=user --files-download=0
  $ ftpquota --show-records --type=limit
    Name: tj
    Quota Type: User
    Per Session: False
    Limit Type: Hard
      Uploaded bytes: unlimited
      Downloaded bytes: unlimited
      Transferred bytes:  unlimited
      Uploaded files: unlimited
      Downloaded files: unlimited
      Transferred files:  unlimited
I want to use this to prevent a user from uploading/download files.

Answer: If you wish to prevent users from uploading or downloading, then you should use <Limit> sections in your ProFTPD configuration; that is what they are designed to do. The ftpquota tool was explicitly designed to not allow being used to prevent uploads/downloads; it is for managing quotas.

© Copyright 2017 The ProFTPD Project
All Rights Reserved