ProFTPD: Controls and mod_ctrls


What Controls Are
ProFTPD version 1.2.10rc1 introduced a new capability: Controls. Controls are a way to communicate directly with a standalone proftpd daemon while it is running. This provides administrators a way to alter the daemon's behavior in real time, without having to restart the daemon and have it re-read its configuration. Changing log levels, kicking connected clients, disabling individual virtual servers, etc. are all possible. The Controls functionality includes an API that third-party modules can use to add new control actions.

The functionality involves a client and a server communicating over a Unix domain socket, using a simple text-based protocol. A new program, ftpdctl, is distributed with ProFTPD; ftpdctl is a Controls client. The server side of the Controls functionality is the mod_ctrls module, which is compiled into a proftpd daemon when the --enable-ctrls configure option is used. Note, however, that the Controls functionality only works for proftpd daemons whose ServerType is standalone; proftpd daemons run via inetd/xinetd cannot support Controls and the ftpdctl program.

Configuring mod_ctrls
Here's an example configuration for mod_ctrls:

  <IfModule mod_ctrls.c>
    ControlsEngine        on
    ControlsACLs          all allow group ftpadm
    ControlsMaxClients    2
    ControlsLog           /var/log/proftpd/controls.log
    ControlsInterval      5
    ControlsSocket        /tmp/ctrls.sock
    ControlsSocketOwner   ftpd ftpd
    ControlsSocketACL     allow group ftpadm
  </IfModule>
The first configuration directive you will want to use is the ControlsEngine directive, which enables the processing of Controls requests by the engine within the mod_ctrls engine.

When first configuring mod_ctrls and its modules, you will probably want to configure a ControlsLog. The mod_ctrls modules will log any errors they have to this file (unless the module has its own module-specific log), as well as all control requests made using ftpdctl. Like most proftpd log files, the directive should use the full path to the log file, and the file cannot be in a world-writeable directory.

The ControlsInterval directive configures how often mod_ctrls checks for connecting clients such as ftpdctl. Often when using ftpdctl, the system administrator may notice a long pause before ftpdct responds. This delay is probably caused by the configured ControlsInterval. A related directive is ControlsMaxClients, which sets how many clients are handled at one time. If more than the configured maximum number of clients are attempting to send control requests, mod_ctrls will wait to handle the remaining clients until its next check.

The ControlsSocket directive is used to configure the path to the Unix domain socket on which mod_ctrls should listen. This path should be in a place where ftpdctl users have access to it, and should have read and write permissions. The ControlsSocketOwner directive explicitly configures the ownership the created Unix domain socket file should have. This means that the configuration above:

  ControlsSocket        /tmp/ctrls.sock
  ControlsSocketOwner   ftpd ftpd
overrides the default location of the Unix domain socket, and tells mod_ctrls to use /tmp/ctrls.sock, and to make the socket owned by user ftpd and group ftpd. The default ownership of the socket is user root, group root.

Controls Access Control Lists
Communicating with the daemon process allows for some great features, but it also allows the potential to easily abuse the system. Thus, by default, all users including user root are denied access to all ftpdctl actions. Access for control actions must be explicitly granted. Also, be aware that the users and groups configured in these ACLs are system users and groups, not the virtual users and groups that proftpd may have been configured to use. The group will be only the primary group of the user; supplemental group memberships are currently ignored.

The mod_ctrls first checks whether the connecting client has been granted the ability to use the Unix domain socket itself. This access list is controlled by the ControlsSocketACL directive. By default, every Controls client can use the socket. Thus, in the example configuration above:

  ControlsSocketACL     allow group ftpadm
only the system group ftpadm would be allowed to send Controls requests to the Unix domain socket; all other users and groups will be denied.

Next, ACLs on the specific control action requested by the connecting client will be check. Most modules that use the Controls API will have their own module-specific directives for setting ACLs on the specific actions implemented by that module. For mod_ctrls, the ControlsACLs configuration directive is used. In the example configuration we see:

  ControlsACLs all allow group ftpadm
which allows only the system group ftpadm to use all of the control actions provided by mod_ctrls. If one wanted to allow all users to use all of the control actions, it would be:
  ControlsACLs all allow user *
However, if one wanted to specify separate ACLs for separate actions, then the ControlsACLs directive would be used multiple times, like so:
  ControlsACLs insctrl allow group wheel
  ControlsACLs lsctrl allow user *
  ControlsACLs rmctrl deny group ftp

