ProFTPD module mod_proxy

## ProFTPD module `mod_proxy`

The purpose of the `mod_proxy` module is to provide FTP proxying capabilities in `proftpd`, both reverse (or "gateway") proxying and forward proxying.

Installation instructions are discussed here. Note that `mod_proxy` requires ProFTPD 1.3.6rc2 or later. Detailed notes on best practices for using this module are here.

This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/).

This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).

The most current version of `mod_proxy` can be found at:

      https://github.com/Castaglia/proftpd-mod_proxy.git

## Author

Please contact TJ Saunders <tj at castaglia.org> with any questions, concerns, or suggestions regarding this module.

## Thanks

2015-08-24: Thanks to Michael Toth <mtoth at queldor.net> for helping test multiple iterations of `mod_proxy` with IIS servers.

## Directives

- ProxyDataTransferPolicy
- ProxyDatastore
- ProxyDirectoryListPolicy
- ProxyEngine
- ProxyForwardEnabled
- ProxyForwardMethod
- ProxyForwardTo
- ProxyLog
- ProxyOptions
- ProxyReverseConnectPolicy
- ProxyReverseServers
- ProxyRetryCount
- ProxyRole
- ProxySourceAddress
- ProxyTables
- ProxyTimeoutConnect
- ProxyTimeoutLinger
- ProxyTLSCACertificateFile
- ProxyTLSCACertificatePath
- ProxyTLSCARevocationFile
- ProxyTLSCARevocationPath
- ProxyTLSCertificateFile
- ProxyTLSCertificateKeyFile
- ProxyTLSCipherSuite
- ProxyTLSEngine
- ProxyTLSOptions
- ProxyTLSPreSharedKey
- ProxyTLSProtocol
- ProxyTLSTimeoutHandshake
- ProxyTLSTransferProtectionPolicy
- ProxyTLSVerifyServer

### ProxyDataTransferPolicy

Syntax: ProxyDataTransferPolicy client|active|passive|pasv|epsv|port|eprt
Default: ProxyDataTransferPolicy client
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyDataTransferPolicy` directive configures the data transfer policy that `mod_proxy` uses when performing data transfers (e.g. file uploads/downloads, directory listings) with the backend/destination server.

The currently supported policies are:

- `client`

  This policy indicates that `mod_proxy` will use whatever the connected client uses. Thus if the client sends `PORT`, `mod_proxy` will send `PORT` to the backend/destination server.

  This is the recommended policy in most cases.

- `active`

  Regardless of the commands sent by the client, `mod_proxy` will use only active data transfers (i.e. using `PORT` commands) with the backend/destination server.

- `passive`

  Regardless of the commands sent by the client, `mod_proxy` will use only passive data transfers (i.e. using `PASV` commands) with the backend/destination server.

- `PASV`

  Regardless of the commands sent by the client, `mod_proxy` will use only `PASV` commands with the backend/destination server.

- `PORT`

  Regardless of the commands sent by the client, `mod_proxy` will use only `PORT` commands with the backend/destination server.

- `EPSV`

  Regardless of the commands sent by the client, `mod_proxy` will use only `EPSV` commands with the backend/destination server.

- `EPRT`

  Regardless of the commands sent by the client, `mod_proxy` will use only `EPRT` commands with the backend/destination server.

### ProxyDatastore

Syntax: ProxyDatastore type [info]
Default: ProxyDatastore SQLite
Context: server config
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyDatastore` directive configures the type of datastore that `mod_proxy` uses for persistence. The currently supported datastore types are:

- Redis
- SQLite

Note that the Redis type also requires the info parameter, namely a prefix for all of the Redis keys. This prefix must be different/unique among all of your `mod_proxy` servers using that Redis server/cluster; the prefix is used to keep all of the data for this server separate from all other servers. For example:

      <IfModule mod_proxy.c>

        ...
        <IfModule mod_redis.c>
          # Use our IP address as our prefix
          ProxyDatastore Redis 1.2.3.4.
        </IfModule>
      </IfModule>

### ProxyDirectoryListPolicy

Syntax: ProxyDirectoryListPolicy client|"LIST" [opt1 ...]
Default: ProxyDirectoryListPolicy client
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyDirectoryListPolicy` directive configures the directory list policy that `mod_proxy` uses when performing directory lists with the backend/destination server.

The currently supported policies are:

- `client`

  This policy indicates that `mod_proxy` will use whatever the connected client uses. Thus if the client sends `MLSD`, `mod_proxy` will send `MLSD` to the backend/destination server.

  This is the recommended policy in most cases.

- `LIST`

  This policy instructs `mod_proxy` to use the `LIST` command with the backend/destination server, regardless of the command used by the client. The `mod_proxy` module then handles any reformatting of the `LIST` response into the response needed by the client. For example, if the client sends `MLSD` but the backend/destination server does not support this command, `mod_proxy` will send `LIST` instead, and translate the backend format into the client-requested format.

  The current implementation of this policy handles `LIST-<MLSD` translations only.

You may also provide options; the currently implemented options are:

- `UseSlink`

  Use this option to have `mod_proxy` use the broken "OS.unix=slink" syntax, preferred by FTP clients such as FileZilla, for indicating symlinks, rather than the more correct "OS.unix=symlink" syntax. See Bug#3318 for a more detailed discussion.

### ProxyEngine

Syntax: ProxyEngine on|off
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyEngine` directive toggles the support for proxying by `mod_proxy`. This is usually used inside a `<VirtualHost>` section to enable proxying of FTP sessions for a particular virtual host. By default `mod_proxy` is disabled for both the main server and all configured virtual hosts.

### ProxyForwardEnabled

