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
- ProxySFTPCiphers
- ProxySFTPCompression
- ProxySFTPDigests
- ProxySFTPHostKey
- ProxySFTPKeyExchanges
- ProxySFTPOptions
- ProxySFTPPassPhraseProvider
- ProxySFTPServerAlive
- ProxySFTPServerMatch
- ProxySFTPVerifyServer
- 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.

- `user@sni`

  This methods indicates that no proxy authentication is used. The client indicates the destination address (and optionally port) of the server as part of a TLS SNI (Server Name Indication) portion of a TLS session:

        AUTH TLS handshake with SNI
        USER real-user
        PASS real-passwd

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.

Example:

      # Limit forward proxying to specific host and port
      ProxyForwardTo ^ftp.example.com:21$

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

- `UseProxyProtocolV2TLVs`

  When `mod_proxy` is configured to use the PROXY protocol V2, via the `UseProxyProtocolV2` option, this option tells `mod_proxy` to send additional data as Type/Length/Value (TLV). These TLVs include:

  - ALPN (Application Layer Protocol Negotiation)
  - Authority (Hostname provided by client)
  - SSL/TLS (whether SSL/TLS is in use at the time of the PROXY protocol)
  - SSL/TLS protocol version
  - SSL/TLS cipher
  - Unique ID (when `mod_unique_id` is present/used)

  Note: use of the `UseProxyProtocolV2` option is also required for the TLVs to be sent.

- `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: RoundRobin
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 discovered via DNS `SRV` or `TXT` records, using `SRV/TXT` URL scheme variants, e.g.:

      # Discover backend addresses via DNS SRV records
      ftp+srv://_ftp._tcp.castaglia.org

      # Discover backend addresses via DNS TXT records (which must be an FTP URL)
      ftp+txt://castaglia.org

These `SRV/TXT` URL scheme variations also apply to FTPS URLs. Note that any explicit port numbers provided in URLs using these `SRV/TXT` scheme variants will be ignored; the actual port numbers to use will be discovered from the `SRV` and `TXT` DNS records.

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

Note that `mod_proxy` has strict requirements for the permissions on `ProxyReverseServers` files. The configured file must not be world-writable, since that would allow any user on the system to modify the file, directing proxied connections anywhere else. If a world-writable `ProxyReverseServers` file is found, you will see the following error message logged:

      unable to use world-writable ProxyReverseServers '/opt/proxy/reverse.json' (perms 0666): Operation not permitted

In addition, the directory containing the `ProxyReverseServers` file cannot be world-writable, either. Even if the file itself is not world-writable, being in a world-writable directory means that any user on the system can delete that file, and write a new file. When a world-writable directory is found, the following error is logged:

      unable to use ProxyReverseServers '/opt/proxy/reverse.json' from world-writable directory '/opt/proxy' (perms 0777): Operation not permitted

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.

### ProxySFTPCiphers

Syntax: ProxySFTPCiphers algo1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPCiphers` directive is used to specify the list of cipher algorithms that `mod_proxy` should use when connecting to backend SSH servers. The current list of supported cipher algorithms is, in the default order of preference:

- aes256-ctr
- aes192-ctr
- aes128-ctr
- aes256-gcm@openssh.com
- aes128-gcm@openssh.com
- aes256-cbc
- aes192-cbc
- aes128-cbc
- cast128-cbc
- 3des-ctr
- 3des-cbc

By default, all of the above cipher algorithms are presented to the server, in the above order, during the key exchange.

The "none" cipher (i.e. no encryption) will not be presented to the server by default; any sites which wish to use "none" will have to explicitly configure it via `ProxySFTPCiphers`.

Note that CTR mode ciphers (e.g. the `aes128-ctr`, `aes192-ctr`, and `aes256-ctr` ciphers) are recommended. Any CBC mode cipher allows for the possibility of an attack which causes several bits of plaintext to be leaked; the attack is described here. This attack is on the SSH2 protocol design itself; any SSH2 implementation which conforms to the RFCs will have this weakness.

