Skip to content

FTP Control Guard (ftp) ​

The FTP Control Guard plugin inspects File Transfer Protocol control connections (RFC 959). It validates command syntax, optionally restricts anonymous logins, tracks authentication failures (530 Login incorrect), and supports transparent AUTH TLS encryption handovers.


Capabilities & Threat Defense ​

Threat / Attack VectorDefense MechanismAction Taken
Credential Brute-Force & SprayingIntercepts 530 Login incorrect response codes; enforces connection close with 421 Too many auth failures when cap is reached (since v3.4.0)Client IP banned after max_auth_failures threshold; connection terminated immediately
Anonymous Login AbuseIntercepts USER anonymous / USER ftpCommand blocked with 530 Anonymous access disabled
Connection FloodingEnforces rate limits per minute and burst capsConnection throttled at the proxy layer
AUTH TLS HandoverDetects 234 Proceed with negotiationTransitions to bidirectional TLS pipe

Installation ​

Install the plugin via CLI or declaratively:

# Install using short nametcp-warden plugins install ftp# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/ftp

Configuration Reference ​

Add an FTP guard service to tcp-warden.yaml:

services:  ftp_guard:    listen: ":2121"    upstream: "127.0.0.1:21"    protocol: "ftp"    rate_limit:      connections_per_minute: 30      burst: 5    max_auth_failures: 5    ban_after_failures: 5    ban_duration: "2h"    plugin_config:      allow_anonymous: false      max_auth_failures: 5

Configuration Options ​

FieldTypeDefaultDescription
listenstring":2121"Local proxy listen address and port.
upstreamstring"127.0.0.1:21"Target FTP server (e.g. vsftpd) address and port.
protocolstring"ftp"Must be set to "ftp".
max_auth_failuresint5Number of failed login attempts before IP is banned. Since v3.4.0 the connection is actively closed with a 421 Too many auth failures response when the cap is reached, and individual lines from clients are bounded to prevent heap exhaustion.
ban_durationstring"2h"Duration of the automated IP ban ("30m", "2h", "24h").
plugin_config.allow_anonymousbooltrueWhen false, blocks USER anonymous or USER ftp commands.

Network & Deployment Architecture ​

Option A: Kernel Firewall Redirection (nftables / iptables) ​

Redirect external traffic destined for port 21 to RouteWarden port 2121:

# Redirect external traffic destined for port 21 to RouteWarden port 2121sudo nft add rule ip routewarden_nat prerouting iifname "eth0" tcp dport 21 redirect to :2121

Note on Passive FTP (PASV): The FTP plugin inspects and protects the control channel (port 21). Passive data connections flow directly or via RouteWarden's generic port range listeners.

Option B: Docker Compose Network Isolation ​

Run the FTP server inside an isolated container network, exposing control port 21 only through RouteWarden:

services:  routewarden:    image: ghcr.io/routewarden/tcp-warden:latest    ports:      - "21:2121"   # Map host FTP control port 21 to RouteWarden      - "9091:9091" # Management API    volumes:      - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro    depends_on:      - ftp-server  ftp-server:    image: fauria/vsftpd:latest    environment:      - FTP_USER=ftpuser      - FTP_PASS=secretpassword      - PASV_ENABLE=YES    # Do NOT publish port 21 to host; keep internal to bridge network

Testing & Verification ​

Verify anonymous access rejection and authentication failure tracking:

# Connect to FTP control port via standard CLI client$ ftp -n 127.0.0.1 2121Connected to 127.0.0.1.220 (vsFTPd 3.0.3)ftp> quote USER anonymous530 Anonymous access disabled by RouteWardenftp> QUIT221 Goodbye.

Released under the MIT License.