Syntax: ProxyForwardEnabled on|off
Default: None
Context: `<Class>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyForwardEnabled` directive determines whether a client can use `mod_proxy` for forward proxying, based on that client's class.

By default, `mod_proxy` rejects any forward proxy request from any client, with the exception of clients connecting from RFC 1918 addresses:

      192.168.0.0/16
      172.16.0.0/12
      10.0.0.0/8

This is done as a security measure: open/unrestricted proxy servers are dangerous both to your network and to the Internet at large. Thus to make it possible for clients to use your server for forward proxying, they must be explicitly enabled to do so.

Example:

      <Class forward-proxy>
        From 1.2.3.4/12

        # Allow clients from this class to use FTP forward proxying
        ProxyForwardEnabled on
      </Class>

See also: `ProxyForwardTo`

### ProxyForwardMethod

Syntax: ProxyForwardMethod method
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyForwardMethod` directive configures the method that clients can use for requesting forward proxying. Some methods require that the client authenticate to the proxy first, and then separately authenticate to the destination server; these methods differ on just when the client specifies the destination server. Other methods do not require proxy authentication. There are many variations on a theme with these methods.

The currently supported methods are:

- `proxyuser,user@host`

  This method indicates that proxy authentication is required. The client first authenticates to the proxy via `USER/PASS` commands:

        USER proxy-user
        PASS proxy-passwd

  Then the client authenticates again, this time including the address (and optionally port) of the destination server as part of the second `USER` command:

        USER real-user@ftp.example.com
        PASS real-passwd

  The `mod_proxy` module will remove the destination address portion of the second `USER` command before proxying it to the destination server.

- `proxyuser@host,user`

  This method indicates that proxy authentication is required. The client first authenticates to the proxy via `USER/PASS` commands; note that the destination address (and optionally port) is included as part of the first `USER` command:

        USER proxy-user@ftp.example.com
        PASS proxy-passwd

  Then the client authenticates again, this time sending the `USER/PASS` commands to authenticate to the destination server:

        USER real-user
        PASS real-passwd

- `user@host`

  This methods indicates that no proxy authentication is used. The client indicates the destination address (and optionally port) of the server as part of the `USER` command:

        USER real-user@ftp.example.com
        PASS real-passwd

  The `mod_proxy` module will remove the destination address portion of the `USER` command before proxying it to the destination server.

Configuring the FTP client's proxy settings to match the above methods varies greatly, depending on the FTP client.

### ProxyForwardTo

Syntax: ProxyForwardTo [!]pattern [flags]
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyForwardTo` directive is used to restrict which hosts/domains can be requested for forward proxying. The destination host/port for forward proxying must match the configured pattern regular expression, or the forward proxying request will fail.

The optional flags parameter, if present, modifies how the given pattern will be evaludated. The supported flags are:

- nocase|NC (no case)
  This makes the pattern case-insensitive, i.e. there is no difference between 'A-Z' and 'a-z' when pattern is matched against the path

`ProxyForwardTo` limits the destination hosts to which clients can request forward proxying; by contrast, `ProxyForwardEnabled` controls forward proxying based on where clients connect from.

### ProxyLog

Syntax: ProxyLog path|"none"
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyLog` directive is used to specify a log file for `mod_proxy`'s reporting on a per-server basis. The path parameter given must be the full path to the file to use for logging.

Note that this path must not be to a world-writable directory and, unless `AllowLogSymlinks` is explicitly set to on (generally a bad idea), the path must not be a symbolic link.

### ProxyOptions

Syntax: ProxyOptions opt1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyOptions` directive is used to configure various optional behavior of `mod_proxy`. For example:

      ProxyOptions UseProxyProtocolV1

The currently implemented options are:

- `ShowFeatures`

  When reverse proxying, `mod_proxy` defaults to not responding to the FTP `FEAT` command, which is used to determine the supported features/capabilities of the FTP server; this is done to prevent leaking of information about internal FTP servers to the outside world. However, some clients rely on the `FEAT` data. For such clients/use cases, use this option to tell `mod_proxy` to proxy the `FEAT` command/response to the backend server.

- `UseDirectDataTransfers`

  The `mod_proxy` module will, by default, proxy all data transfers through the proxy server. Some sites may wish to have the FTP data transfers occur directly between the frontend client and the backend server, to avoid the latency/overhead of the proxying. Use this option to enable these direct data transfers (akin to Direct Server Return, or DSR), for both forward and reverse proxy roles:

        # Enable data transfers directly from frontend client to/from backend server
        ProxyOptions UseDirectDataTransfers

- `UseProxyProtocolV1`

  When `mod_proxy` connects to the backend/destination server, use the `PROXY` V1 protocol, sending the human-readable `PROXY` command to the destination server. This allows backend servers to implement access controls/logging, based on the IP address of the connecting client. The `mod_proxy_protocol` ProFTPD module can be used to handle the `PROXY` command on the receiving side, i.e. when using `proftpd` as the backend/destination server.

  Note: do not use this option unless the backend server does support the `PROXY` V1 protocol. Otherwise, the `PROXY` protocol message will only confuse the backend server, possibly leading to connection/login failures.

- `UseProxyProtocolV2`

  When `mod_proxy` connects to the backend/destination server, use the `PROXY` V2 protocol, sending a binary-encoded "PROXY" command to the destination server. This allows backend servers to implement access controls/logging, based on the IP address of the connecting client. The `mod_proxy_protocol` ProFTPD module can be used to handle the "PROXY" command on the receiving side, i.e. when using `proftpd` as the backend/destination server.

  Note: do not use this option unless the backend server does support the `PROXY` V2 protocol. Otherwise, the "PROXY" protocol message will only confuse the backend server, possibly leading to connection/login failures.