In general, there is no need to use this directive unless only one specific cipher must be used.

### ProxySFTPCompression

Syntax: ProxySFTPCompression on|off|delayed
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPCompression` directive enables the use of zlib compression of the payload of SSH2 packets. This can make for smaller packets, but require more CPU time to compress/uncompress the data.

The delayed parameter tells `mod_proxy` to support a custom extension used by OpenSSH, where compression is not actually enabled until after the client has successfully authenticated.

### ProxySFTPDigests

Syntax: ProxySFTPDigests algo1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPDigests` directive is used to specify the list of MAC digest algorithms that `mod_proxy` should use when connecting to backend SSH servers. The current list of supported MAC algorithms is:

- hmac-sha2-256
- hmac-sha2-256-etm@openssh.com
- hmac-sha2-512
- hmac-sha2-512-etm@openssh.com
- hmac-sha1
- hmac-sha1-etm@openssh.com
- hmac-sha1-96
- hmac-sha1-96-etm@openssh.com
- umac-64@openssh.com
- umac-64-etm@openssh.com
- umac-128@openssh.com
- umac-128-etm@openssh.com

By default, all of the above MAC algorithms are presented to the server, in the above order, during the key exchange. Note that some algorithms (e.g. the SHA256 and SHA512 algorithms) may not be supported by the version of OpenSSL used; this will be automatically detected.

The following list of algorithms are supported, but not presented to servers by default. These algorithms must be explicitly configured via `ProxySFTPDigests` to be available for use by servers:

- hmac-md5
- hmac-md5-etm@openssh.com
- hmac-md5-96
- hmac-md5-96-etm@openssh.com
- hmac-ripemd160

The "none" MAC (i.e. no MAC) will not be presented to the server by default; any sites which wish to use "none" will have to explicitly configure it via `ProxySFTPDigests`.

In general, there is no need to use this directive unless only one specific MAC algorithm must be used.

### ProxySFTPHostKey

Syntax: ProxySFTPHostKey file|agent:/path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPHostKey` directive configures the path to a host key file. The `mod_proxy` module uses the keys thus configured for "hostbased" user authentication to backend SSH servers.

You can use either an RSA key, a DSA key, and/or ECDSA keys. These could be the exact same host key files as used by your SSH server, e.g.:

      <IfModule mod_sftp.c>
        ...
        SFTPHostKey /etc/ssh_host_dsa_key
        SFTPHostKey /etc/ssh_host_rsa_key
        SFTPHostKey /etc/ssh_host_ecdsa_key
        SFTPHostKey /etc/ssh_host_ed25519_key
      </IfModule>

      <IfModule mod_proxy.c>
        ProxyEngine on
        ..
        ProxySFTPHostKey /etc/ssh_host_dsa_key
        ProxySFTPHostKey /etc/ssh_host_rsa_key
        ProxySFTPHostKey /etc/ssh_host_ecdsa_key
        ProxySFTPHostKey /etc/ssh_host_ed25519_key
      </IfModule>

The `ProxySFTPHostKey` directive can also be used to load host keys from an SSH agent such as OpenSSH's `ssh-agent`. For example:

      # Load all of the keys from the ssh-agent configured for proxy use
      ProxySFTPHostKey agent:%{env:SSH_AUTH_SOCK}

Using an SSH agent for storing host keys allows for configurations where the host keys are not stored on files on the server system, e.g. the keys can be loaded into the SSH agent from a PKCS#11 token.

### ProxySFTPKeyExchanges

Syntax: ProxySFTPKeyExchanges algo1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPKeyExchanges` directive is used to specify the list of key exchange algorithms that `mod_proxy` should use when connecting to backend SSH servers. The current list of supported key exchange algorithms is:

- curve448-sha512
- curve25519-sha256
- ecdh-sha2-nistp521
- ecdh-sha2-nistp384
- ecdh-sha2-nistp256
- diffie-hellman-group18-sha512
- diffie-hellman-group16-sha512
- diffie-hellman-group14-sha256
- diffie-hellman-group-exchange-sha256
- diffie-hellman-group-exchange-sha1
- diffie-hellman-group14-sha1
- diffie-hellman-group1-sha1
- rsa1024-sha1

By default, all of the above key exchange algorithms except `diffie-hellman-group1-sha1` are presented to the server, in the above order, during the key exchange.

Note that the `diffie-hellman-group1-sha1` key exchange algorithm uses a weak hardcoded Diffie-Hellman group, and thus is not enabled by default. To enable this key exchange algorithm, you must configure the `ProxySFTPKeyExchanges` list to explicitly include this algorithm, or use the following in your configuration:

      ProxySFTPOptions AllowWeakDH

In general, there is no need to use this directive unless only one specific key exchange algorithm must be used.

### ProxySFTPOptions

Syntax: ProxySFTPOptions opt1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPOptions` directive is used to configure various optional SSH-specific behavior of `mod_proxy`.

For example:

      ProxySFTPOptions AllowWeakDH

The currently implemented options are:

- `AllowWeakDH`

  The `mod_proxy` module will not use Diffie-Hellman groups of less than 2048 bits, due to weaknesses that can downgrade the security of an SSH session. If for any reason your SFTP/SCP servers require smaller Diffie-Hellman groups, then use this option.

- `NoExtensionNegotiation`

  By default, `mod_proxy` will offer/support the SSH extension negotiation, defined by RFC 8308. Use this option to disable support for extension negotiations.

- `NoHostkeyRotation`

  By default, `mod_proxy` will offer/support the OpenSSH hostkey rotation extensions, "hostkeys-00@openssh.com" and "hostkeys-prove-00@openssh.com". Use this option to disable support for these custom OpenSSH extensions.

- `NoStrictKex`

  By default, `mod_proxy` will honor/support the OpenSSH "strict KEX" mode extension, "kex-strict-c-v00@openssh.com" and "kex-strict-s-v00@openssh.com". Use this option to disable support for these custom OpenSSH extensions.

- `OldProtocolCompat`

  Older servers identity their protocol versions as "1.99", rather than as "2.0". By default, `mod_proxy` will refuse to handle connections to such servers. To enable compatibility with these servers (which tend to be derived from ssh.com/Tectia code), use this option.

  Note that this option automatically enables the `PessimisticKexinit` ProxySFTPOption as well.

- `PessimisticKexinit`

  As described here, the `mod_proxy` module tries to reduce the connection latency by optimistically sending the `KEXINIT` key exchange message. However, some SSH servers cannot handle this behavior. Use this option to disable the optimistic sending of the `KEXINIT` message.

### ProxySFTPPassPhraseProvider

Syntax: ProxySFTPPassPhraseProvider path
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPPassPhraseProvider` directive is used to specify an external program which will be called, when `mod_proxy` starts up, for each encrypted `ProxySFTPHostKey` file. The program will be invoked with two command-line arguments, passed on `stdin` to the program:

      servername:portnumber file

where `servername:portnumber` indicates the server using that encrypted certificate key, and file indicates the host key file in question. The program then must print the corresponding passphrase for the key to `stdout`.

The intent is that this external program can perform any security checks necessary, to make sure that the system is not compromised by an attacker, and only when these checks pass successfully will the passphrase be provided. These security checks, and the way the passphrase is determined, can be as complex as you like.

Example:

      ProxySFTPPassPhraseProvider /etc/ftpd/proxy/get-ssh-passphrase

### ProxySFTPServerAlive

