## 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:

- `help`
  Lists all registered control actions and a brief description for each

- `insctrl`
  Reenable a disabled control action

- `lsctrl`
  Lists all enabled control actions and the modules providing them

- `rmctrl`
  Disable a control action

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, re-enable 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:

- `debug`
- `dump`
- `kick`
- `restart`
- `shutdown`
- `start`
- `status`
- `stop`
- `trace`

These actions provide basic 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-2019 The ProFTPD Project
All Rights Reserved