- `UseReverseProxyAuth`

  When reverse proxying, `mod_proxy` delegates user authentication to the selected backend server. However, there are some sites which need to centralize user authentication in the proxy; use this option to require proxy authentication for such cases. Note that this option only pertains to reverse proxy connections; proxy authentication when forward proxying is determined by the `ProxyForwardMethod` directive.

### ProxyRetryCount

Syntax: ProxyRetryCount count
Default: ProxyRetryCount 5
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyRetryCount` directive configures the number of times `mod_proxy` will attempt to connect to the backend/destination server. The default is 5 attempts.

### ProxyReverseConnectPolicy

Syntax: ProxyReverseConnectPolicy policy
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyReverseConnectPolicy` directive configures the policy that `mod_proxy` will use for selecting the backend server when reverse proxying.

The currently supported policies are:

- `LeastConns`

  Select the backend server with the lowest number of proxied connections.

- `LeastResponseTime`

  Select the backend server with the least response time; this is determined based on the connect time to the backend server, and its number of current connections.

- `PerGroup`

  Select a backend server based on the primary group of the authenticated `USER` name used by the connecting client; any future connections using that same `USER` name will be routed to the same backend server.

  Note: use of this `ProxyReverseConnectPolicy` also requires use of the `UseReverseProxyAuth` `ProxyOption`, as authenticating the given `USER` name is required for discovering the primary group of that user.

- `PerHost`

  Select a backend server based on the IP address of the connecting client; any future connections from that IP address will be routed to the same backend server.

- `PerUser`

  Select a backend server based on the `USER` name used by the connecting client; any future connections using that same `USER` name will be routed to the same backend server.

- `Random`

  Randomly select any of the backend servers.

- `RoundRobin`

  Select the next backend server in the list.

- `Shuffle`

  Similar to the `Random` policy, except the selection happens from the not-yet-chosed backend servers. This means that all backend servers will eventually be used evenly, just in a random order.

### ProxyReverseServers

Syntax: ProxyReverseServers servers
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyReverseServers` directive configures the list of servers to be used as the backend servers for reverse proxying.

Each server must be configured as a URL. Only the "ftp" and "ftps" schemes are currently supported. If not specified, the port will be 21. IPv6 addresses must be enclosed within square brackets. Thus, for example, the following are all valid URLs:

      ftp://ftp1.example.com:2121
      ftp://1.2.3.4
      ftp://[::ffff:6.7.8.9]:2121
      ftps://ftp2.example.com
      ftps://ftp3.example.com:990

And using them all in the configuration would look like:

      ProxyReverseServers ftp://ftp1.example.com:2121 ftps://1.2.3.4 ftp://[::ffff:6.7.8.9]:2121

The backend servers can also be contained in a list in a JSON file, e.g.:

    [
      "ftp://ftp1.example.com:2121",
      "ftp://[::ffff:6.7.8.9]:2121"
    ]

You then only need to configure the path to that JSON file:

      ProxyReverseServers file:/path/to/backends.json

The backend servers can also be provided from an external SQL database, queried by `mod_proxy` via `SQLNamedQuery`. For example, assuming a schema such as this:

      CREATE TABLE proxy_user_servers (
        user_name TEXT,
        url TEXT
      );

      CREATE INDEX proxy_user_servers_name_idx ON proxy_user_servers (user_name);

where url contains URLs, e.g. "ftp://1.2.3.4:21". Then you would configure `mod_proxy` to query for those per-user backend URLs using `ProxyReverseServers` like this:

      <IfModule mod_sql.c>
        ...
        SQLNamedQuery get-user-servers SELECT "url FROM proxy_user_servers WHERE user_name = %{0}"
      </IfModule>

      ProxyRole reverse
      ProxyReverseConnectPolicy PerUser
      ProxyReverseServers sql:/get-user-servers

Given that `mod_proxy` uses SQLite, does that mean that the table used for storing these per-user URLs must be SQLite? No. The use of `SQLNamedQuery` means that any database, supported by `mod_sql`, can be used.

### ProxyRole

Syntax: ProxyRole role
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyRole` directive configures whether `mod_proxy` will perform forward or reverse proxying. The list of supported roles are thus:

- `forward`

  Perform forward proxying.

- `reverse`

  Perform reverse proxying.

Note that the `ProxyRole` directive is required for `mod_proxy` to function. If this directive is not configured, connections to `mod_proxy` will fail.

### ProxySourceAddress

Syntax: ProxySourceAddress address
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxySourceAddress` directive configures the address (or device name) of a network interface on the host machine, to be used when connecting to the backend/destination server. This directive is most useful on a multi-homed (or DMZ) host; frontend connections can be received on one network interface, and the backend connections can use a different network interface. Imagine e.g. separate WAN/LAN interfaces on a proxying host.

### ProxyTables

Syntax: ProxyTables table-info
Default: None
Context: server config
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyTables` directive is used to specify a directory that `mod_proxy` will use for storing its database files; these files are used for tracking the various load balancing/healthcheck statistics used for proxying.

Note that the `ProxyTables` directive is required for `mod_proxy` to function. If this directive is not configured, connections to `mod_proxy` will fail.

### ProxyTimeoutConnect