Syntax: ProxySFTPServerAlive count interval
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPServerAlive` directive configures `mod_proxy` to send messages to a server, through the encrypted channel, to request a response from the server. If count server alive messages are sent without receiving any response messages from the server, the client will disconnect. The interval indicates how much time, in seconds, that `mod_proxy` waits for data from the server before sending a server alive message.

For example, using:

      ProxySFTPServerAlive 3 15

will cause an unresponsive server to be disconnected after approximately 45 seconds.

### ProxySFTPServerMatch

Syntax: ProxySFTPServerMatch pattern key1 val1 ...
Default: None
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPServerMatch` directive is used to tune some of the SSH2/SFTP values based on the connected server, for better interoperability with those servers. The pattern parameter specifies a POSIX regular expression which will be matched against the connected server's SSH version banner. If the server's banner version matches pattern, then the configured keys/values are used for that session.

The currently supported SSH2/SFTP keys which can be tuned via `ProxySFTPServerMatch` are:

- pessimisticNewkeys

### ProxySFTPVerifyServer

Syntax: ProxySFTPVerifyServer on|off
Default: ProxySFTPVerifyServer off
Context: server config, `<VirtualHost>`, `<Global>`
Module: mod_proxy
Compatibility: 1.3.8rc3 and later

The `ProxySFTPVerifyServer` directive tells `mod_proxy` whether to verify whether the host keys presented by backend SSH servers have changed.

When `mod_proxy` connects to a backend SSH server for the very first time, its hostkey will be recorded. The next time `mod_proxy` connects, the hostkey presented will be compared to the previously recorded hostkey. When `ProxySFTPVerifyServer` is on, any hostkey mismatches will be logged.

### 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|MatchClient
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. Note that SSL/TLS support requires the presence/use of the `mod_tls` ProFTPD module.

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`.

- Use of SSL/TLS to the backend server will attempt to match the SSL/TLS usage of the frontend client. Thus if the frontend client uses explicit FTPS, then explicit FTPS will be attempted with the backend; similarly for implicit FTPS. And if the frontent client does not use FTPS, then SSL/TLS will not be used for the backend connection.

### 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. Note that implicit FTPS support requires the presence/use of the `mod_tls` ProFTPD module.

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`

Note that there is an additional wrinkle/caveat for implicit FTPS support when your `mod_proxy` installation uses shared/DSO modules. You must ensure, in a list of `LoadModule` directives, that `mod_proxy` appears before `mod_tls`:

      # Make sure that mod_tls appears after mod_proxy in the list of loaded
      # modules
      LoadModule mod_proxy.c
      LoadModule mod_tls.c

Why does this matter?

A lot of things happen when a client connects, via TCP, to the server (in this case, to a server configured to be a proxy). ProFTPD publishes this "on connect" event to all of the modules, in "module load order". The last module loaded will be the first module to receive this event; this means "module loader order" is the reverse order of your `LoadModule` directives.

What we want is for the `mod_tls` module to handle the "on connect" event first, so that it handles the implicit TLS handshake. In doing so, the `mod_tls` module will record some internal session state showing that a TLS handshake occurred. The `mod_proxy` module looks for the internal session state that `mod_tls` sets, and will Do The Right Thing if it sees that a TLS session is in effect.

On the other hand, if `mod_proxy` appears after `mod_tls` in the `LoadModule` list, then it is the `mod_proxy` module which handles the "on connect" event before `mod_tls` does. `mod_proxy` looks for the session state to see if a TLS session is in effect, does not find the session state, and then attempts to proxy things like the backend banner to the client. The frontend client is expecting TLS handshake bytes, not plaintext text bytes from the banner, and thus the frontend client will see a TLS error.

SFTP/SCP Support
The `mod_proxy` module now supports reverse proxying (but not forward proxying) of SFTP/SCP sessions.

To support a reverse proxy configuration for SFTP/SCP sessions, specify the `sftp` scheme in your `ProxyReverseServers` URIs, like so:

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

        ProxyRole reverse
        ProxyReverseConnectPolicy RoundRobin
        ProxyReverseServers sftp://ssh-backend1.example.com:2222 sftp://ssh-backend2.example.com ...
      </IfModule>