Why is there such complexity for a simple client/server interaction? The primary answer is that the running proftpd daemon has a lot of privileges, and access to the running daemon should be strictly controlled. Allow few people to have access via control actions; those few should be able to use only the control actions necessary.

What Controls Do
The mod_ctrls module provides basic control actions, or commands that ftpdctl can send to the daemon:

Denied Actions Versus Disabled Actions
What is the difference between an action that has been denied via an ACL versus an action that has been disabled using the rmctrl action? A disabled action cannot be used by any client, regardless of any ACLs that have been configured for that action. Thus if a system administrator deemed that a particular control action should not be used by anyone, that action can be disabled. The rmctrl action can disable any action implemented by any module using the Controls API, not just actions provided by mod_ctrls. Once disabled, a given action can be re-enabled using the insctrl action. The insctrl and rmctrl actions, in addition to lsctrl, cannot themselves be disabled. For example, to disable an action called foo:

  # ftpdctl rmctrl foo
  ftpdctl: 'foo' control disabled
  # ftpdctl lsctrl
  ftpdctl: help (mod_ctrls.c)
  ftpdctl: insctrl (mod_ctrls.c)
  ftpdctl: lsctrl (mod_ctrls.c)
  ftpdctl: rmctrl (mod_ctrls.c)
Note that the foo action is not listed by lsctrl. Now, reenable that action:
  # ftpdctl insctrl foo
  ftpdctl: 'foo' control enabled
  # ftpdctl lsctrl
  ftpdctl: foo (mod_foo.c)
  ftpdctl: help (mod_ctrls.c)
  ftpdctl: insctrl (mod_ctrls.c)
  ftpdctl: lsctrl (mod_ctrls.c)
  ftpdctl: permit (mod_ban.c)
  ftpdctl: rmctrl (mod_ctrls.c)
And now the foo action, provided by the module mod_foo.c, appears in the lsctrl listing.

The following shows an example of attempting to disable the lsctrl action. It demonstrates the use of the -v command-line option for ftpdctl, for showing a verbose interaction with mod_ctrls:

  # ftpdctl -v lsctrl
  ftpdctl: adding "lsctrl" to reqargv
  ftpdctl: contacting server
  ftpdctl: sending control request
  ftpdctl: receiving control response
  ftpdctl: debug (mod_ctrls_admin.c)
  ...

  # ftpdctl rmctrl lsctrl

  # ftpdctl -v lsctrl
  ftpdctl: adding "lsctrl" to reqargv
  ftpdctl: contacting server
  ftpdctl: sending control request
  ftpdctl: receiving control response
  ftpdctl: access denied
The "access denied" message happens because of the special status of the lsctrl action which keeps it from being disablable.

Admin Controls
The mod_ctrls module, by itself, is rather unexciting. Other modules, such as mod_ctrls_admin, provide more interesting and useful control actions, including:

These actions provide basis administrative control over the running proftpd daemon; see the mod_ctrls_admin documentation for more details.

A basic mod_ctrls_admin configuration is:

  <IfModule mod_ctrls_admin.c>
    AdminControlsACLs all allow user *
  </IfModule>
will allows anyone to use any mod_ctrls_admin control action.

Here is another configuration, encompassing both mod_ctrls and mod_ctrls_admin:

  <IfModule mod_ctrls.c>
    ControlsEngine        on
    ControlsACLs          all allow group ftpadm
    ControlsMaxClients    2
    ControlsLog           /var/log/proftpd/controls.log
    ControlsInterval      5
    ControlsSocketACL     allow group ftpadm
    ControlsSocket        /tmp/ctrls.sock
    ControlsSocketOwner   ftpd ftpd

    <IfModule mod_ctrls_admin.c>
      AdminControlsACLs all allow user dave,bob,lisa
    </IfModule>
  </IfModule>
In this configuration, users dave, bob, and lisa are allowed to use all of the control actions supplied by mod_ctrls_admin. However, unless these users are members of group ftpadm, access will be denied. Only group ftpadm has been allowed access to the mod_ctrls socket itself by the ControlsSocketACL directive.

What about configuring an ACL for a given control that includes both users and groups? Use:

  AdminControlsACLs restart allow user dave,lisa
  AdminControlsACLs restart allow group ftpadm
What if user dave is not a member of group ftpadm? If configured, both user and group ACLs must deny the client. If either allows access, the client can use that control action.


© Copyright 2017 The ProFTPD Project
All Rights Reserved