Syntax: ProxyTimeoutConnect timeout
Default: ProxyTimeoutConnect 5sec
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyTimeoutConnect` directive configures the amount of time that `mod_proxy` will wait for the backend/destination server to accept a TCP connection, before giving up.

Note that if there are many different backend/destination servers to try (due to e.g. `ProxyRetryCount`), and if those backend servers are slow, the connecting client might itself see connection timeouts to `mod_proxy`. To guard against such slow backend servers, a more aggressively short timeout can be used:

      ProxyTimeoutConnect 1sec

### ProxyTimeoutLinger

Syntax: ProxyTimeoutLinger timeout
Default: ProxyTimeoutLinger 3sec
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyTimeoutLinger` directive configures the amount of time that `mod_proxy` will wait (lingering), after receiving all of the data on the data transfer connection to the backend server, for the explicit "end of transfer" response from the backend server, before giving up.

### ProxyTLSCACertificateFile

Syntax: ProxyTLSCACertificateFile path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCACertificateFile` directive configures one file where you can assemble the certificates of Certification Authorities (CA) which will be used to verify the servers' certificates. Such a file is merely the concatenation of the various PEM-encoded CA certificates. This directive can be used in addition to, or as an alternative for, `ProxyTLSCACertificatePath`.

Example:

      ProxyTLSCACertificateFile /etc/ftpd/cacerts.pem

Note that the location of CA certificates is required for `mod_proxy`'s TLS support; verification of server certificates is required for secure connections to backend/destination servers. For this reason, `mod_proxy` ships with its own default `ProxyTLSCACertificateFile`, which is generated using `libcurl`'s `mk-ca-bundle.pl` script:

      $ lib/mk-ca-bundle.pl -u cacerts.pem

### ProxyTLSCACertificatePath

Syntax: ProxyTLSCACertificatePath directory
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCACertificatePath` directive sets the directory for the certificates of Certification Authorities (CAs); these are used to verify the server certificates presented. This directive may be used in addition to, or as alternative for, `ProxyTLSCACertificateFile`.

The files in the configured directory have to be PEM-encoded, and are accessed through hash filenames. This means one cannot simply place the CA certificates there: one also has to create symbolic links named hash-value.N. The `c_rehash` utility that comes with OpenSSL can be used to create the necessary symlinks.

Example:

      ProxyTLSCACertificatePath /etc/ftpd/cacerts/

### ProxyTLSCARevocationFile

Syntax: ProxyTLSCACertificateFile path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCARevocationFile` directive configures one file that can contain the Certificate Revocation Lists (CRL) of Certification Authorities (CA); these CRLs are used during the verification of server certificates. Such a file is merely the concatenation of the various PEM-encoded CRL files. This directive can be used in addition to, or as an alternative for, `ProxyTLSCARevocationPath`.

Example:

      ProxyTLSCARevocationFile /etc/ftpd/cacrls.pem

### ProxyTLSCARevocationPath

Syntax: ProxyTLSCARevocationPath directory
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCARevocationPath` directive sets the directory for the Certificate Revocation Lists (CRL) of Certification Authorities (CAs); these are used during the verification of server certificates. This directive may be used in addition to, or as alternative for, `ProxyTLSCARevocationFile`.

The files in the configured directory have to be PEM-encoded, and are accessed through hash filenames. This means one cannot simply place the CRLs there: one also has to create symbolic links named hash-value.N. The `c_rehash` utility that comes with OpenSSL can be used to create the necessary symlinks.

Example:

      ProxyTLSCARevocationPath /etc/ftpd/cacrls/

### ProxyTLSCertificateFile

Syntax: ProxyTLSCertificateFile path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCertificateFile` directive points to the PEM-encoded file containing the client certificate file, and optionally also the corresponding private key. Note that this directive is only needed for backend/target FTPS servers which require client authentication.

Example:

      ProxyTLSCertificateFile /etc/ftpd/client-cert.pem

### ProxyTLSCertificateKeyFile

Syntax: ProxyTLSCertificateKeyFile path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCertificateKeyFile` directive points to the PEM-encoded file containing the client certificate file, and optionally also

The `ProxyTLSCertificateKeyFile` directive points to the PEM-encoded private key file for the client certificate indicated by `ProxyTLSCertificateFile`. If the private key is not combined with the certificate in the `ProxyTLSCertificateFile`, use this additional directive to point to the file with the standalone private key. When `ProxyTLSCertificateFile` is used and the file contains both the certificate and the private key, this directive need not be used. However, this practice is strongly discouraged. Instead we recommend you to separate the certificate and the private key.

### ProxyTLSCipherSuite

Syntax: ProxyTLSCipherSuite [protocol] cipher-list
Default: ProxyTLSCipherSuite DEFAULT:!ADH:!EXPORT:!DES
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSCipherSuite` directive configures the list of acceptable SSL/TLS ciphersuites to use for backend SSL/TLS connections. The syntax is that of the OpenSSL `ciphers` command, e.g.:

      $ openssl ciphers -v <cipher-list>

may be used to list all of the ciphers and the order described by a specific <cipher-list>.

If the SSL library supports TLSv1.3 (e.g. OpenSSL-1.1.1 and later), the protocol specifier "TLSv1.3" can be used to configure the cipher suites for that protocol:

      # Configure TLSv1.3 ciphersuites
      ProxyTLSCipherSuite TLSv1.3 TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256

### ProxyTLSEngine

Syntax: ProxyTLSEngine on|off|auto
Default: ProxyTLSEngine auto
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSEngine` directive configures the use of SSL/TLS for backend FTP connections. The supported values are:

- on

  Use of SSL/TLS is required; backend servers which do not support SSL/TLS or which fail the SSL/TLS handshake will cause the proxied session to be closed.

- off

  Use of SSL/TLS is disabled; backend servers which do support SSL/TLS will be ignored, and only FTP will be used.

- auto

  Use of SSL/TLS will be automatically used if the backend server supports SSL/TLS (via `AUTH TLS` in its `FEAT` response). If the SSL/TLS handshake fails, the backend connection will proceed using plain FTP.

  Note: this is the default behavior in `mod_proxy`.

