How can I limit users to a particular directory tree?

For general open access you can use an <Anonymous> directive context block, possibly in combination with a UserPassword/AnonRequirePassword directive.

However if you wish to jail an entire group (or groups) of users, you can use the DefaultRoot directive. DefaultRoot lets you specify a root jailed directory (or '~' for the user's home directory), and an optional group-expression argument which can be used to control which groups of users the jail will be applied to. For example: ... <VirtualHost myhost.mynet.foo> DefaultRoot ~ ... </VirtualHost> This creates a configuration where all users who log into myhost.mynet.foo are jailed into their home directories (cannot chdir into a higher level directory). Alternatively, you could: ... <VirtualHost myhost.mynet.foo> DefaultRoot /u2/public users,!staff ... </VirtualHost>

In this example, all users who are members of group 'users', but not members of group "staff" are jailed into /u2/public. If a user does not meet the group-expression requirements, they login as per normal (not jailed, default directory is their home). You can use multiple DefaultRoot directives to create multiple jails inside the same directive context. If two DefaultRoot directives apply to the same user, ProFTPD arbitrarily chooses one (based on how the configuration file was parsed).

Security Implications

The DefaultRoot directive is implemented using the chroot(2) system call. This moves the "/" (or root) directory to a specified point within the file system and jails the user into this sub-tree. However this is not the holy grail of security, a chroot jail can be broken, it is not a trivial matter but it's nowhere near impossible. DefaultRoot should be used as part of a general system of security not the only security measure.

A more detailed http://www.bpfh.net/simes/computing/chroot-break.html on this subject and on the breaking of chroot jails has been written by Simon Burr

Non-root server issues

The chroot() system call will not work under a non-root ftp server process, the call requires root privileges. Without them it simply doesn't work, there doesn't appear to be any checking in the code of the uid/gid before calling chroot so using DefaultRoot in such a setup will cause the server to fail.