When proxying SSH connections like this, there will be two separate SSH sessions:

      frontend client --- (SSH session #1) --> proxy --- (SSH session #2) --> backend server

Each SSH session creates a unique session ID, as a hash of client- and server-provided values known as `H`. This is important for some types of authentication, as we see later.

Proxying of SSH connections is more complex than proxying of FTP/FTPS connections, especially when it comes to authentication. FTP supports simple password-based authentication, whereas SSH supports multiple different authentication mechanisms. And two authentication mechanisms, in particular, require a slightly more sophisticated `mod_proxy` configuration: "hostbased" and "publickey".

When the frontend SSH client requests the "keyboard-interactive" or "password" authentication mechanisms, then `mod_proxy` can handle the client using the above configuration as-is. For the "hostbased" and "publickey" authentication mechanisms, `mod_proxy` needs to do a little more work -- and it requires support on the backend servers for "hostbased" authentication.

Let's consider the common "publickey" use case (the frontend "hostbased" case is quite similar). The frontend client has a public/private key pair; `mod_proxy` has no knowledge or access to that frontend private key (by design). The frontend client performs the "publickey" authentication by hashing several types of data, then encrypts that hash with its private key; the server (`mod_proxy` in this case) then hashes the same data, and decrypts the client-sent value with the client's public key. The complication is that one of the types of data included in these hashes is the unique session ID known as `H` mentioned above. The `H` values for the SSH connections (from the frontend client to the proxy, and separately from the proxy to the backend server) will be different. And since `mod_proxy` has no access to the frontend private key, it cannot forge or change `H` (and this is a good thing). But it does mean that `mod_proxy` cannot simply proxy the "publickey" authentication request as is, from frontend client to backend server.

Instead, for these authentication mechanisms, `mod_proxy` will use a different authentication mechanism, specifically "hostbased" authentication, to the backend server. This makes sense, right? The backend server trusts that the proxy is doing proper handling of all frontend clients, thus backend server can trust the entire proxy host, not just individual clients proxied by that host. In order support these backend "hostbased" authentication requests, `mod_proxy` will need to know its private key to use for such things, via `ProxySFTPHostKey`; this can actually the same private key used by the `SFTPHostKey` directive (or be a different private key, depending on your needs). It also requires that the backend servers be configured to trust the corresponding public key; for `mod_sftp`, this would be done using its `SFTPAuthorizedHostKeys` directive.

The SFTP/SCP support in `mod_proxy` also properly supports the `PerUser`, `PerGroup` `ProxyReverseConnectPolicy` values, subject the same caveats. This means that `mod_proxy` can proxy different frontend SSH users to different backend servers (and even rewrite/change the user name via the `mod_rewrite` module); just like for FTP/FTPS sessions, you can configure the per-user lookup of backend servers using SQL queries, JSON files, etc.

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.dns
- proxy.forward
- proxy.ftp.conn
- proxy.ftp.ctrl
- proxy.ftp.data
- proxy.ftp.dirlist
- proxy.ftp.facts
- 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.ssh.agent
- proxy.ssh.auth
- proxy.ssh.bcrypt
- proxy.ssh.cipher
- proxy.ssh.compress
- proxy.ssh.crypto
- proxy.ssh.db
- proxy.ssh.disconnect
- proxy.ssh.interop
- proxy.ssh.kex
- proxy.ssh.keys
- proxy.ssh.msg
- proxy.ssh.packet
- proxy.ssh.service
- proxy.ssh.utf8
- 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.

Logging Notes
The following is a list of notes, logging variables that can be used in custom `LogFormats` and/or custom SQL statements:

- `%{note:mod_proxy.backend-ip}`: IP address of the backend/proxied server
- `%{note:mod_proxy.backend-port}`: Port of the backend/proxied server
- `%{note:mod_proxy.backend-url}`: URL to the backend/proxied server

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

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. Note: `mod_proxy` uses the OpenSSL library, so its development library/header files must be installed for building `mod_proxy`.

© Copyright 2015-2023 TJ Saunders
All Rights Reserved