### ProxyTLSOptions

Syntax: ProxyTLSOptions options
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSOptions` directive is used to configure various optional SSL/TLS behavior of `mod_proxy`.

Example:

      ProxyTLSOptions EnableDiags

The currently implemented options are:

- `AllowWeakSecurity`

  Sets the cryptographic security level to "zero", meaning that any/all ciphers and key sizes are permitted. This option allows for the best interoperability with any FTPS server, but is not recommended. Use only when necessary.

- `EnableDiags`

  Sets callbacks in the OpenSSL library such that a lot of SSL/TLS protcol information is logged to the `ProxyLog` file. This option is very useful when debugging strange interactions with FTPS servers.

- `NoSessionCache`

  By default, when using SSL/TLS, `mod_proxy` will cache the negotiated SSL sessions in its local database, for reuse in enabling SSL session resumption in future connections to those hosts. Use this option to disable use of session caching if/when needed.

- `NoSessionTickets`

  By default, when using SSL/TLS, `mod_proxy` will cache any session tickets offered by the server in its local database, for reuse in enabling SSL session resumption in future connections to those hosts. Use this option to disable use of session tickets if/when needed.

### ProxyTLSPreSharedKey

Syntax: ProxyTLSPreSharedKey identity key-info
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSPreSharedKey` directive is used to configure a pre-shared key (PSK), for use in TLS-PSK ciphersuites. Each PSK has an identity (a string/name used by clients to request the use of that PSK), and the actual key data. The key data may be encoded in different ways; the `ProxyTLSPreSharedKey` directive requires that the data be hex-encoded, as indicated in the key-info parameter.

The key-info parameter is comprised of the type of encoding used for the key data, and the full path to the key file. Only "hex" encoding is supported right now. Thus an example `ProxyTLSPreSharedKey` directive would be:

      ProxyTLSPreSharedKey MyPSK hex:/path/to/psk.key

The configured file cannot be world-readable or world-writable; the `mod_proxy` module will skip/ignore such insecure permissions.

To generate this shared key (which is just a randomly generated bit of data), you can use:

      $ openssl rand 160 -out /path/to/identity.key -hex

Note that `ProxyTLSPreSharedKey` requires at least 20 bytes of key data. Having generated the random key data, tell `mod_proxy` to use it via:

      ProxyTLSPreSharedKey identity hex:/path/to/identity.key

### ProxyTLSProtocol

Syntax: ProxyTLSProtocol protocols
Default: ProxyTLSProtocol TLSv1 TLSv1.1 TLSv1.2
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSProtocol` directive is used to configure the SSL/TLS protocol versions that `mod_proxy` should use when establishing SSL/TLS sessions to backend servers.

The allowed protocols are:

`SSLv3` | Use only SSLv3
`TLSv1` | Use only TLSv1
`TLSv1.1` | Use only TLSv1.1
`TLSv1.2` | Use only TLSv1.2

To support both SSLv3 and TLSv1, simply list both parameters for the `ProxyTLSProtocol` directive, e.g.:

      ProxyTLSProtocol SSLv3 TLSv1

The `ProxyTLSProtocol` directive can also be used in a different manner, to add or subtract protocol support. For example, to enable all protocols except SSLv3, you can use:

      ProxyTLSProtocol ALL -SSLv3

Using the directive in this manner requires that "ALL" be the first parameter, and that all protocols have either a `+` (add) or `-` (subtract) prefix. "ALL" will always be expanded to all of the supported SSL/TLS protocols known by `mod_proxy` and supported by `OpenSSL`.

### ProxyTLSTimeoutHandshake

Syntax: ProxyTLSTimeoutHandshake timeout
Default: ProxyTLSTimeoutHandshake 30sec
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSTimeoutHandshake` directive configures the maximum number of seconds for `mod_proxy` to complete an SSL/TLS handshake. If set to zero, `mod_proxy` will wait forever for a handshake to complete. The default is 30 seconds.

### ProxyTLSTransferProtectionPolicy

Syntax: ProxyTLSTransferProtectionPolicy client|required|clear
Default: ProxyTLSTransferProtectionPolicy required
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc1 and later

The `ProxyTLSTransferProtectionPolicy` directive configures the data transfer protection policy, when using SSL/TLS, that `mod_proxy` uses when performing data transfers (e.g. file uploads/downloads, directory listings) with the backend/destination server.

The currently supported policies are:

- `client`

  This policy indicates that `mod_proxy` will use whatever the connected client uses. Thus if the client sends `PROT C`, `mod_proxy` will send `PROT C` to the backend/destination server.

- `required`

  Regardless of the commands sent by the client, `mod_proxy` will use only protected TLS data transfers (i.e. using `PROT P` commands) with the backend/destination server.

  This is the recommended policy in most cases.

- `clear`

  Regardless of the commands sent by the client, `mod_proxy` will use only clear (i.e. unprotected) data transfers (i.e. using `PROT C` commands) with the backend/destination server.

### ProxyTLSVerifyServer

Syntax: ProxyTLSVerifyServer on|off
Default: ProxyTLSVerifyServer on
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.6rc2 and later

The `ProxyTLSVerifyServer` directive configures how `mod_proxy` handles certificates presented by servers. If off, the module will accept any server certificate and establish an SSL/TLS session, but will not verify the certificate. If on, the module will verify a server's certificate and, furthermore, will fail all SSL handshake attempts unless the server presents a valid certificate.

## Usage

Benefits of Proxying
The benefits of using a module like `mod_proxy` depend mostly on the type of proxying, forward or reverse, that `mod_proxy` is configured to perform. There are some benefit, however, that the module can bring, regardless of the type of proxying:

- Add ProFTPD's FTPS support (via `mod_tls`) for FTP servers which do not support FTPS
- Add ProFTPD's FTPS support for FTP clients which do not support FTPS
- Add ProFTPD's IPv6 support for FTP clients/servers which do not support it
- Add ProFTPD's logging power (`TransferLog`, `ExtendedLog`, etc) for FTP servers which do not provide such versatile logging
- Use ProFTPD's monitoring capabilities (e.g. `mod_snmp`) for FTP servers which do not have such features

When using `mod_proxy` for proxying, all data transfers (e.g. file uploads/downloads, directory listings, etc) pass through `mod_proxy`; data transfers do not occur directly between the "frontend" clients and the "backend" servers. This happens so that clients are completely unaware of the network structure for the backend servers; this is especially important when reverse proxying, where it means that the client sees `mod_proxy` as the real FTP server.

Forward Proxying
One of the most common benefits of a forward proxy is having controlled access, by clients within an internal LAN, to outside servers. FTP makes this sort of thing notoriously difficult for firewalls/routers due to its multi-TCP connection nature; this, in turn, makes proxying of FTP more difficult. But `mod_proxy` makes this possible; it understands FTP, and thus provides the access control needed for such use cases.

When using `mod_proxy` as a forward proxy, FTP clients which can only perform active data transfers can use `mod_proxy` as a way to use passive data transfers with the destination FTP server.

Similarly, FTP clients which do not support IPv6 can proxy through `mod_proxy` to reach a destination FTP server with an IPv6 address; `mod_proxy` handles IPv4 and IPv6 addresses transparently.

Reverse Proxying
Just as `mod_proxy` can aid naive/legacy FTP clients via forward proxying, `mod_proxy` can similarly front legacy FTP servers. For example, `mod_proxy` can sit in front of FTP servers which do not handle IPv6 addresses, and provide this functionality transparently to IPv6-capable FTP clients.

When performing reverse proxying, `mod_proxy` can also perform load balancing in various ways. The common methods of "round robin" and "least connections" are implemented; `mod_proxy` also provides "sticky session" load balancing of clients as well.

Handling of Proxied Sessions
Once a proxied session has authenticated with the backend/destination server, the `mod_proxy` module automatically chroots itself to a subdirectory of the `ProxyTables` directory, after which all root privileges are permanently dropped.

Forward Proxy Configuration
Before discussing example forward proxy configurations for `mod_proxy`, it is very important to understand the consequences of providing forward proxy capabilities.

Important Security Considerations
A forward proxy can be used by any client to have the proxy connect to any arbitrary host, while hiding the client's true identity. Malicious behavior, hacking or denial-of-service attempts, etc will appear to be coming from your proxy; this is dangerous for your network, and for the Internet at large. Think of the damage that has been done, and continues to happen, due to open/unrestricted proxies/relays such as open DNS or SMTP/email proxies.

This is the reason that `mod_proxy` does not allow just any client to use its forward proxy capabilities by default; instead, only clients connecting from the LAN are allowed by default. Allowing trusted outside clients is done using the `ProxyForwardEnabled` directive. Even allowing internal clients to use your forward proxy can be troublesome, depending on the destination hosts selected by the clients. To ensure that your clients are using the forward proxy to connect only to the hosts allowed, you can use the `ProxyForwardTo` directive to configure a regular expression-based whitelist of allowed destination/target hosts; this is strongly recommended.

With all that said, here's an example `mod_proxy` configuration for supporting forward proxying:

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole forward
        ProxyForwardMethod user@host
        ProxyForwardTo ^ftp\.example\.com\:21$ [NC]
      </IfModule>

If the configured `ProxyForwardTo` pattern is not met, the following will be logged in the `ProxyLog`:

    mod_proxy/0.7[16151]: host/port 'server.example.org:2121' did not match ProxyForwardTo ^ftp\.example\.com\:21$, rejecting

Reverse Proxy Configuration
Reverse proxies (also known as "gateways") are often used to provide access to FTP resources, located with internal networks, to the outside world. The reverse proxy can perform load balancing, provide functionality that the internal servers may not be able to do, and even perform things like caching.

Access control for reverse proxies is less critical than for forward proxies because clients can only reach, via the reverse proxy, the backend servers that the reverse proxy has been configured to use; the clients do not get to choose arbitrary hosts for the reverse proxy to use.

Here's an example `mod_proxy` configuration for supporting reverse proxying:

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole reverse
        ProxyReverseConnectPolicy RoundRobin
        ProxyReverseServers ftp://ftp-backend1.example.com:2121 ftp://ftp-backend2.example.com:2121 ...
      </IfModule>

Here's an example `mod_proxy` configuration for reverse proxying, with lookup of per-user backend servers:

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole reverse
        ProxyReverseConnectPolicy PerUser

        # We need to provide a pool of backend servers as a fallback
        ProxyReverseServers ftp://ftp-backend1.example.com:2121 ftp://ftp-backend2.example.com:2121 ...

        # Look up per-user backend servers from user-specific JSON files
        ProxyReverseServers file:/var/ftp/proxy/backends/%U.json
      </IfModule>

Similarly, you can use a per-group lookup for the backend servers when reverse proxying:

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole reverse
        ProxyReverseConnectPolicy PerGroup
        ProxyOptions UseReverseProxyAuth

        # We need to provide a pool of backend servers as a fallback
        ProxyReverseServers ftp://ftp-backend1.example.com:2121 ftp://ftp-backend2.example.com:2121 ...

        # Look up per-group backend servers from group-specific JSON files
        ProxyReverseServers file:/var/ftp/proxy/backends/%g.json
      </IfModule>

In order to support FTP over SSL/TLS (FTPS) connections from clients when reverse proxying, simply include your normal `mod_tls` configuration with the same `<VirtualHost>` configuration with your `mod_proxy` configuration.

For FTPS support to the backend servers, your reverse proxy configuration would look something like this:

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole reverse
        ProxyReverseConnectPolicy RoundRobin
        ProxyReverseServers ftp://ftp-backend1.example.com:2121 ftp://ftp-backend2.example.com:2121 ...

        # Use FTPS when supported/available by the backend server
        ProxyTLSEngine auto

        # List of trusted root CAs
        ProxyTLSCACertificateFile /etc/ftpd/cacerts.pem
      </IfModule>

In fact, `mod_proxy` comes with a default `ProxyTLSCACertificateFile` (comprised of the root CAs that most browsers use/trust), and the default `ProxyTLSEngine` value is auto. This means that, by default, `mod_proxy` will try to use FTPS for backend connections automatically (assuming that ProFTPD is built with OpenSSL support using the `--enable-openssl` configure option).

Load Balancing versus Session Stickiness
For reverse proxy configurations, there is a choice between load balancing and sticky session `ProxyReverseConnectPolicy` parameters; these parameters determine the selection of the backend server that will handle the incoming connection.

Which should you use, and why?

All of the balancing policies are able to select the backend server when the FTP client connects to the proxy, before sending any commands. Most of the "sticky" policies, on the other hand, require more knowledge about the user (e.g. `USER` name, `HOST` name, SSL session ID) before the backend server can be determined, thus backend server selection is delayed until that information is obtained.

Balancing is best when all of your backend severs are identical with regard to the content they have, and when it does not matter which server handles a particular client. Maybe all of your backend servers use a shared filesystem via NFS or similar, thus directory listings will be the same for a user no matter which backend server is used, and uploading files to one server means that those files can be downloaded/seen by the other servers. Balancing policies are also best when all of your backend servers have similar processing power (memory, CPU, network, disk), so that all backend servers are equally capable of providing the same service to the connecting client.

The balancing policies are:

- `LeastConns`
- `Random`
- `RoundRobin`
- `Shuffle`

Stickiness is best when your backend servers are not identical, and some users/clients should only ever go to the same set of backend servers. Thus the user/client needs to be "sticky" to a given backend server.

The sticky policies are:

- `PerGroup`
- `PerHost`
- `PerUser`

Implicit FTPS Support
The `mod_proxy` module includes support for using implicit FTPS with backend servers, both when forward and reverse proxying.

In order to use implicit FTPS for a reverse proxy server, the URI syntax must be used in a `ProxyReverseServers` directive; the scheme must be "ftps" and the port must be explicitly specified as 990, thus: `ProxyReverseServers ftps://ftp.example.com:990`

When forward proxying, the client must request the destination server and specify a port of 990, e.g.: `USER user@ftp.example.com:990`

SFTP/SCP Support
The `mod_proxy` module only works for FTP/FTPS sessions; it does not currently support/handle SFTP/SCP sessions.

Logging
The `mod_proxy` module supports different forms of logging. The main module logging is done via the `ProxyLog` directive. For debugging purposes, the module also uses trace logging, via the module-specific channels:

- proxy
- proxy.conn
- proxy.db
- proxy.forward
- proxy.ftp.conn
- proxy.ftp.ctrl
- proxy.ftp.data
- proxy.ftp.dirlist
- proxy.ftp.msg
- proxy.ftp.sess
- proxy.ftp.xfer
- proxy.inet
- proxy.netio
- proxy.random
- proxy.reverse
- proxy.reverse.db
- proxy.reverse.redis
- proxy.session
- proxy.tls
- proxy.tls.db
- proxy.tls.redis
- proxy.uri

Thus for trace logging, to aid in debugging, you would use the following in your `proftpd.conf`:

      TraceLog /path/to/proxy-trace.log
      Trace proxy:20

This trace logging can generate large files; it is intended for debugging use only, and should be removed from any production configuration.

Suggested Future Features
The following lists the features I hope to add to `mod_proxy`, according to need, demand, inclination, and time:

- `MODE Z` support
- SFTP/SCP support

See the GitHub issues page for current bugs and feature requests, and to report issues.

Frequently Asked Questions

Question: I have heard a lot about both "round robin" and "least conns" for load balancing. Which is better for FTP connections?
Answer: There is not an easy answer to this, because it really comes down to the type of traffic that your FTP servers will see.

If your FTP sessions tend to be long-lived (e.g. on the order of minutes to hours), then using `ProxyReverseConnectPolicy LeastConns` will tend to provide the best distribution of those sessions across your pool of backend servers. The assumption here is that new connections arrive infrequently relative to the number of existing connections.

On the other hand, if your FTP sessions tend to be shorter (e.g. minutes at most), then using `ProxyReverseConnectPolicy RoundRobin` might provide a more even distribution of connections across your pool of backend servers.

Question: I am using:

      ProxyReverseConnectPolicy PerHost

and would like to configure different pools of backend servers for different incoming clients. How do I do this?
Answer: The best way to achieve this would be to use classes and `mod_ifsession`'s `<IfClass>` sections. For example:

      <Class proxied-clients>
      </Class>

      ProxyRole reverse
      ProxyReverseConnectPolicy PerHost

      <IfClass proxied-clients>
        ProxyReverseServers ftp://ftp-special1.example.com:2121 ftp://ftp-special2.example.com:2121 ...
      </IfClass>

      # Don't forget to configure the backend server pool for clients coming
      # from other networks!
      <IfClass !proxied-clients>
        ProxyReverseServers ftp://ftp-backend1.example.com:2121 ftp://ftp-backend2.example.com:2121 ...
      </IfClass>

Question: Does `mod_proxy` support SSL/TLS connections, i.e. FTPS?
Answer: Short answer: yes.

The long answer is the `mod_proxy` supports FTPS connections both on the frontend, from connecting clients, and on the backend, to backend servers. This means that all of the following flows are supported:

      client --- FTPS ---> proxy --- FTP ---> server
      client --- FTP ---> proxy --- FTPS ---> server
      client --- FTPS ---> proxy --- FTPS ---> server

Thus `mod_proxy`'s FTPS support is suited for reverse proxy configurations, where `mod_proxy` can be used to provide SSL/TLS capabilities to old/legacy FTP servers which do not implement it. The `mod_proxy` module is also suited for forward proxy configurations, where the FTPS support can be used to provide SSL/TLS capabilities to old/legacy FTP clients which do not implement it.

Question: I want to use `mod_proxy` for reverse proxying. I want to centralize all of my user authentication in `mod_proxy`, and I want to use different user credentials when logging in to the backend servers. Can `mod_proxy` do all of this?
Answer: Yes.

There are a couple of key parts of your `mod_proxy` configuration to pay attention to, for achieving the above.

      <IfModule mod_proxy.c>
        ProxyEngine on
        ProxyLog /path/to/proxy.log
        ProxyTables /var/ftp/proxy

        ProxyRole reverse

        # Make sure to authenticate in the proxy itself
        ProxyOptions UseReverseProxyAuth

        ProxyReverseConnectPolicy ...

        # Include the username/passwords to use for the backend servers
        # in the URLs.
        ProxyReverseServers ftp://user1:password1@ftp-backend1.example.com:2121 ftp://user2:password2@ftp-backend2.example.com:2121 ...
      </IfModule>

When a URL uses the "username:password" syntax for including the credentials to use for that connection, `mod_proxy` will use those URI credentials when logging in to that backend server.

Question: I have configured `mod_proxy` to block the `LIST` command for one group of users, like so:

      <Limit LIST>
        DenyGroup somegroup
      </Limit>

But this `<Limit>` is not working. Is this a bug?
Answer: No, it is not a bug. It is, unfortunately, expected behavior.

The `<Limit>` mechanism works on the authenticated user/group names. And in many cases, it is not `mod_proxy` which authenticates the user, it is the backend server. Thus it is the backend server which knows the groups to which the authenticated user belongs; `mod_proxy` only knows that the `USER` name was successfully authenticated by the backend user. Thus group-based limits cannot be honored.

However, if proxy auth is enabled, then group-based limits will work. This means using either the following when forward proxying:

      ProxyRole forward

      # Either of these two methods result in proxy auth
      ProxyForwardMethod proxyuser,user@host
      ProxyForwardMethod proxyuser@host,user

or this, when reverse proxying:

      ProxyRole reverse
      ProxyOptions UseReverseProxyAuth

Question: Can `mod_proxy` be configured as a reverse proxy, and to select the backend server based on the client certificate used by the frontend FTPS client?
Answer: Yes, but it requires the use of a custom SQL query.

The idea here is to define a SQL query which looks up the backend server to use, based on information in the frontend FTPS client certificate. Since the `mod_proxy` module already uses SQLite, you should be able to use the `mod_sql_sqlite` module if needed. The SQL table schema might look like:

      CREATE TABLE proxy_backends (
        common_name TEXT PRIMARY KEY,
        url TEXT
      );

      <IfModule mod_tls.c>
        ...
        # Tell mod_tls to export environment variables containing various
        # certificate fields, for use by e.g. mod_sql.
        TLSOptions StdEnvVars
        ...
      </IfModule>

      <IfModule mod_sql.c>
        ...
        # Use the TLS_CLIENT_S_DN_CN environment variable (for the CommonName of
        # the Subject section of the frontend FTPS client certificate) provided
        # by mod_tls in our selection.
        SQLNamedQuery get-user-servers SELECT "url FROM proxy_backends WHERE common_name = '%{env:TLS_CLIENT_S_DN_CN}'"
        ...
      </IfModule>

      <IfModule mod_proxy.c>
        ...
        ProxyRole reverse
        ProxyReverseConnectPolicy PerUser

        # Use a named SQL query to lookup the backend server to use.  Note that
        # the name used here is the name of our SQLNamedQuery defined above.
        ProxyReverseServers sql:/get-user-servers
        ...
      </IfModule>

Note that the `mod_tls` module provides many other environment variables for other fields of the certificate; using a field other than CommonName is also quite doable, depending on your needs.

## Installation

To install `mod_proxy`, go to the third-party module area in the proftpd source code and unpack the `mod_proxy` source tarball:

      $ cd proftpd-dir/contrib/
      $ tar zxvf /path/to/mod_proxy-version.tar.gz

after unpacking the latest proftpd-1.3.x source code. For including `mod_proxy` as a statically linked module:

      $ ./configure --with-modules=mod_proxy:...

To build `mod_proxy` as a DSO module:

      $ ./configure --enable-dso --with-shared=mod_proxy:...

Then follow the usual steps:

      $ make
      $ make install

Note: `mod_proxy` uses the `SQLite` library; thus the `sqlite3` development library/headers must be installed for building `mod_proxy`.

It is highly recommended that SQLite 3.8.5 or later be used. Problems have been reported with `mod_proxy` when SQLite 3.6.20 is used; these problems disappeared once SQLite was upgraded to a newer version.

© Copyright 2015-2020 TJ Saunders
All Rights Reserved